import requests
import json
import time
import socket

vmixHost = '192.168.1.66'
vmixPort = '8088'
vmixTCPPort = 8099

teamSize = 4
baseTitleName = "teamTitle"
imagePathBase = "D:\\video_projects\\midnightguns\\"
blankImageName = "blank.png"
deathImageName = "splat.png"
blankPreset = 0
deadColor = "%23333333"
aliveColor = "%23ffffff"
inactiveColor = "%23333333"
bleedColor = "%23ff011d"

#TODO: clear players that leave the server, reset colors on reset, handle map change?

teams = []
updateKeys = ["name", "kills", "health", "deaths", "throwable", "heldWeapon"]
imageUpdateKeys = []

weaponStrings = ["-", "PISTOL", "M4", "MP5", "SNIPER", "SHOTGUN", "HANDCANNON", "CARBINE", "THROWING KNIFE", "HOCKEY STICK", "MOLOTOV", "DYNAMITE", "FIRE AXE", "BOW"]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((vmixHost, vmixTCPPort))

class TeamData():    
    def __init__(self, number): 
        self.number = number
        self.players = []

def updateTitle(titleId,subTitle,data):
    sendstring = "FUNCTION SetText Input={}&SelectedName={}.Text&Value={}\r\n".format(titleId,subTitle,data)
    print(sendstring)
    s.sendall(bytes(sendstring, "latin-1","ignore"))
    data = s.recv(1024)
    print(f"Recieved {data!r}")
    #requestStr = "http://{}:{}/api/?Function=SetText&Input={}&SelectedName={}.Text&Value={}".format(vmixHost,vmixPort,titleId,subTitle,data)
    #print (requestStr)
    #requests.get(requestStr)
    
def updateTitleImage(titleId,imageId,data):
    sendstring = "FUNCTION SetImage Input={}&SelectedName={}.Source&Value={}\r\n".format(titleId,imageId,data)
    print(sendstring)
    s.sendall(bytes(sendstring, "latin-1","ignore"))
    data = s.recv(1024)
    print(f"Recieved {data!r}")
    #requestStr = "http://{}:{}/api/?Function=SetImage&Input={}&SelectedName={}.Source&Value={}".format(vmixHost,vmixPort,titleId,imageId,data)
    #print (requestStr)
    #requests.get(requestStr)

def setupPlayer(playerData): #playerData contains team key
    if len(teams) == 0: #teams empty
        setupTeams()       
        for team in teams:
            if team.number == playerData["team"]:
                team.players.append(playerData)
                updateAssignedPlayerTitle(playerData, True)
                break
        return
    
    isInTeam = lookupPlayerNum(playerData["playerNum"])
    if isInTeam[1] == -1: #player not registered to team
        for team in teams:
            if team.number == playerData["team"]:
                team.players.append(playerData)
                updateAllTitles()
                print("stopping")
                break
                
    elif isInTeam[1] == playerData["team"]: #player already in a team
        updateAssignedPlayerTitle(playerData)
    
    else: #player migrating teams
        migratingPlayer = teams[isInTeam[0]].players[isInTeam[2]]
        migratingPlayer.update(playerData)
        teams[isInTeam[0]].players.remove(teams[isInTeam[0]].players[isInTeam[2]])
        clearTitles()
        for team in teams:
            if team.number == playerData["team"]:
                team.players.append(migratingPlayer)
                break
        updateAllTitles()
    return
    
    
def lookupTeam(teamNum):
    returnValue = -1
    for team in teams:
        if team.index == teamNum:
            returnValue = teams.index(team)
    return returnValue
     
def lookupPlayerNum(playernum): #return [assigned team index, assigned team number, player index, playerdata] or [-1, -1, -1, -1]
    returnValue = [-1, -1, -1, -1]
    for teamindex, team in enumerate(teams):
        for index, player in enumerate(team.players):
            if playernum == player["playerNum"]:
               returnValue[0] = teamindex
               returnValue[1] = team.number
               returnValue[2] = index
               returnValue[3] = player
               return returnValue
    return returnValue
        
    
def setupTeams():
    global teams
    team1Players = TeamData(1)
    team2Players = TeamData(2)
    teams = [team1Players,team2Players]
    clearTitles()
    
