"""
计算E_{i,D}
E_{i,D}=所有邻居的剩余时间 - (背叛对象的剩余时间 + 与背叛对象的上轮交互时间)
       =除去背叛对象的剩余时间 - 背叛对象的上一轮交互时间
输出：
1. json格式(详细)
{
    "GID": {
        "RID": {
            "PID": EID,
        }
    }
}
2. CSV格式(按轮平均值, CLASSIC/SURVIVE分别对应一个文件)
"""
import csv
import json
from functools import reduce
from sre_constants import MAX_REPEAT
from tkinter.tix import MAX
import numpy as np
from island.match import Match
from island.matches import Matches

MAX_ROUND = 28

class Eid:
    def __init__(self):
        self.details = {}
        self.survivals = {}
        with open('outputs/survivals_new.json','r') as f:
            self.survivals = json.load(f)
        self.neighbors = {}
        with open('outputs/neighborhood_new.json', 'r') as f:
            self.neighbors = json.load(f)
        self.seasons = [
            dict(season=Matches('wos-data-2022-1', network_type='BA'), name='NEW_BA'),
            dict(season=Matches('wos-data-2022-1', network_type='WS'), name='NEW_WS')
        ]
        # self.seasonSurvive = Matches.from_profile('SURVIVE')
        # self.seasonClassic = Matches.from_profile('CLASSIC')


    def getNeighborTR(self, m, r, p, s, d):
        """
        获取该玩家所有邻居中，除去背叛对象的剩余时间
        
        :param m: Match
        :param r: Round ID
        :param p: PID
        :param s: Survivals list(neighborhood)
        :param d: list of victim
        :returns: E_{i}
        """
        truenb = [i for i in s if i not in d]
        trs = dict.fromkeys(truenb, 1440)
        req = m.query('action', 'request').where(lambda x: x['rno'] == r and x['from'] in truenb and x['to'] != p)
        for d in req.raw_data:
            trs[d['from']] -= d['tr']

        for d in m.query('action', 'approve').where(lambda x: x['rno'] == r and x['from'] in truenb and x['to'] != p).raw_data:
            trs[d['from']] -= d['tr']

        for d in m.query('action', 'cancel').where(lambda x: x['rno'] == r and x['from'] in truenb and x['to'] != p).raw_data:
            trs[d['from']] += req.where(lambda x: x['from'] == d['from'] and x['to'] == d['to'] and x['log_id'] < d['log_id']).orderby('log_id').raw_data[-1]['tr']

        for d in m.query('action', 'deny').where(lambda x: x['rno'] == r and x['to'] in truenb and x['from'] != p).raw_data:
            trs[d['to']] += req.where(lambda x: x['from'] == d['to'] and x['to'] == d['from'] and x['log_id'] < d['log_id']).orderby('log_id').raw_data[-1]['tr']

        return reduce(lambda x,y: x + max(0,min(y,1440)), trs.values(), 0)

    def getTR2(self, m, r, p, s, d):
        truenb = [i for i in s if i not in d]
        return reduce(lambda a, b: a + b['tr'], m.query('action', 'done').where(lambda x: x['rno'] == r and ((x['a'] in truenb and x['b'] == p)or(x['b']in truenb and x['a'] == p))).raw_data, 0)

    def getNeighborhood(self, m, r, p):
        """
        获取该玩家当轮存活邻居
        
        :param m: Match
        :param r: Round ID
        :param p: PID
        :returns: Survivals list(neighborhood)
        """
        if str(p) not in self.neighbors[m.name]:
            print("Alone(%d)!" % p)
            return []
        return [i for i in self.survivals[m.name][str(r)] if i in self.neighbors[m.name][str(p)]]

    def getVictims(self, m, r, p):
        """
        获取该玩家当轮的背叛对象，以及与他们的博弈时间资源之和

        :param m: Match
        :param r: Round ID
        :param p: PID
        :returns: sum of tr and a list of victim
        """
        victim = []
        ans = 0
        for d in m.query('action', 'done').where(lambda x: x['rno'] == r-1 and (x['a'] == p or x['b'] == p)).raw_data:
            if d['a'] == p and d['act_a'] == 'D':
                victim.append(d['b'])
                ans += d['tr']
            elif d['b'] == p and d['act_b'] == 'D':
                victim.append(d['a'])
                ans += d['tr']
        return (ans, victim)

    def getVictims2(self, m, r, p):
        """
        获取该玩家当轮的背叛对象，以及与他们的博弈时间资源之和
        ## 悲观假设背叛上一轮所有博弈对象

        :param m: Match
        :param r: Round ID
        :param p: PID
        :returns: sum of tr and a list of victim
        """
        victim = []
        ans = 0
        for d in m.query('action', 'done').where(lambda x: x['rno'] == r-1 and (x['a'] == p or x['b'] == p)).raw_data:
            victim.append(d['b'] if d['a'] == p else d['a'])
            ans += d['tr']
        return (ans, victim)

    def calcRoundData(self, m, r):
        """
        计算某场比赛，某一轮的Eid值
        
        :param m: Match
        :param r: Round ID
        :param p: PID
        :returns: an average value and a detail dict
        """
        r += 1
        ans = {}
        sigma = 0.0
        for p in self.survivals[m.name][str(r)]:
            u, v = self.getVictims2(m, r, p)
            e = max(0, min(1440, self.getNeighborTR(m, r, p, self.getNeighborhood(m, r, p), v))) - u
            ans[p] = e
            sigma += e
        return (sigma, ans)

    def calc_season(self, season, name):
        """
        calc E_i,D
        """
        avg = np.zeros(MAX_ROUND)
        cnt = np.zeros(MAX_ROUND)
        for m in season.data:
            d = {}
            maxr = int(m.query('game', 'created').first()['info']['game_end_at'])
            for r in range(1, maxr):
                sigma, ans = self.calcRoundData(m, r)
                d[r] = ans
                avg[r-1] += sigma
                cnt[r-1] += len(ans)
            self.details[m.name] = d
        print(cnt)
        for i in range(MAX_ROUND): 
            if cnt[i] == 0: 
                cnt[i] = 1
        avg /= cnt
        with open(f'outputs/EID_{name}.csv', 'w') as f:
            csv.writer(f).writerow(avg)
        return avg

    def calc(self):
        for s in self.seasons:
            self.calc_season(s['season'], s['name'])
        with open('outputs/EID_new_detail.json', 'w') as f:
            json.dump(self.details, f)
        
if __name__ == '__main__':
    e = Eid()
    e.calc()
