import math
import struct
from Log import LOG
SHORT_I = 2
BYTE_I = 1
BOOLEAN_I = 1
DOUBLE_I = 8
INT_I = 4
INT_SIZE = 32
LONG_SIZE = 64
SHORT_SIZE = 16
CHUNK_BIT_SIZE = 7
SHORT_MAX_VALUE = 32767
SHORT_MIN_VALUE = -32768
INT_MAX_VALUE = 2147483647
UNSIGNED_SHORT_MAX_VALUE = 65536
UNSIGNED_INT_MAX_VALUE = 4294967295
UNSIGNED_LONG_MAX_VALUE = 18446744073709551615

class Reader():
    def __init__(self):
        self.message = ""
        self.full_message = ""

    def dump_message(self, full=True):
        if full == True:
            to_print = self.full_message
        else:
            to_print = self.message
        i = 0
        line = 0
        ret = ''
        ret_str = ''
        for i in range(0, len(to_print) - 1):
            val = struct.unpack(">B", to_print[i:i+1])[0]
            if line == 16:
                LOG.error(f"{ret} | {ret_str}")
                ret = ""
                ret_str = ""
                line = 0
            ret += (str(hex(val)).split('0x')[1].zfill(2)) + " "
            if chr(val).isprintable():
                ret_str += chr(val)
            else:
                ret_str += '.'
            line += 1
        LOG.error(f"{ret:<48} | {ret_str}")


    def load_message(self, message, full_message):
        self.full_message = full_message
        self.message = message

    def readDouble(self):
        val = struct.unpack(">d", self.message[:DOUBLE_I])[0]
        self.message = self.message[DOUBLE_I:]
        return int(val)

    def readBoolean(self):
        val = struct.unpack(">?", self.message[:BOOLEAN_I])[0]
        self.message = self.message[BOOLEAN_I:]
        return val

    def readVarUhLong(self):
        val = self.readVarLong()
        if val < 0:
            val = val + UNSIGNED_LONG_MAX_VALUE
        return val

    def readVarUhInt(self):
        val = self.readVarInt()
        if val < 0:
            val = val + UNSIGNED_INT_MAX_VALUE
        return val

    def readVarUhShort(self):
        val = self.readVarShort()
        if val is not None and val < 0:
            val = val + UNSIGNED_SHORT_MAX_VALUE
        return val

    def readString(self, string_len=None):
        string = ""
        string_len = self.readUnsignedShort()
        for i in range(0, string_len):
            string += chr(reader.readUnsignedByte())
        return string

    def readVarLong(self):
        value = 0
        offset = 0
        has_next = 0
        while offset < LONG_SIZE:
            current = self.readByte()
            has_next = (current & 0b10000000) >> 7
            if offset > 0:
                value += (current & 0b01111111) << offset
            else:
                value += (current & 0b01111111)
            offset = offset + CHUNK_BIT_SIZE
            if has_next == 0:
                return value
        LOG.error("Too much data for readVarLong")

    def readVarInt(self):
        value = 0
        offset = 0
        has_next = 0
        while offset < INT_SIZE:
            current = self.readByte()
            has_next = (current & 0b10000000) >> 7
            if offset > 0:
                value += (current & 0b01111111) << offset
            else:
                value += (current & 0b01111111)
            offset = offset + CHUNK_BIT_SIZE
            if has_next == 0:
                if value > INT_MAX_VALUE:
                    value -= UNSIGNED_INT_MAX_VALUE
                return value
        LOG.error("Too much data for readVarInt")

    def readType(self):
        tmp = self.readUnsignedShort()
        try:
            return types[tmp]
        except:
            print (tmp)
            return None

    def readInt(self):
        val = struct.unpack(">I", self.message[:INT_I])[0]
        self.message = self.message[INT_I:]
        return val

    def readShort(self):
        val = struct.unpack(">h", self.message[:SHORT_I])[0]
        self.message = self.message[SHORT_I:]
        return val

    def readUnsignedShort(self):
        val = struct.unpack(">H", self.message[:SHORT_I])[0]
        self.message = self.message[SHORT_I:]
        return val

    def readUnsignedByte(self):
        val = struct.unpack(">B", self.message[:BYTE_I])[0]
        self.message = self.message[BYTE_I:]
        return val

    def readByte(self):
        val = struct.unpack(">b", self.message[:BYTE_I])[0]
        self.message = self.message[BYTE_I:]
        return val

    def readVarShort(self):
        ans = 0
        for i in range(0, 16, 7):
            b = reader.readByte()
            ans += (b & 0b01111111) << i
            if not b & 0b10000000:
                return ans
        raise Exception("Too much data")