def checkDeadName(playerData, teamData, storedData, newPlayerReg=False): #update to only update if updated - EXTEND TO RESET
    newColor = ""
    teamTitle = baseTitleName+str(teamData[1])
    playerTitles = [["name", deadColor], ["health", inactiveColor], ["healthIcon", inactiveColor]]
    if "deadFlags" in playerData:    
        dataIsNew = checkNewData(playerData, storedData, "deadFlags")
        for title in playerTitles:
            if dataIsNew==True or newPlayerReg==True:
                if playerData["deadFlags"] != 0: 
                    newColor = title[1]
                    playerData["health"] = 0
                else:
                    newColor = aliveColor
                playerTitle = "p" + str(teamData[2]) + title[0]
                sendstring = "FUNCTION SetTextColour Input={}&SelectedName={}.Text&Value={}\r\n".format(teamTitle,playerTitle,newColor)
                print(sendstring)
                s.sendall(bytes(sendstring, "latin-1","ignore"))
                data = s.recv(1024)
                print(f"Recieved {data!r}")
                #requestStr = "http://{}:{}/api/?Function=SetTextColour&Input={}&SelectedName={}.Text&Value={}".format(vmixHost,vmixPort,teamTitle,playerTitle,newColor)
                #print (requestStr)
                #requests.get(requestStr)


def checkBleeding(playerData, teamData, storedData, newPlayerReg=False):
    newColor = ""
    teamTitle = baseTitleName+str(teamData[1])
    playerTitles = [["health", bleedColor], ["healthIcon", bleedColor]]
    if "bleedingAmount" in playerData:
        dataIsNew = checkNewData(playerData, storedData, "bleedingAmount")
        for title in playerTitles:
            if dataIsNew==True or newPlayerReg==True:
                if playerData["bleedingAmount"] != 0: 
                    newColor = title[1]
                else:
                    newColor = aliveColor
                playerTitle = "p" + str(teamData[2]) + title[0]
                sendstring = "FUNCTION SetTextColour Input={}&SelectedName={}.Text&Value={}\r\n".format(teamTitle,playerTitle,newColor)
                print(sendstring)
                s.sendall(bytes(sendstring, "latin-1","ignore"))
                data = s.recv(1024)
                print(f"Recieved {data!r}")
                #requestStr = "http://{}:{}/api/?Function=SetTextColour&Input={}&SelectedName={}.Text&Value={}".format(vmixHost,vmixPort,teamTitle,playerTitle,newColor)
                #print (requestStr)
                #requests.get(requestStr)
        
    
def updateAssignedPlayerTitle(playerData, newPlayerReg=False): #only perform on player with assigned team, then overwrite the stored info _after_ pushing title updates
    playerIndex = lookupPlayerNum(playerData["playerNum"])
    if playerIndex[0] != -1:
        storedPlayerData = teams[playerIndex[0]].players[playerIndex[2]]
        #print("Stored Data", storedPlayerData)
        pIndex = playerIndex[2]
        checkBleeding(playerData, playerIndex, storedPlayerData, newPlayerReg)
        checkDeadName(playerData, playerIndex, storedPlayerData, newPlayerReg)
        for key in updateKeys:
            if key in playerData:
                pTitle = key
                pValue = playerData[pTitle]
                dataIsNew = checkNewData(playerData, storedPlayerData, key)
                if dataIsNew==True or newPlayerReg==True:
                    if key == "throwable":
                        updateTitleImage(baseTitleName+str(playerIndex[1]) , "p"+str(pIndex)+pTitle, imagePathBase + str(pValue) + ".png")
                    elif key == "heldWeapon":
                        pValue = ProcessHeldWeapon(playerData, pValue)
                        updateTitleImage(baseTitleName+str(playerIndex[1]) , "p"+str(pIndex)+pTitle, imagePathBase + str(pValue) + ".png")
                    else:
                        updateTitle(baseTitleName+str(playerIndex[1]),"p"+str(pIndex)+pTitle,pValue)
                    storedPlayerData[key] = playerData[key]
        storedPlayerData.update(playerData)
        
def checkNewData(playerData, storedPlayerData, keyToCompare): #returns True for matching on disparate data
    if not storedPlayerData[keyToCompare]:
        storedPlayerData[keyToCompare] = playerData[keyToCompare]
        return True
    if keyToCompare == "heldWeapon" and playerData[keyToCompare] == storedPlayerData[keyToCompare]:
        if playerData["weapon1"] == storedPlayerData["weapon1"] and playerData["weapon2"] == storedPlayerData["weapon2"]:
            return False
        else:
            return True
    elif playerData[keyToCompare] == storedPlayerData[keyToCompare]:
        return False
    else:
        return True
        
def ProcessThrowable(inputString):
    return weaponStrings[int(inputString)]

def ProcessHeldWeapon(playerData, inputString):
    print("/n TRYING TO MAP {} to {} or {}".format(inputString, playerData["weapon1"], playerData["weapon2"] ))
    if int(inputString) == 0:
        return playerData["weapon1"]
    else:
        return playerData["weapon2"]
    
def updateAllTitles():
    for team in teams:
        for player in team.players:
            updateAssignedPlayerTitle(player, True)

