from Reader import reader
from Log import LOG
import ObjectItem
import threading
import requests
import json
import FMResult
from Options import options
from Gui import GUI
from Server import server

#[UNIT_PUITS, RUNE1_PUITS, RUNE2_PUITS, RUNE3_PUITS]
PUITS_EQUIVALENCE = {
    "117": "po",
    "128": "pm",
    "111": "pa",
    "125": "vi",
    "118": "fo",
    "126": "ine",
    "119": "age",
    "123": "cha",
    "124": "sa",
    "138": "pui",
    "174": "ini",
    "115": "cri",
    "176": "prospe",
    "226": "pi",
    "240": "re terre",
    "243": "re feu",
    "241": "re eau",
    "242": "re air",
    "244": "re neutre",
    "210": "re per terre",
    "213": "re per feu",
    "211": "re per eau",
    "212": "re per air",
    "214": "re per neutre",
    "416": "re pou",
    "420": "re cri",
    "160": "re pa",
    "161": "re pm",
    "410": "ret pa",
    "412": "ret pm",
    "158": "pod",
    "753": "tac",
    "752": "fui",
    "112": "do",
    "422": "do terre",
    "430": "do neutre",
    "424": "do feu",
    "428": "do air",
    "426": "do eau",
    "414": "do pou",
    "418": "do cri",
    "225": "do pi",
    "2804": "do per di",
    "2808": "do per ar",
    "2812": "do per so",
    "2800": "do per me",
    "2803": "re per me",
    "2807": "re per di",
    "178": "so",
    "220": "do ren",
    "182": "invo",
    "795": "chasse"
}
PUITS_VALUES = {
    "118": [1, 1, 3, 10],
    "126": [1, 1, 3, 10],
    "119": [1, 1, 3, 10],
    "125": [0.2, 1, 3, 10],
    "174": [0.1, 1, 3, 10],
    "123": [1, 1, 3, 10],
    "124": [3, 3, 9, 30],
    "176": [3, 3, 9],
    "138": [2, 2, 6, 20],
    "226": [2, 2, 6, 20],
    "240": [2, 2],
    "243": [2, 2],
    "241": [2, 2],
    "242": [2, 2],
    "244": [2, 2],
    "210": [6, 6],
    "213": [6, 6],
    "211": [6, 6],
    "212": [6, 6],
    "214": [6, 6],
    "416": [2, 2, 6],
    "420": [2, 2, 6],
    "160": [7, 7, 21],
    "161": [7, 7, 21],
    "410": [7, 7, 21],
    "412": [7, 7, 21],
    "158": [0.25, 2.5, 7.5, 25],
    "753": [4, 4, 12],
    "752": [4, 4, 12],
    "112": [20, 20],
    "422": [5, 5, 15],
    "430": [5, 5, 15],
    "424": [5, 5, 15],
    "428": [5, 5, 15],
    "426": [5, 5, 15],
    "414": [5, 5, 15],
    "418": [5, 5, 15],
    "225": [5, 5, 15],
    "2804": [15, 15],
    "2808": [15, 15],
    "2812": [15, 15],
    "2800": [15, 15],
    "2803": [15, 15],
    "2807": [15, 15],
    "178": [10, 10],
    "115": [10, 10],
    "220": [10, 10],
    "182": [30, 30],
    "117": [51, 51],
    "128": [90, 90],
    "111": [100, 100],
    "795": [5, 5]
}

