#!/usr/bin/env python3
# by aCat
# v2021.06.22a

import os, re, sys, math

VERBOSE = True


class GameResults:
    def __init__(self, player1, player2, game):
        self.player1, self.player2, self.game, self.plays, self.score1, self.score2 = (
            player1,
            player2,
            game,
            0,
            0,
            0,
        )
        self.draws, self.player1wins = 0, 0
        self.scorelist1, self.scorelist2 = [], []
        self.avg1, self.avg2, self.std1, self.std2 = 0, 0, 0, 0
        self.ternEXP, self.ternVAR, self.ternSTDERR, self.tern95CI = 0, 0, 0, 0
        self.confidentP1win, self.confidentP1lose, self.confidentP1draw = (
            False,
            False,
            False,
        )
        self.timeouts = {}

    def __str__(self):
        return str(
            (
                self.player1,
                self.player2,
                self.game,
                self.plays,
                self.score1,
                self.score2,
                self.timeouts,
            )
        )


def arrangeFiles(logfiles):
    arrfiles = {}
    for f in logfiles:
        m = re.search("^(.+\-.+)\-(.+?)_vs_(.+?)\.log$", f)
        players = tuple(sorted((m.group(2), m.group(3))))
        if players not in arrfiles:
            arrfiles[players] = {}
        if m.group(1) not in arrfiles[players]:
            arrfiles[players][m.group(1)] = []
        arrfiles[players][m.group(1)].append(f)
    return arrfiles


def calculateGameResults(players, game, logfiles, dir):
    gr = GameResults(players[0], players[1], game)
    for f in logfiles:
        m = re.search("^.+\-.+\-(.+?)_vs_.+\.log$", f)
        reverse = m.group(1) != players[0]
        with open(os.path.join(dir, f), "r") as data:
            v = 0
            for entry in data:
                v += 1
                entry = entry.strip().split()
                gr.plays += 1
                if len(entry) > 5:
                    if not entry[5] in gr.timeouts:
                        gr.timeouts[entry[5]] = 0
                    gr.timeouts[entry[5]] += 1
                if reverse:
                    gr.score1 += int(entry[4])
                    gr.scorelist1.append(int(entry[4]))
                    if int(entry[4]) > int(entry[3]):
                        gr.player1wins += 1
                    gr.score2 += int(entry[3])
                    gr.scorelist2.append(int(entry[3]))
                else:
                    gr.score1 += int(entry[3])
                    gr.scorelist1.append(int(entry[3]))
                    if int(entry[3]) > int(entry[4]):
                        gr.player1wins += 1
                    gr.score2 += int(entry[4])
                    gr.scorelist2.append(int(entry[4]))
                if entry[4] == entry[3]:
                    gr.draws += 1
    if gr.plays < 1:
        return gr
    gr.avg1 = gr.score1 / gr.plays
    gr.var1 = sum([(s - gr.avg1) ** 2 for s in gr.scorelist1]) / (gr.plays - 1)
    gr.std1 = math.sqrt(gr.var1)
    gr.avg2 = gr.score2 / gr.plays
    gr.ternEXP = (gr.player1wins + gr.draws / 2) / gr.plays
    gr.ternVAR = (gr.player1wins + gr.draws / 4) / gr.plays - gr.ternEXP * gr.ternEXP
    gr.ternSTDERR = math.sqrt(gr.ternVAR) / math.sqrt(gr.plays)
    gr.tern95CI = 100 * (1.96 * (math.sqrt(gr.ternVAR)) / math.sqrt(gr.plays - 1))
    if gr.avg1 > 50 + gr.tern95CI:
        gr.confidentP1win = True
    elif gr.avg1 < 50 - gr.tern95CI:
        gr.confidentP1lose = True
    else:
        gr.confidentP1draw = True
    return gr