def clearTitles():
    resetColorKeys = ["name", "kills", "health", "deaths", "healthIcon"]
    for team in teams:
        sendstring = "FUNCTION SelectTitlePreset Input={}&Value={}\r\n".format("teamTitle"+str(team.number),str(blankPreset))
        print(sendstring)
        s.sendall(bytes(sendstring, "latin-1","ignore"))
        data = s.recv(1024)   
        print(f"Recieved {data!r}")
        #requestStr = "http://{}:{}/api/?Function=SelectTitlePreset&Input={}&Value={}".format(vmixHost,vmixPort,"teamTitle"+str(team.number),str(blankPreset))
        #insert line here to reset color to fff
        #print(requestStr)
        #requests.get(requestStr)
        time.sleep(0.5)
        for i in range(teamSize):
            for key in resetColorKeys:
                sendstring = "FUNCTION SetTextColour Input={}&SelectedName={}.Text&Value={}\r\n".format("teamTitle"+str(team.number),"p" + str(i) + key ,inactiveColor)
                print(sendstring)
                s.sendall(bytes(sendstring, "latin-1","ignore"))
                data = s.recv(1024)
                print(f"Recieved {data!r}")
                #requestStr = "http://{}:{}/api/?Function=SetTextColour&Input={}&SelectedName={}.Text&Value={}".format(vmixHost,vmixPort,"teamTitle"+str(team.number),"p" + str(i) + key ,inactiveColor)
                #print (requestStr)
                #requests.get(requestStr)
    time.sleep(0.5)
    
def scrubPlayerName(rawName):
    replacementDict={
        "^^": "",
        "^a": "",        
        "^1": "", 
        "^2": "",
        "^3": "",
        "^4": "",
        "^5": "",
        "^6": "",
        "^7": "",
        "^8": "",
        "^9": "",
        "^b": "",
        "^d": "",
        "^m": "",
        "^h": "",
        "^s": "",
        "^r": ""
    }

    parsedName = rawName
    for i, char in enumerate(parsedName):
        if char == "^" and parsedName[i+1] == "x":
            replacementDict[parsedName[i:i+5]] = ""
        elif char == "^" and parsedName[i+1] == "&":
            replacementDict[parsedName[i:i+4]] = ""
        elif char == "^" and parsedName[i+1] == "[":
            endindex = parsedName[i:].index("^]")
            replacementDict[parsedName[i:endindex+2+i]] = ""
        elif char == "^" and parsedName[i+1] == "{":
            endindex = parsedName[i:].index("}")
            replacementDict[parsedName[i:endindex+1+i]] = ""
        elif char == "^" and parsedName[i+1] == "'":
            endindex = parsedName[i:].index("=")
            replacementDict[parsedName[i:endindex+1+i]] = ""
        elif char == "^" and parsedName[i+1] == "U":
            replacementDict[parsedName[i:i+6]] = ""

    for chars, matches in replacementDict.items():
        parsedName = parsedName.replace(chars, matches)
    
    return parsedName 
        
def processPlayerInputStream(playerData):
    if "name" in playerData:
        playerData["name"] = scrubPlayerName(playerData["name"])
    if "team" in playerData:
        if playerData["team"] != 0:
            setupPlayer(playerData)
    else:
        updateAssignedPlayerTitle(playerData)

def main():
    testDicts = []
	#updateTitleImage(baseTitleName+"2","Image1.Source",imagePathBase+deathImageName)
    testDicts.append(json.loads('{"packetType": "PLAYERINFO", "playerNum": 1, "updateFlags": 65535, "name": "reki", "team": 1, "health": 1, "bleedingAmount": 0, "deadFlags": 0, "selectedWeapon": 0, "weapon1": 4, "weapon2": 1, "throwable": 8, "itemA": 2, "itemB": 20, "ammo1": 11, "ammo2": 8, "mags1": 6, "mags2": 2, "score": 0, "kill": 0, "deaths": 0}'))
    testDicts.append(json.loads('{"packetType": "PLAYERINFO", "playerNum": 4, "updateFlags": 65535, "name": "rekis best friend", "team": 1, "health": 19, "bleedingAmount": 0, "deadFlags": 0, "selectedWeapon": 0, "weapon1": 4, "weapon2": 1, "throwable": 8, "itemA": 2, "itemB": 20, "ammo1": 22, "ammo2": 8, "mags1": 6, "mags2": 2, "score": 0, "kill": 0, "deaths": 0}'))
    for team in teams:
        print ("Team", team.number, ",", len(team.players), "players")
        for player in team.players:
            print (player["name"], player["playerNum"], player["team"])



if __name__ == "__main__":
    from sys import exit
    exit(main())
