from Reader import reader, types, status
import json
from MonstersReference import monsters_ref
from EffectsReference import effects_reference
from Log import LOG
from i18n import i18n_fr
import os
import sys
from Forgemagie import PUITS_EQUIVALENCE, PUITS_VALUES, REVERSE
from AuctionHouse import EQUIPMENTS
from pickler import decompress_pickle


class ObjectReference():
    def __init__(self):
        self.data = decompress_pickle(f'{os.path.dirname(__file__)}/data/Items.pbz2')
        self.effects = effects_reference.data

    def get_object_name(self, uid):
        uid = int(uid)
        for item in self.data:
            if item['id'] == uid:
                return i18n_fr.get_name(item['nameId'])
        return str(uid)

obj_ref = ObjectReference()

class ObjectEffect():
    def __init__(self, action_id=None, imin=None, imax=None):
        self.type = None
        self.effect_string = ""
        self.value = None
        self.min = imin
        self.negative = False
        self.max = imax
        self.string = None
        if action_id != None:
            self.value = 0
            self.action_id = action_id
            for i in obj_ref.effects:
                if i['id'] == self.action_id:
                    if i['descriptionId'] == 0:
                        continue
                    if str(self.action_id) in REVERSE:
                        self.negative = True
                        self.effect_string = i18n_fr.get_name(str(i['descriptionId'])).replace("-#1{~1~2 à -}#2 ", ' -').replace("#1{~1~2 à }#2", '')
                    else:
                        self.effect_string = i18n_fr.get_name(str(i['descriptionId'])).replace("#1{~1~2 à }#2", '').replace("-#1{~1~2 à -}#2 ", ' -')
            return
        self.action_id = reader.readVarUhShort()
        for i in obj_ref.effects:
            if i['id'] == self.action_id:
                if i['descriptionId'] == 0:
                    continue
                if str(self.action_id) in REVERSE:
                    self.negative = True
                    self.effect_string = i18n_fr.get_name(str(i['descriptionId'])).replace("-#1{~1~2 à -}#2 ", ' -').replace("#1{~1~2 à }#2", '')
                else:
                    self.effect_string = i18n_fr.get_name(str(i['descriptionId'])).replace("#1{~1~2 à }#2", '').replace("-#1{~1~2 à -}#2 ", ' -')

class ObjectEffectMinMax(ObjectEffect):
    def __init__(self):
        super().__init__()
        self.min = reader.readVarUhInt()
        self.max = reader.readVarUhInt()

class ObjectEffectMount(ObjectEffect):
    def __init__(self):
        super().__init__()
        self.type = 'mount'
        bbox = reader.readByte()
        self.id = reader.readVarUhLong()
        self.expiration_date = reader.readVarUhLong()
        self.model = reader.readVarUhInt()
        self.name = reader.readString()
        self.owner = reader.readString()
        self.level = reader.readByte()
        self.sex = False if bbox & 1 == 0 else True
        self.is_rideable = False if bbox & 2 == 0 else True
        self.is_feconded = False if bbox & 4 == 0 else True
        self.is_fecondation_ready = False if bbox & 8 == 0 else True
        self.reproduction_count = reader.readVarInt()
        self.reproduction_count_max = reader.readVarUhInt()
        length = reader.readUnsignedShort()
        self.effects = []
        for i in range(0, length):
            self.effects.append(ObjectEffectInteger())
        length = reader.readUnsignedShort()
        self.capacities = []
        for i in range(0, length):
            self.capacities.append(reader.readVarUhInt())

class ObjectEffectDice(ObjectEffect):
    def __init__(self):
        super().__init__()
        self.dice_num = reader.readVarUhInt()
        self.dice_side = reader.readVarUhInt()
        self.dice_const = reader.readVarUhInt()

class ObjectEffectDate(ObjectEffect):
    def __init__(self):
        super().__init__()
        self.year = reader.readVarUhShort()
        self.month = reader.readByte()
        self.day = reader.readByte()
        self.hour = reader.readByte()
        self.minute = reader.readByte()

class ObjectEffectInteger(ObjectEffect):
    def __init__(self, action_id=None, imin=None, imax=None):
        super().__init__(action_id, imin, imax)
        self.type = "int"
        if action_id != None:
            self.min = imin
            self.max = imax
            return
        self.value = reader.readVarUhInt()

    def set_minmax_values(self, imin, imax):
        self.min = imin
        self.max = imax