REVERSE = {
"778": "3",
"1021": "5",
"1022": "6",
"1023": "8",
"2823": "10",
"441": "77",
"108": "81",
"143": "81",
"407": "81",
"646": "81",
"440": "84",
"1014": "96",
"1016": "97",
"1013": "98",
"1015": "99",
"1012": "100",
"111": "101",
"168": "101",
"1079": "101",
"265": "105",
"143": "108",
"407": "108",
"646": "108",
"168": "111",
"1079": "111",
"121": "112",
"145": "112",
"171": "115",
"117": "116",
"157": "118",
"154": "119",
"145": "121",
"152": "123",
"156": "124",
"153": "125",
"155": "126",
"128": "127",
"169": "127",
"1080": "127",
"169": "128",
"1080": "128",
"186": "138",
"407": "143",
"646": "143",
"335": "149",
"159": "158",
"162": "160",
"163": "161",
"1079": "168",
"1080": "169",
"183": "172",
"184": "173",
"175": "174",
"177": "176",
"179": "178",
"1189": "180",
"1008": "181",
"1011": "181",
"215": "210",
"217": "212",
"218": "213",
"222": "221",
"245": "240",
"246": "241",
"247": "242",
"248": "243",
"249": "244",
"255": "250",
"256": "251",
"257": "252",
"258": "253",
"259": "254",
"402": "401",
"1010": "401",
"1165": "401",
"1010": "402",
"1165": "402",
"2796": "405",
"646": "407",
"411": "410",
"413": "412",
"415": "414",
"417": "416",
"419": "418",
"421": "420",
"423": "422",
"425": "424",
"427": "426",
"429": "428",
"431": "430",
"1162": "509",
"624": "616",
"628": "623",
"2875": "629",
"1073": "700",
"716": "715",
"717": "715",
"717": "716",
"754": "752",
"755": "753",
"1034": "780",
"826": "825",
"1155": "825",
"1155": "826",
"1005": "1003",
"1011": "1008",
"1165": "1010",
"1097": "1024",
"1078": "1033",
"2844": "1033",
"2845": "1033",
"1077": "1076",
"2844": "1078",
"2845": "1078",
"1223": "1123",
"1176": "1151",
"2801": "2800",
"2803": "2802",
"2805": "2804",
"2807": "2806",
"2809": "2808",
"2811": "2810",
"2813": "2812",
"2815": "2814",
"2820": "2819",
"2828": "2822",
"2829": "2822",
"2830": "2822",
"2829": "2828",
"2830": "2828",
"2830": "2829",
"2835": "2834",
"2837": "2836",
"2839": "2838",
"2841": "2840",
"2843": "2842",
"2845": "2844",
"2847": "2846",
"2849": "2848",
"2851": "2850",
"2853": "2852",
"2855": "2854",
"2857": "2856",
"2859": "2858",
"2861": "2860",
}

RUNE_NOT_APPLIED = 1
RUNE_APPLIED = 2

PUIT_UNCHANGED = 1
PUIT_UP = 2
PUIT_DOWN = 3

current_fm = None

