import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.routing
import os.path
import random, string, time, re, json, os, binascii

currentGames = {}

stageSuspectNumbers = [
    3,
    4,
    5,
    5,
    5,
    6,
    6
]
stageCharacterNumbers = [
    20,
    12,
    10,
    9,
    6,
    5,
    4
]

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        newId = ''.join(["%s" % random.randint(0, 9) for x in range(6)])
        while newId in currentGames:
            ''.join(["%s" % random.randint(0, 9) for x in range(6)])
        self.redirect("/"+newId)

class AssassinHandler(tornado.web.RequestHandler):
    def get(self,gameId):
        self.render("assassin.html",gameId=gameId)

class GameSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        self.game = None
        self.role = None
    def on_message(self, message):
        msgObj = json.loads(message)
        #print(message)
        if msgObj.get("action") == "joinGame" and msgObj.get("gameId") and not self.game:
            if currentGames.get(msgObj.get("gameId")):
                if not currentGames[msgObj["gameId"]]["assassin"]:
                    currentGames[msgObj["gameId"]]["assassin"] = self
                    self.game = msgObj["gameId"]
                    self.role = "assassin"
                elif not currentGames[msgObj["gameId"]]["controller"]:
                    currentGames[msgObj["gameId"]]["controller"] = self
                    self.game = msgObj["gameId"]
                    self.role = "controller"
                else:
                    self.write_message(json.dumps({"error":"game full"}))
                    self.close()
                    return
                print("joined")
                print(currentGames[self.game])
                self.write_message(json.dumps({
                    "action":"joined",
                    "gameId":self.game,
                    "role":self.role
                }))
            else:
                currentGames[msgObj["gameId"]] = {
                    "stage":-1,
                    "status":"waitingForPlayers",
                    "assassin":self,
                    "controller":None,
                    "readyStates":{
                        "assassin":False,
                        "controller":False
                    }
                }
                self.game = msgObj["gameId"]
                self.role = "assassin"
                print("created")
                print(currentGames[self.game])
                self.write_message(json.dumps({
                    "action":"joined",
                    "gameId":self.game,
                    "role":self.role
                }))
        if msgObj.get("action") == "ready" and self.game:
            if currentGames.get(self.game) and currentGames[self.game]["status"] == "waitingForPlayers":
                currentGames[self.game]["readyStates"][self.role] = True
                if currentGames[self.game]["readyStates"]["assassin"] and currentGames[self.game]["readyStates"]["controller"]:
                    nextStage(self.game)
        if msgObj.get("action") == "message" and self.game and self.role == "controller":
            if currentGames.get(self.game) and currentGames[self.game]["status"] == "writing":
                currentGames[self.game]["assassin"].write_message(json.dumps({
                    "action":"message",
                    "message":msgObj.get("message")[:stageCharacterNumbers[currentGames[self.game]["stage"]]]
                }))
                currentGames[self.game]["controller"].write_message(json.dumps({ #not really necessary but this way we know the message went through
                    "action":"message",
                    "message":msgObj.get("message")[:stageCharacterNumbers[currentGames[self.game]["stage"]]],
                    "order":currentGames[self.game]["assassinDisplayOrder"]
                }))
                currentGames[self.game]["status"] = "picking"
        if msgObj.get("action") == "sight" and self.game and self.role == "assassin" and currentGames[self.game]["controller"] is not None:
            currentGames[self.game]["controller"].write_message(json.dumps(msgObj))
        if msgObj.get("action") == "choice" and msgObj.get("choice") is not None and self.game and self.role == "assassin":
            if currentGames.get(self.game) and currentGames[self.game]["status"] == "picking":
                if currentGames[self.game]["assassinDisplayOrder"][msgObj["choice"]] == 0:
                    currentGames[self.game]["assassin"].write_message(json.dumps({
                        "action":"choice",
                        "choice":msgObj["choice"],
                        "correct":True,
                        "finished":currentGames[self.game]["stage"] == len(stageSuspectNumbers)-1
                    }))
                    currentGames[self.game]["controller"].write_message(json.dumps({
                        "action":"choice",
                        "choice":msgObj["choice"],
                        "correct":True,
                        "finished":currentGames[self.game]["stage"] == len(stageSuspectNumbers)-1
                    }))
                    currentGames[self.game]["status"] = "roundOver"
                    if currentGames[self.game]["stage"] == len(stageSuspectNumbers)-1:
                        currentGames[self.game]["assassin"].close()
                        currentGames[self.game]["controller"].close()
                        del currentGames[self.game]
                else:
                    currentGames[self.game]["assassin"].write_message(json.dumps({
                        "action":"choice",
                        "choice":msgObj["choice"],
                        "correct":False
                    }))
                    currentGames[self.game]["controller"].write_message(json.dumps({
                        "action":"choice",
                        "choice":msgObj["choice"],
                        "correct":False
                    }))
                    currentGames[self.game]["assassin"].close()
                    currentGames[self.game]["controller"].close()
                    del currentGames[self.game]
        if msgObj.get("action") == "continue" and self.game and self.role == "assassin":
            if currentGames[self.game]["stage"] < len(stageSuspectNumbers) - 1:
                nextStage(self.game)
    def on_close(self):
        if self.game and currentGames.get(self.game):
            currentGames[self.game][self.role] = None
            currentGames[self.game]["readyStates"][self.role] = False
            if currentGames[self.game]["assassin"] is None and currentGames[self.game]["controller"] is None:
                del currentGames[self.game]