class ObjectEffectString(ObjectEffect):
    def __init__(self):
        super().__init__()
        self.string = reader.readString()

class Item():
    def __init__(self):
        pass

class ObjectItem(Item):
    def __init__(self, from_fm=False, from_bid_exchange=None):
        super().__init__()
        self.name = ""
        self.item_ref = None
        self.item_type = -1
        self.ordered_infos = {
            "117": "",
            "128": "",
            "111": "",
            "125": "",
            "118": "",
            "126": "",
            "119": "",
            "123": "",
            "124": "",
            "138": "",
            "174": "",
            "115": "",
            "176": "",
            "226": "",
            "240": "",
            "243": "",
            "241": "",
            "242": "",
            "244": "",
            "210": "",
            "213": "",
            "211": "",
            "212": "",
            "214": "",
            "416": "",
            "420": "",
            "160": "",
            "161": "",
            "410": "",
            "412": "",
            "158": "",
            "753": "",
            "752": "",
            "112": "",
            "422": "",
            "430": "",
            "424": "",
            "428": "",
            "426": "",
            "414": "",
            "418": "",
            "225": "",
            "2804": "",
            "2808": "",
            "2812": "",
            "2800": "",
            "2803": "",
            "2807": "",
            "178": "",
            "220": "",
            "182": "",
            "795": "",
        }
        self.infos = []
        if from_bid_exchange is not None:
            self.init_from_bid_exchange(from_bid_exchange)
        else:
            if from_fm == False:
                self.position = reader.readShort()
            self.object_gid = reader.readVarUhShort()
            for item in obj_ref.data:
                if item['id'] == self.object_gid:
                    self.item_ref = item
                    break
            self.effects = []
            self.effects_len = reader.readUnsignedShort()
            for i in range(0, self.effects_len):
                typev = reader.readType()
                if typev == "ObjectEffectInteger;":
                    tmp = ObjectEffectInteger()
                    imin, imax = self.find_type_minmax_values(tmp.action_id)
                    tmp.set_minmax_values(imin, imax)
                    self.effects.append(tmp)
                    self.add_to_info_string(tmp)
                elif typev == "ObjectEffectString;":
                    self.effects.append(ObjectEffectString())
                elif typev == "ObjectEffectMinMax;":
                    self.effects.append(ObjectEffectMinMax())
                elif typev == "ObjectEffectDice;":
                    self.effects.append(ObjectEffectDice())
                elif typev == "ObjectEffect;":
                    self.effects.append(ObjectEffect())
                elif typev == "ObjectEffectDate;":
                    self.effects.append(ObjectEffectDate())
                elif typev == "ObjectEffectMount;":
                    self.effects.append(ObjectEffectMount())
                else:
                    LOG.error(f"Unhandled Type: {typev} for ObjectEffects")
            self.object_uid = reader.readVarUhInt()
            self.quantity = reader.readVarUhInt()

        if self.item_ref != None:
            self.append_missing_effects()
        if self.item_ref == None:
            LOG.error("Could not find item")
        else:
            self.name = i18n_fr.get_name(str(self.item_ref['nameId']))
            self.item_type = self.item_ref['typeId']
        for k, i in self.ordered_infos.items():
            if i != "":
                self.infos.append(i)
        self.calculate_max_weight()
        self.calculate_weight()

    def export(self):
        print (f"{self.object_gid},{self.quantity}", end="")

    def init_from_bid_exchange(self, item):
        for val in obj_ref.data:
            if val['id'] == item.object_gid:
                self.item_ref = val
                break
        self.effects = []
        for effect in item.effects:
            if effect.type == 'int':
                imin, imax = self.find_type_minmax_values(effect.action_id)
                effect.set_minmax_values(imin, imax)
                self.add_to_info_string(effect)
            self.effects.append(effect)
        self.name = item.name
        self.object_gid = item.object_gid
        self.object_uid = item.object_uid
        self.prices = item.prices

    def append_missing_effects(self):
        for t in self.item_ref['possibleEffects']:
            found = 0
            for i in self.effects:
                if int(t['effectId']) == int(i.action_id):
                    found = 1
                    continue
            if found == 0:
                if t['diceSide'] > t['diceNum']:
                    tmp = ObjectEffectInteger(action_id=int(t['effectId']), imin=int(t['diceNum']), imax=int(t['diceSide']))
                else:
                    tmp = ObjectEffectInteger(action_id=int(t['effectId']), imin=int(t['diceSide']), imax=int(t['diceNum']))
                self.effects.append(tmp)
                self.add_to_info_string(tmp)

    def add_to_info_string(self, val):
        if str(val.action_id) in self.ordered_infos.keys():
            self.ordered_infos[str(val.action_id)] = f"{val.value}/{val.max}{val.effect_string}"
        elif str(val.action_id) in REVERSE.keys():
            self.ordered_infos[REVERSE[str(val.action_id)]] = f"{val.value}/{val.max}{val.effect_string}"

    def calculate_weight(self):
        weight = 0
        for eff in self.effects:
            if eff.value != None and str(eff.action_id) in PUITS_VALUES.keys():
                weight += float(eff.value) * float(PUITS_VALUES[str(eff.action_id)][0])
            elif eff.value != None and str(eff.action_id) in REVERSE.keys() and REVERSE[str(eff.action_id)] in PUITS_VALUES:
                weight -= float(eff.value) * float(PUITS_VALUES[REVERSE[str(eff.action_id)]][0])
        self.weight = weight
        return round(weight, 2)

    def calculate_max_weight(self):
        weight = 0
        for eff in self.effects:
            if str(eff.action_id) in PUITS_VALUES.keys():
                weight += float(eff.max) * float(PUITS_VALUES[str(eff.action_id)][0])
            elif str(eff.action_id) in REVERSE.keys() and REVERSE[str(eff.action_id)] in PUITS_VALUES:
                weight -= float(eff.max) * float(PUITS_VALUES[REVERSE[str(eff.action_id)]][0])
        self.max_weight = weight
        return round(weight, 2)

    def get_specific_stat(self, stat_id):
        for i in self.effects:
            if i.action_id == stat_id:
                return i.value, i.max, i.min
        return 0, 0, 0

    def find_type_minmax_values(self, type_s):
        if self.item_ref == None:
            return (0, 0)
        for t in self.item_ref['possibleEffects']:
            if t['effectId'] == type_s:
                if t['diceSide'] > t['diceNum']:
                    return t['diceNum'], t['diceSide']
                else:
                    return t['diceNum'], t['diceNum']
        return (0, 0)