class Forgemagie():
    def __init__(self, server):
        self.item = None
        self.rune = None
        self.prev_rune = None
        self.unhandled = False
        self.previous_uid = -1
        self.server = server
        self.item_weight = 0
        self.rune_weight = 0
        self.success_rate = 0
        self.current_stat = None
        self.max_stat = None
        self.min_stat = None
        self.current_weight = None
        self.min_weight = None
        self.max_weight = None
        self.current_status = None
        self.max_status = None
        self.prev_rune_weight = None
        self.item_max_weight = None
        self.weight = None

    def get_min_stat_value(self, new):
        if self.rune != None and new != None:
            for effect in new.effects:
                if effect.action_id == self.rune.effects[0].action_id:
                    return effect.min
            return 0


    def get_stat_value(self, new):
        if self.rune != None and new != None:
            for effect in new.effects:
                if effect.action_id == self.rune.effects[0].action_id:
                    return effect.value
            return 0

    def apply_fm(self):
        if self.item == None:
            return
        if self.rune == None and self.prev_rune != None:
            self.rune = self.prev_rune
            self.rune_weight = self.prev_rune_weight
        modif = FMResult.FMResult().parse_modifications()
        val = self.get_stat_value(modif.item)
        if self.unhandled == False:
            new_weight = round(self.get_stat_value(modif.item) * PUITS_VALUES[str(self.rune.effects[0].action_id)][0], 2)
            serv_data = {
                "item_name": self.item.name,
                "rune_name": self.rune.name,
                "item_gid": self.item.object_gid,
                "item_uid": self.item.object_uid,
                "rune_weight": self.rune_weight,
                "stat_weight": self.current_weight,
                "stat_new_weight": new_weight,
                "stat_max_weight": self.max_weight,
                "stat_min_weight": self.min_weight,
                "item_weight": self.item_weight,
                "item_max_weight": self.item_max_weight,
                "success": True if modif.result == RUNE_APPLIED else False,
                'critical': False,
            }
            table, summ, neg = self.compare_item(modif, modif.item)
            if neg == 0 and modif.result == RUNE_APPLIED and modif.pool_status == PUIT_UNCHANGED:
                serv_data['critical'] = True
            serv_data['summary'] = summ
            serv_data['old_relicat'] = options.relicat
            self.calculate_puit(modif, table)
            serv_data['new_relicat'] = options.relicat
            serv_data['prev_stats'] = json.dumps(self.item.infos)
            self.add_item(modif.item)
            serv_data['item_stats'] = json.dumps(self.item.infos)
            serv_data['new_weight'] = self.item_weight
            self.server.send_to_server(serv_data, "runes/register_try/")
        else:
            self.add_item(modif.item)
        self.refresh_interface()

    def calculate_puit(self, result, table):
        if result.pool_status == PUIT_UNCHANGED:
            return
        if result.result == RUNE_APPLIED:
            table.pop(0)
        for i in table:
            options.relicat += float(i)
        options.relicat = round(options.relicat, 2)
        if options.relicat < 0:
            options.relicat = float(0)
        if options.relicat > 100:
            options.relicat = float(100)

    def get_rune_winrate(self):
        for effect in self.rune.effects:
            if str(effect.action_id) in PUITS_VALUES:
                self.current_stat, self.max_stat, self.min_stat = self.item.get_specific_stat(effect.action_id)
                self.current_weight = round(self.current_stat * PUITS_VALUES[str(effect.action_id)][0], 2)
                self.max_weight = round(self.max_stat * PUITS_VALUES[str(effect.action_id)][0], 2)
                self.min_weight = round(self.min_stat * PUITS_VALUES[str(effect.action_id)][0], 2)
                if self.max_weight != 0:
                    data = {
                        'ratio': round(self.rune_weight / self.max_weight * 100, 1),
                        'current': round(self.current_weight / self.max_weight * 100, 1)
                    }
                    #try:
                        #self.success_rate = requests.post("https://chrisps.captain-nwa.ch/rune_percent", json=data).json()
                    #except:
                    self.success_rate = 0
                else:
                    self.success_rate = 0
                return

    def compare_item(self, modif, new_item):
        result = []
        neg = 0
        summary_string = ""
        is_neg = False
        result.append(-1 * self.rune_weight)
        for old_eff in self.item.effects:
            if old_eff.value == None:
                continue
            found = 0
            for new_eff in new_item.effects:
                is_neg = False
                if new_eff.value == None:
                    continue
                if old_eff.action_id == new_eff.action_id:
                    if new_eff.value - old_eff.value != 0:
                        if str(new_eff.action_id) in PUITS_VALUES.keys():
                            index = str(new_eff.action_id)
                        elif str(new_eff.action_id) in REVERSE.keys():
                            index = REVERSE[str(new_eff.action_id)]
                            is_neg = True
                        else:
                            continue
                        val = -1 * (float(new_eff.value - old_eff.value) * PUITS_VALUES[index][0])
                        if is_neg == True:
                            val = val * -1
                        result.append(val)
                        if new_eff.value - old_eff.value < 0:
                            neg += 1
                        summary_string += f"{new_eff.value - old_eff.value}{new_eff.effect_string}\n"
                    found = 1
                    break
            if found == 0 and str(old_eff.action_id) in PUITS_VALUES.keys():
                neg += 1
                result.append(float(old_eff.value) * PUITS_VALUES[str(old_eff.action_id)][0])
                summary_string += f"-{old_eff.value}{old_eff.effect_string}\n"
        for new_eff in new_item.effects:
            is_neg = False
            if new_eff.value == None:
                continue
            found = 0
            for old_eff in self.item.effects:
                if old_eff.value == None:
                    continue
                if old_eff.action_id == new_eff.action_id:
                    found = 1
                    break
            if found == 0:
                if str(new_eff.action_id) in PUITS_VALUES.keys():
                    index = str(new_eff.action_id)
                elif str(new_eff.action_id) in REVERSE.keys():
                    index = REVERSE[str(new_eff.action_id)]
                    is_neg = True
                else:
                    continue
                val = -1 * (float(new_eff.value) * PUITS_VALUES[index][0])
                if is_neg == True:
                    val *= -1
                result.append(val)
                summary_string += f"{new_eff.value}{new_eff.effect_string}\n"
        summary_string = summary_string[:-1]
        return result, summary_string, neg

    def load_item(self):
        #DISCARDING REMOTE INFO
        reader.readBoolean()
        item = ObjectItem.ObjectItem()
        self.add_item(item)

    def unload_item(self):
        item = ObjectItem.ObjectItemRemoved()
        self.remove_item(item)

    def get_item_stats(self):
        self.current_status = []
        for stat in self.item.effects:
            if str(stat.action_id) in PUITS_VALUES or str(stat.action_id) in REVERSE:
                self.current_status.append((stat.value, stat.action_id))

    def get_item_max_stats(self):
        self.max_status = []
        for stat in self.item.effects:
            if str(stat.action_id) in PUITS_VALUES or str(stat.action_id) in REVERSE:
                self.max_status.append((stat.max, stat.action_id))
        return

    def calculate_max_weight(self, item):
        weight = 0
        for eff in item.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])
        return round(weight, 2)

    def calculate_weight(self, item):
        weight = 0
        for eff in item.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])
        return round(weight, 2)

    def add_item(self, item):
        if item.name == "Rune de Signature":
            return
        if  item.item_type == 26 or item.item_type == 189: #potion/orbe
            self.unhandled = True
            return
        if item.item_type == 78:
            self.unhandled = False
            self.rune = item
            self.rune_weight = self.rune.calculate_weight()
            self.prev_rune = item
            self.prev_rune_weight = self.rune_weight
            GUI.rune_fm_html = f"{self.rune.name}<br />{self.rune_weight}"
            LOG.debug(f"Rune loaded: {self.rune.name} - Weight: {self.rune_weight}")
        else:
            if item.object_uid != self.previous_uid:
                options.relicat = 0
            self.previous_uid = item.object_uid
            self.item = item
            self.item_weight = self.item.calculate_weight()
            self.item_max_weight = self.item.calculate_max_weight()
            self.get_item_stats()
            self.get_item_max_stats()
            GUI.item_fm_html = f"{self.item.name}<br />{self.item_weight}"
            LOG.debug(f"Item loaded: {self.item.name} ({self.item.object_uid}) - Weight: {self.item_weight}")
        if self.rune != None and self.item != None:
            self.get_rune_winrate()
        self.refresh_interface()
        return

    def remove_item(self, item):
        if self.item is not None and item.object_uid == self.item.object_uid:
            LOG.debug(f"Successfully removed {self.item.name}")
            GUI.item_fm_html = ""
            self.item = None
        elif self.rune is not None and item.object_uid == self.rune.object_uid:
            LOG.debug(f"Successfully removed {self.rune.name}")
            GUI.rune_fm_html = ""
            self.rune = None
            self.weight = 0
        self.refresh_interface()
        return

    def refresh_interface(self):
        html = ""
        GUI.pool_fm_html = round(options.relicat, 2)
        if self.item != None and self.rune != None:
            GUI.text_fm_html = f"""
            Stats: {self.current_stat} (+ {self.rune.effects[0].value}) / {self.max_stat}{self.rune.effects[0].effect_string} <br />
            Weight: {round(self.current_weight, 2)} (+ {self.rune_weight}) / {self.max_weight} <br />
            
            """
#            (BETA) Success Rate: {self.success_rate}
        else:
            GUI.text_fm_html = ""
        GUI.update_fm_html()