def nextStage(gameId):
    theGame = currentGames[gameId]
    theGame["stage"] += 1
    theGame["suspects"] = []
    sameClothes = random.randint(1,stageSuspectNumbers[theGame["stage"]]-1)
    for x in range(stageSuspectNumbers[theGame["stage"]]):
        if x == sameClothes and random.random() < 0.7: #don't do this every time, just most times
            newSuspect = generateSuspect(top=theGame["suspects"][0]["top"])
        else:
            newSuspect = generateSuspect()

        while newSuspect in theGame["suspects"]: #make sure we don't have identical suspects
            newSuspect = generateSuspect()
        theGame["suspects"].append(newSuspect)
        print(newSuspect)
    theGame["assassinDisplayOrder"] = list(range(stageSuspectNumbers[theGame["stage"]]))
    random.shuffle(theGame["assassinDisplayOrder"])
    theGame["controller"].write_message(json.dumps({
        "action":"suspects",
        "suspects":theGame["suspects"],
        "limit":stageCharacterNumbers[theGame["stage"]]
    }))
    theGame["assassin"].write_message(json.dumps({
        "action":"suspects",
        "suspects":[theGame["suspects"][x] for x in theGame["assassinDisplayOrder"]],
        "limit":stageCharacterNumbers[theGame["stage"]]
    }))
    theGame["status"] = "writing"

def generateSuspect(top = None):
    suspect = {
        "hair":random.randint(0,8),
        "skin":random.randint(0,2),
        "eyes":random.randint(0,2),
        "height":10 + random.randint(0,2)*25
    }
    if top is not None:
        suspect["top"] = top
    else:
        suspect["top"] = random.randint(0, 5)
    if suspect["top"] < 4:
        suspect["tie"] = random.randint(0,2)
    return suspect

def make_app():
    return tornado.web.Application(
        [
            (r"/", IndexHandler),
            (r"/([0-9]{6})", AssassinHandler),
            (r"/websocket", GameSocketHandler)
        ],
        template_path = os.path.join(os.path.dirname(__file__), "templates"),
        static_path = os.path.join(os.path.dirname(__file__), "static"),
        websocket_ping_interval = 3,
        websocket_ping_timeout = 4,
        debug=False
    )

if __name__ == "__main__":
    #print(generateSuspect())
    app = make_app()
    app.listen(80)
    tornado.ioloop.IOLoop.current().start()