def runInDirectory(dir):
    logfiles = [
        f
        for f in os.listdir(dir)
        if re.search(".log$", f) and not f.startswith("manager")
    ]
    if VERBOSE:
        print("INFO: Found %d logfiles in directory %s." % (len(logfiles), dir))
    arrfiles = arrangeFiles(logfiles)
    if VERBOSE:
        print("INFO: Found %d pairs of players." % len(arrfiles))

    for players, gamefiles in arrfiles.items():
        results = []
        outfile = "_RESULTS_%s_vs_%s.txt" % (players[0], players[1])
        for game, logfiles in gamefiles.items():
            results.append(calculateGameResults(players, game, logfiles, dir))
        out = []
        playsum = 0
        cwins1, cdraws1, closes1 = 0, 0, 0
        for gr in sorted(
            results,
            key=lambda r: (
                r.game.split("-")[0],
                float(re.sub("[a-zA-Z]", "", r.game.split("-")[1])),
            ),
        ):
            playsum += gr.plays
            winstr = "W" if gr.confidentP1win else ("L" if gr.confidentP1lose else "D")
            if gr.confidentP1win:
                cwins1 += 1
            elif gr.confidentP1lose:
                closes1 += 1
            else:
                cdraws1 += 1
            out.append(
                "{0:<42} {1:4}  {2:6.1f}   {3:6.1f}    {4:4.1f}  {5:6.1f}    ({6})     {7}\n".format(
                    gr.game,
                    gr.plays,
                    gr.avg1,
                    gr.avg2,
                    100 * math.sqrt(gr.ternVAR),
                    gr.tern95CI,
                    winstr,
                    ",".join(("{}:{}x".format(*i) for i in gr.timeouts.items())),
                )
            )

        perlimit = {}
        for gr in results:
            limit = gr.game.split("-")[1]
            if not limit in perlimit:
                perlimit[limit] = []
            perlimit[limit].append(gr)

        out2 = []
        for limit in sorted(perlimit.keys()):
            grs = perlimit[limit]
            s1 = [gr.score1 / gr.plays for gr in grs]
            s2 = [gr.score2 / gr.plays for gr in grs]
            avg1 = sum(s1) / len(grs)
            avg2 = sum(s2) / len(grs)
            std1 = math.sqrt(sum([(s - avg1) ** 2 for s in s1]) / len(s1))

            out2.append(
                "  {0:<8} {1:6.2f}      {2:6.2f}       {3:2.2f}   WDL-{7}  {4} : {5} : {6}   WDL-{8}  {6} : {5} : {4}\n".format(
                    limit,
                    avg1,
                    avg2,
                    std1,
                    cwins1,
                    cdraws1,
                    closes1,
                    gr.player1,
                    gr.player2,
                )
            )

        header = "INFO: PLAYERS: %s VS %s:\n" % (players[0], players[1])
        header += "                                                  %s   %s\n" % (
            players[0],
            players[1],
        )
        header += "  game-test                                  n    scoreP1   scoreP2   sdev     95%CI P1win   errors\n"
        summary = "            %s   %s\n" % (players[0], players[1])
        summary += (
            "test        AvgScoreP1  AvgScoreP2  std      // summary: %d games, %d plays\n"
            % (len(results), playsum)
        )
        with open(os.path.join(dir, outfile), "w") as f:
            f.write(header)
            f.writelines(out)
            f.write(summary)
            f.writelines(out2)
        print(header, end="")
        for outline in out:
            print("  " + outline, end="")
        print(summary, end="")
        for outline in out2:
            print(outline, end="")


def getLogdirs(dir):
    dirlist = [dir]
    for r, d, f in os.walk(dir):
        for recdir in d:
            dirlist.append(os.path.join(r, recdir))
    return dirlist


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("ERROR: The first argument should be directory containing log files.")
        sys.exit(1)

    dir = sys.argv[1]
    dirlist = getLogdirs(dir)
    for d in dirlist:
        runInDirectory(d)

if VERBOSE:
    print("INFO: done.")
