"""
一场比赛的数据
"""
import json

class Match:
    """Match"""

    def __init__(self, raw, dtype='json'):
        """
        :param json_string: string represents data in json format
        """
        self.name = ''
        if dtype == 'json':
            self.raw_data = json.loads(raw)
            self.dtype = 'json'
        else:
            self.raw_data = raw
            self.dtype = 'mid'

    def query(self, cat, act):
        """
        start a query
        eg. match.query('game', 'created')
        if cat or act is `None', the corresponding field matches anything
        :param cat: category
        :param act: action
        """
        if self.dtype == 'mid':
            raise RuntimeError("Query on intermediate result.")
        result = []
        for entry in self.raw_data:
            if (cat == None or entry['cat'] == cat) and (act == None or entry['act'] == act):
                result.append(entry)
        return Match(result, dtype='mid')

    def where(self, where_expr):
        """
        add some condition
        eg. match.query('player', 'join').where(lambda i: i['pid']==9527)
        """
        if self.dtype != 'mid':
            raise RuntimeError("Where query on raw data.")
        return Match([item for item in self.raw_data if where_expr(item)], dtype='mid')

    def orderby(self, key):
        """
        sort it!
        eg. match.query('player', 'join').where(lambda i: i['pid']<9527).orderby('created_at')
        """
        if self.dtype != 'mid':
            raise RuntimeError("Orderby query on raw data.")

        return Match(sorted(self.raw_data, key=lambda x: x[key]), dtype='mid')

    def select(self, key1, *args):
        """
        select some columns
        eg. match.query('player', 'join')
                 .select('pid', 'created_at')
                 .where(lambda i: i['pid']<9527)
                 .orderby('created_at')
        """
        if self.dtype != 'mid':
            raise RuntimeError("select query on raw data.")

        result = []

        keys = [key1]
        for k in args:
            keys.append(k)

        for entry in self.raw_data:
            dct = dict.fromkeys(keys)
            for k in keys:
                dct[k] = entry[k]
            result.append(dct)

        return Match(result, dtype='mid')

    def first(self):
        """
        return the first result
        """
        if self.dtype != 'mid':
            raise RuntimeError("first query on raw data.")

        rawl = len(self.raw_data)
        return self.raw_data[0] if rawl > 0 else None

    def count(self):
        """
        return the amount of results
        """
        if self.dtype != 'mid':
            raise RuntimeError("count query on raw data.")

        return len(self.raw_data)


    def get_tr(self, i, target, nb, sv):
        """
        # 计算tau_f的方式是统计周围邻居剩余的tr
        # 本函数计算第i轮，所有有消耗tr的player，最后剩余的tr
        # 由于是计算剩余tr，所以不能计算和target之间的交互
        # nb是target的邻居
        # 2018/08/31 更新：即便是已经交互过的邻居，他的剩余时间资源依旧应该计算入tau_f中，使得公式完整。
        # 2018/09/01 更新：不应该算在tau_P中，尝试单独列成一项，因为优化目标不同。
        # 2018/09.01 更新：将tau_p更改为tau_f
        """
        trs = {}
        req = self.query('action', 'request').where(lambda x: x['rno'] == i+1)
        app = self.query('action', 'approve').where(lambda x: x['rno'] == i+1).raw_data
        can = self.query('action', 'cancel').where(lambda x: x['rno'] == i+1).raw_data
        den = self.query('action', 'deny').where(lambda x: x['rno'] == i+1).raw_data

        fr = []
        for r in self.query('action', 'done').where(lambda x: x['rno'] == i and (x['a'] == target or x['b'] == target)).raw_data:
            if r['a'] == target:
                fr.append(r['b'])
            elif r['b'] == target:
                fr.append(r['a']) 

        truenb = []
        for r in nb:
            if r not in fr and r in sv:
                truenb.append(r)
                trs[r] = 1440

        for r in req.raw_data:
            if r['from'] in truenb and r['to'] != target :
                if r['from'] not in trs:
                    trs[r['from']] = 1440
                trs[r['from']] -= r['tr']

        for r in app:
            if r['from'] in truenb and r['to'] != target:
                if r['from'] not in trs:
                    trs[r['from']] = 1440
                trs[r['from']] -= r['tr']

        for r in can:
            if r['from'] in trs and r['to'] != target:
                trs[r['from']] += req.where(lambda x: x['from'] == r['from'] and x['to'] == r['to'] and x['log_id'] < r['log_id']).orderby('log_id').raw_data[-1]['tr']

        for r in den:
            if r['to'] in trs and r['from'] != target:
                trs[r['to']] += req.where(lambda x: x['from'] == r['to'] and x['to'] == r['from'] and x['log_id'] < r['log_id']).orderby('log_id').raw_data[-1]['tr']

        return trs


    def network_type(self):
        """
        返回网络类型，如果找不到，则返回None
        """
        try:
            return json.loads(self.query('game', 'created').first()['info']['config'])['network']['type']
        except:
            print("Can't get network: %s" % json.loads(self.query('game', 'created').first()))
            return None

    @staticmethod
    def read_from_json(json_path):
        """
        从json_path读取json文件，并返回Match
        :param json_path: path to json file
        """
        json_string = ''
        with open(json_path, 'r', encoding='utf-8') as file:
            json_string = file.read()
        return Match(json_string)