class ObjectItemRemoved():
    def __init__(self):
        self.remote = reader.readBoolean()
        self.object_uid = reader.readVarUhInt()

class BidExchangerObjectInfo():
    def __init__(self):
        self.object_uid = reader.readVarUhInt()
        self.object_gid = reader.readVarUhShort()
        self.object_type = reader.readInt()
        self.name = ''
        self.item_ref = None
        self.type = None
        for item in obj_ref.data:
            if int(item['id']) == int(self.object_gid):
                self.item_ref = item
                break
        if self.item_ref == None:
            LOG.error(f"Could not find item {self.object_gid}")
        if self.item_ref != None:
            self.name = i18n_fr.get_name(str(self.item_ref['nameId']))
        self.effects_len = reader.readUnsignedShort()
        self.effects = []
        for i in range(0, self.effects_len):
            typev = reader.readType()
            if typev == "ObjectEffectInteger;":
                self.effects.append(ObjectEffectInteger())
            elif typev == "ObjectEffectString;":
                self.effects.append(ObjectEffectString())
            elif typev == "ObjectEffectMinMax;":
                self.effects.append(ObjectEffectMinMax())
            elif typev == "ObjectEffectDice;":
                self.effects.append(ObjectEffectDice())
            elif typev == "ObjectEffect;":
                self.effects.append(ObjectEffect())
            elif typev == "ObjectEffectDate;":
                self.effects.append(ObjectEffectDate())
            elif typev == "ObjectEffectMount;":
                self.effects.append(ObjectEffectMount())
            else:
                LOG.error(f"Unhandled Type: {typev} for ObjectEffects")
        self.price_len = reader.readUnsignedShort()
        self.prices = []
        for i in range(0, self.price_len):
            self.prices.append(reader.readVarUhLong())
        self.value = None
        if self.object_type in EQUIPMENTS:
            self.value = ObjectItem(from_bid_exchange=self)

class ExchangeTypesItemsExchangerDescriptionForUser():
    def __init__(self):
        self.object_type = reader.readInt()
        self.type = ''
        self.items_len = reader.readUnsignedShort()
        LOG.debug(self.items_len)
        LOG.debug(f"Total items: {self.items_len}")
        self.items = []
        for i in range(0, self.items_len):
            self.items.append(BidExchangerObjectInfo())
