import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

from lexer import Lexer
from parser import Parser
from ast_nodes import Exp, ExpInt, ExpBinop, ExpLet, ExpVar, ExpRoll, Roll, RollKeepHighest, RollKeepLowest, RollMin, RollMax, ExpMin, ExpMax


class DieRoller():
    def __init__(self) -> None:
        self.lexer = Lexer().get_lexer()
        self.parser = Parser()

    def parse(self, string: str) -> Exp:
        tokens = self.lexer.lex(string)
        expression = self.parser.parse(tokens)
        return expression

    def roll(self, string: str):
        exp = self.parse(string)
        # print(exp)
        # exit()
        show = exp.show({})
        eval = exp.eval({})
        if show is None or eval is None or isinstance(eval,tuple):
            raise Exception("Something went wrong")
        return f"**Result**: {show}\n**Total**: {eval}"

if __name__ == "__main__":
    d = DieRoller()
    test_strings = [
        ("4", ExpInt(4)),
        ("1 + 1", ExpBinop("+",ExpInt(1),ExpInt(1))),
        ("5*5", ExpBinop("*",ExpInt(5),ExpInt(5))),
        ("1+5/5", ExpBinop("+",ExpInt(1),ExpBinop("/",ExpInt(5),ExpInt(5)))),
        ("(1+5)/6", ExpBinop("/",ExpBinop("+",ExpInt(1),ExpInt(5)),ExpInt(6))),
        ("let x=5 in x*2", ExpLet("x",ExpInt(5),ExpBinop("*",ExpVar("x"),ExpInt(2)))),
        ("let x= let y = 2 in y in let z = 5 in x*z", ExpLet("x",ExpLet("y",ExpInt(2),ExpVar("y")),ExpLet("z",ExpInt(5),ExpBinop("*",ExpVar("x"),ExpVar("z"))))),
        ("4d6", ExpRoll(Roll(ExpInt(4),ExpInt(6)))),
        ("d6", ExpRoll(Roll(ExpInt(1),ExpInt(6)))),
        ("(2+2)d(3*2)", ExpRoll(Roll(ExpBinop("+",ExpInt(2),ExpInt(2)),ExpBinop("*",ExpInt(3),ExpInt(2))))),
        ("4d3*2", ExpBinop("*",ExpRoll(Roll(ExpInt(4),ExpInt(3))),ExpInt(2))),
        ("4d6kh3", ExpRoll(RollKeepHighest(Roll(ExpInt(4),ExpInt(6)),ExpInt(3)))),
        ("2d20kl1", ExpRoll(RollKeepLowest(Roll(ExpInt(2),ExpInt(20)),ExpInt(1)))),
        ("11d10kh6kl1", ExpRoll(RollKeepLowest(RollKeepHighest(Roll(ExpInt(11),ExpInt(10)),ExpInt(6)),ExpInt(1)))),
        ("3d6min6", ExpRoll(RollMin(Roll(ExpInt(3),ExpInt(6)),ExpInt(6)))),
        ("3d6max1", ExpRoll(RollMax(Roll(ExpInt(3),ExpInt(6)),ExpInt(1)))),
        ("let x = 1d4 in (x)min3", ExpLet("x",ExpRoll(Roll(ExpInt(1),ExpInt(4))),ExpMin(ExpVar("x"),ExpInt(3)))),
        ("10max(2d10)",ExpMax(ExpInt(10),ExpRoll(Roll(ExpInt(2),ExpInt(10)))))
    ]
    for s, t in test_strings:
        r = d.parse(s)
        correct = r == t
        print(f"{str(s) : <20}:    {'✅' if correct else '❌'} ({r.eval({})})")
        if not correct:
            print(f"Expected: {t}")
            print(f"Actual:   {r}\n")

    print(d.roll(input(":")))