reader = Reader()



status = {}
status[0] = "PLAYER_STATUS_OFFLINE"
status[1] = "PLAYER_STATUS_UNKNOWN"
status[10] = "PLAYER_STATUS_AVAILABLE"
status[20] = "PLAYER_STATUS_IDLE"
status[21] = "PLAYER_STATUS_AFK"
status[30] = "PLAYER_STATUS_PRIVATE"
status[40] = "PLAYER_STATUS_SOLO"
types = {}
types[9900] = "CharacterMinimalPlusLookInformations;"
types[3180] = "CharacterBaseInformations;"
types[570] = "PartyMemberInformations;"
types[1185] = "PartyMemberArenaInformations;"
types[1577] = "PartyInvitationMemberInformations;"
types[1248] = "CharacterHardcoreOrEpicInformations;"
types[7291] = "CharacterMinimalGuildInformations;"
types[6348] = "CharacterMinimalAllianceInformations;"
types[9679] = "CharacterMinimalPlusLookAndGradeInformations;"
types[3180] = "CharacterBaseInformations;"
types[570] = "PartyMemberInformations;"
types[1185] = "PartyMemberArenaInformations;"
types[1577] = "PartyInvitationMemberInformations;"
types[1248] = "CharacterHardcoreOrEpicInformations;"
types[5011] = "EntityDispositionInformations;"
types[6186] = "IdentifiedEntityDispositionInformations;"
types[1913] = "FightEntityDispositionInformations;"
types[9731] = "AbstractSocialGroupInfos;"
types[7360] = "BasicGuildInformations;"
types[226] = "GuildInformations;"
types[5091] = "GuildFactSheetInformations;"
types[4531] = "GuildInsiderFactSheetInformations;"
types[221] = "GuildInAllianceInformations;"
types[2923] = "AlliancedGuildFactSheetInformations;"
types[5233] = "BasicAllianceInformations;"
types[8325] = "BasicNamedAllianceInformations;"
types[7088] = "AllianceInformations;"
types[8738] = "AllianceFactSheetInformations;"
types[5941] = "GuildVersatileInformations;"
types[9583] = "GuildInAllianceVersatileInformations;"
types[5091] = "GuildFactSheetInformations;"
types[4531] = "GuildInsiderFactSheetInformations;"
types[8738] = "AllianceFactSheetInformations;"
types[8082] = "PrismSubareaEmptyInfo;"
types[5948] = "PrismGeolocalizedInformation;"
types[522] = "PrismInformation;"
types[4882] = "AlliancePrismInformation;"
types[5380] = "AllianceInsiderPrismInformation;"
types[9713] = "CharacterCharacteristic;"
types[2595] = "CharacterCharacteristicDetailed;"
types[588] = "CharacterCharacteristicValue;"
types[4337] = "FightTeamMemberInformations;"
types[2173] = "FightTeamMemberCharacterInformations;"
types[6975] = "FightTeamMemberWithAllianceCharacterInformations;"
types[4670] = "FightTeamMemberTaxCollectorInformations;"
types[9354] = "FightTeamMemberMonsterInformations;"
types[7385] = "FightTeamMemberEntityInformation;"
types[7355] = "FightTeamInformations;"
types[5118] = "FightAllianceTeamInformations;"
types[645] = "GameFightCharacteristics;"
types[5655] = "FightResultListEntry;"
types[4472] = "FightResultFighterListEntry;"
types[8] = "FightResultTaxCollectorListEntry;"
types[8754] = "FightResultPlayerListEntry;"
types[6098] = "FightResultMutantListEntry;"
types[6085] = "FightResultAdditionalData;"
types[7672] = "FightResultPvpData;"
types[2180] = "FightResultExperienceData;"
types[9646] = "AbstractFightDispellableEffect;"
types[3092] = "FightTemporaryBoostEffect;"
types[4586] = "FightTemporaryBoostStateEffect;"
types[5539] = "FightTemporarySpellBoostEffect;"
types[5215] = "FightTemporaryBoostWeaponDamagesEffect;"
types[3757] = "FightTemporarySpellImmunityEffect;"
types[1785] = "FightTriggeredEffect;"
types[9016] = "ObjectEffect;"
types[6306] = "ObjectEffectInteger;"
types[2348] = "ObjectEffectCreature;"
types[9051] = "ObjectEffectLadder;"
types[9489] = "ObjectEffectMinMax;"
types[6190] = "ObjectEffectDuration;"
types[1200] = "ObjectEffectString;"
types[3164] = "ObjectEffectDice;"
types[3218] = "ObjectEffectDate;"
types[4087] = "ObjectEffectMount;"
types[6478] = "UpdateMountCharacteristic;"
types[6243] = "UpdateMountBooleanCharacteristic;"
types[6889] = "UpdateMountIntegerCharacteristic;"
types[3314] = "Shortcut;"
types[8862] = "ShortcutObject;"
types[2167] = "ShortcutObjectPreset;"
types[5057] = "ShortcutObjectIdolsPreset;"
types[8315] = "ShortcutObjectItem;"
types[2970] = "ShortcutSpell;"
types[6309] = "ShortcutSmiley;"
types[627] = "ShortcutEmote;"
types[8876] = "ShortcutEntitiesPreset;"
types[7742] = "Idol;"
types[9826] = "PartyIdol;"
types[9826] = "PartyIdol;"
types[2664] = "AchievementAchieved;"
types[4807] = "AchievementAchievedRewardable;"
types[5691] = "IgnoredInformations;"
types[5140] = "IgnoredOnlineInformations;"
types[8244] = "FriendInformations;"
types[2107] = "FriendOnlineInformations;"
types[5256] = "AcquaintanceInformation;"
types[5921] = "AcquaintanceOnlineInformation;"
types[486] = "FriendSpouseInformations;"
types[1493] = "FriendSpouseOnlineInformations;"
types[2073] = "InteractiveElementSkill;"
types[1744] = "InteractiveElementNamedSkill;"
types[3755] = "InteractiveElement;"
types[4042] = "InteractiveElementWithAgeBonus;"
types[570] = "PartyMemberInformations;"
types[1185] = "PartyMemberArenaInformations;"
types[1577] = "PartyInvitationMemberInformations;"
types[2696] = "PartyEntityBaseInformation;"
types[9181] = "PartyEntityMemberInformation;"
types[3170] = "SkillActionDescription;"
types[4641] = "SkillActionDescriptionTimed;"
types[1458] = "SkillActionDescriptionCollect;"
types[2377] = "SkillActionDescriptionCraft;"
types[4067] = "HouseInformations;"
types[2501] = "AccountHouseInformations;"
types[4609] = "HouseInformationsInside;"
types[282] = "HouseInformationsForGuild;"
types[849] = "HouseOnMapInformations;"
types[4403] = "HouseInstanceInformations;"
types[8004] = "HouseGuildedInformations;"
types[9435] = "PaddockBuyableInformations;"
types[304] = "PaddockGuildedInformations;"
types[5940] = "GameContextActorPositionInformations;"
types[2985] = "GameContextActorInformations;"
types[3456] = "GameFightFighterInformations;"
types[6923] = "GameFightAIInformations;"
types[4960] = "GameFightMonsterInformations;"
types[1598] = "GameFightMonsterWithAlignmentInformations;"
types[131] = "GameFightTaxCollectorInformations;"
types[4828] = "GameFightFighterNamedInformations;"
types[4921] = "GameFightCharacterInformations;"
types[2853] = "GameFightMutantInformations;"
types[4937] = "GameFightEntityInformation;"
types[6181] = "GameRolePlayActorInformations;"
types[9286] = "GameRolePlayNamedActorInformations;"
types[603] = "GameRolePlayHumanoidInformations;"
types[4102] = "GameRolePlayMutantInformations;"
types[7089] = "GameRolePlayCharacterInformations;"
types[3074] = "GameRolePlayMountInformations;"
types[9532] = "GameRolePlayMerchantInformations;"
types[6439] = "GameRolePlayNpcInformations;"
types[9752] = "GameRolePlayNpcWithQuestInformations;"
types[4593] = "GameRolePlayGroupMonsterInformations;"
types[3543] = "GameRolePlayGroupMonsterWaveInformations;"
types[7200] = "GameRolePlayTreasureHintInformations;"
types[6184] = "GameRolePlayTaxCollectorInformations;"
types[5262] = "GameRolePlayPrismInformations;"
types[9258] = "GameRolePlayPortalInformations;"
types[2985] = "GameContextActorInformations;"
types[3456] = "GameFightFighterInformations;"
types[6923] = "GameFightAIInformations;"
types[4960] = "GameFightMonsterInformations;"
types[1598] = "GameFightMonsterWithAlignmentInformations;"
types[131] = "GameFightTaxCollectorInformations;"
types[4828] = "GameFightFighterNamedInformations;"
types[4921] = "GameFightCharacterInformations;"
types[2853] = "GameFightMutantInformations;"
types[4937] = "GameFightEntityInformation;"
types[6181] = "GameRolePlayActorInformations;"
types[9286] = "GameRolePlayNamedActorInformations;"
types[603] = "GameRolePlayHumanoidInformations;"
types[4102] = "GameRolePlayMutantInformations;"
types[7089] = "GameRolePlayCharacterInformations;"
types[3074] = "GameRolePlayMountInformations;"
types[9532] = "GameRolePlayMerchantInformations;"
types[6439] = "GameRolePlayNpcInformations;"
types[9752] = "GameRolePlayNpcWithQuestInformations;"
types[4593] = "GameRolePlayGroupMonsterInformations;"
types[3543] = "GameRolePlayGroupMonsterWaveInformations;"
types[7200] = "GameRolePlayTreasureHintInformations;"
types[6184] = "GameRolePlayTaxCollectorInformations;"
types[5262] = "GameRolePlayPrismInformations;"
types[9258] = "GameRolePlayPortalInformations;"
types[6181] = "GameRolePlayActorInformations;"
types[9286] = "GameRolePlayNamedActorInformations;"
types[603] = "GameRolePlayHumanoidInformations;"
types[4102] = "GameRolePlayMutantInformations;"
types[7089] = "GameRolePlayCharacterInformations;"
types[3074] = "GameRolePlayMountInformations;"
types[9532] = "GameRolePlayMerchantInformations;"
types[6439] = "GameRolePlayNpcInformations;"
types[9752] = "GameRolePlayNpcWithQuestInformations;"
types[4593] = "GameRolePlayGroupMonsterInformations;"
types[3543] = "GameRolePlayGroupMonsterWaveInformations;"
types[7200] = "GameRolePlayTreasureHintInformations;"
types[6184] = "GameRolePlayTaxCollectorInformations;"
types[5262] = "GameRolePlayPrismInformations;"
types[9258] = "GameRolePlayPortalInformations;"
types[364] = "HumanInformations;"
types[8365] = "HumanOption;"
types[8670] = "HumanOptionObjectUse;"
types[8978] = "HumanOptionAlliance;"
types[9165] = "HumanOptionGuild;"
types[1195] = "HumanOptionOrnament;"
types[5734] = "HumanOptionEmote;"
types[4002] = "HumanOptionTitle;"
types[3759] = "HumanOptionSkillUse;"
types[8560] = "HumanOptionFollowers;"
types[504] = "TaxCollectorStaticInformations;"
types[7550] = "TaxCollectorStaticExtendedInformations;"
types[2215] = "TaxCollectorInformations;"
types[3137] = "TaxCollectorComplementaryInformations;"
types[103] = "TaxCollectorGuildInformations;"
types[6750] = "TaxCollectorLootInformations;"
types[2633] = "TaxCollectorWaitingForHelpInformations;"
types[361] = "GroupMonsterStaticInformations;"
types[6457] = "GroupMonsterStaticInformationsWithAlternatives;"
types[5953] = "QuestActiveInformations;"
types[9905] = "QuestActiveDetailedInformations;"
types[3478] = "QuestObjectiveInformations;"
types[5738] = "QuestObjectiveInformationsWithCompletion;"
types[6691] = "SpawnInformation;"
types[5650] = "BaseSpawnMonsterInformation;"
types[7750] = "SpawnScaledMonsterInformation;"
types[1859] = "SpawnMonsterInformation;"
types[6930] = "SpawnCharacterInformation;"
types[3693] = "SpawnCompanionInformation;"
types[1464] = "GameContextBasicSpawnInformation;"
types[6196] = "GameContextSummonsInformation;"
types[3456] = "GameFightFighterInformations;"
types[6923] = "GameFightAIInformations;"
types[4960] = "GameFightMonsterInformations;"
types[1598] = "GameFightMonsterWithAlignmentInformations;"
types[131] = "GameFightTaxCollectorInformations;"
types[4828] = "GameFightFighterNamedInformations;"
types[4921] = "GameFightCharacterInformations;"
types[2853] = "GameFightMutantInformations;"
types[4937] = "GameFightEntityInformation;"
types[2658] = "GameFightFighterLightInformations;"
types[6259] = "GameFightFighterMonsterLightInformations;"
types[9166] = "GameFightFighterNamedLightInformations;"
types[6262] = "GameFightFighterTaxCollectorLightInformations;"
types[5927] = "GameFightFighterEntityLightInformation;"
types[1387] = "MapCoordinates;"
types[9071] = "MapCoordinatesAndId;"
types[6543] = "MapCoordinatesExtended;"
types[2986] = "Preset;"
types[2947] = "PresetsContainerPreset;"
types[8466] = "IconNamedPreset;"
types[2157] = "SpellsPreset;"
types[5802] = "ForgettableSpellsPreset;"
types[3670] = "StatsPreset;"
types[2835] = "IdolsPreset;"
types[1059] = "EntitiesPreset;"
types[1905] = "FullStatsPreset;"
types[6968] = "ItemsPreset;"
types[5576] = "TreasureHuntStep;"
types[8528] = "TreasureHuntStepFollowDirectionToPOI;"
types[2473] = "TreasureHuntStepDig;"
types[7117] = "TreasureHuntStepFight;"
types[6999] = "TreasureHuntStepFollowDirectionToHint;"
types[4562] = "TreasureHuntStepFollowDirection;"
types[9357] = "AbstractPlayerSearchInformation;"
types[495] = "PlayerSearchTagInformation;"
types[5579] = "PlayerSearchCharacterNameInformation;"
types[6474] = "PortalInformation;"
types[4826] = "BreachBranch;"
types[2271] = "ExtendedBreachBranch;"
types[7739] = "ExtendedLockedBreachBranch;"
types[2271] = "ExtendedBreachBranch;"
types[7739] = "ExtendedLockedBreachBranch;"
types[1760] = "PlayerStatus;"
types[5893] = "PlayerStatusExtended;"
types[2858] = "ServerSessionConstant;"
types[6587] = "ServerSessionConstantString;"
types[5768] = "ServerSessionConstantInteger;"
types[8504] = "ServerSessionConstantLong;"
types[7154] = "StatisticData;"
types[794] = "StatisticDataInt;"
types[9227] = "StatisticDataBoolean;"
types[1664] = "StatisticDataShort;"
types[5171] = "StatisticDataString;"
types[1808] = "StatisticDataByte;"
types[7631] = "DebtInformation;"
types[9302] = "KamaDebtInformation;"