# • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        
# ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     
# ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ 
# ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌
# ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪
#               Magicbane Emulator Project © 2013 - 2022
#                          www.magicbane.com

from collections import OrderedDict

from arcane.enums.arc_rune import *
from arcane.enums.hashes import hash_to_string, string_to_hash
from arcane.util import ResStream
from .ArcItem import ArcItem
from .common.Arcane import AttributeValue, SkillGrant
from .common.Inventory import Inventory
from .common.SparseData import SparseData


class Group:
    def load_binary(self, stream: ResStream):
        self.group_type = stream.read_dword()
        self.group_is_faction = stream.read_bool()
        self.group_is_guild = stream.read_bool()

    def save_binary(self, stream: ResStream):
        stream.write_dword(self.group_type)
        stream.write_bool(self.group_is_faction)
        stream.write_bool(self.group_is_guild)

    def load_json(self, data):
        self.group_type = data['group_type']
        self.group_is_faction = data['group_is_faction']
        self.group_is_guild = data['group_is_guild']

    def save_json(self):
        data = OrderedDict()
        data['group_type'] = self.group_type
        data['group_is_faction'] = self.group_is_faction
        data['group_is_guild'] = self.group_is_guild
        return data


class SkillAdjust:
    def load_binary(self, stream: ResStream):
        self.skill_type = stream.read_dword()

        num_adjusts = stream.read_dword()
        self.skill_adjusts = [
            [
                stream.read_dword(),
                stream.read_dword(),
            ] for _ in range(num_adjusts)
        ]

    def save_binary(self, stream: ResStream):
        stream.write_dword(self.skill_type)
        stream.write_dword(len(self.skill_adjusts))
        for skill in self.skill_adjusts:
            stream.write_dword(skill[0])
            stream.write_dword(skill[1])

    def load_json(self, data):
        self.skill_type = string_to_hash(data['skill_type'])
        self.skill_adjusts = []
        for skill in data['skill_adjusts']:
            self.skill_adjusts.append(skill)

    def save_json(self):
        data = OrderedDict()
        data['skill_type'] = hash_to_string(self.skill_type)
        data['skill_adjusts'] = []
        for skill in self.skill_adjusts:
            data['skill_adjusts'].append(skill)
        return data


class BodyPart:
    def load_binary(self, stream: ResStream):
        self.body_part_render = stream.read_qword()
        self.body_part_position = stream.read_tuple()

    def save_binary(self, stream: ResStream):
        stream.write_qword(self.body_part_render)
        stream.write_tuple(self.body_part_position)

    def load_json(self, data):
        self.body_part_render = data['body_part_render']
        self.body_part_position = data['body_part_position']

    def save_json(self):
        data = OrderedDict()
        data['body_part_render'] = self.body_part_render
        data['body_part_position'] = self.body_part_position
        return data


class ArcRune(ArcItem):
    def load_binary(self, stream: ResStream):
        super().load_binary(stream)
        self.rune_type = stream.read_dword()
        self.rune_sub_type = stream.read_dword()
        self.rune_is_standard_character_creation = stream.read_bool()
        self.rune_creation_cost = stream.read_dword()
        self.rune_rank = stream.read_dword()
        self.rune_pracs_per_level = stream.read_dword()
        self.rune_exp_req_to_level = stream.read_float()
        self.rune_sex = stream.read_dword()
        self.rune_class_icon = stream.read_qword()
        self.rune_health = stream.read_dword()
        self.rune_mana = stream.read_dword()
        self.rune_stamina = stream.read_dword()
        self.rune_min_damage = stream.read_float()
        self.rune_max_damage = stream.read_float()
        self.rune_attack = stream.read_dword()
        self.rune_defense = stream.read_dword()
        self.rune_level = stream.read_dword()
        self.rune_speed = [
            stream.read_float() for _ in range(7)
        ]
        self.rune_group = Group()
        self.rune_group.load_binary(stream)
        self.rune_dsc = stream.read_string()
        self.rune_fx_txt = stream.read_string()
        self.rune_group_tactics = stream.read_dword()
        self.rune_group_role_set = stream.read_dword()
        num_enemies = stream.read_dword()
        self.rune_enemy_monster_types = [
            stream.read_dword() for _ in range(num_enemies)
        ]
        num_groupees = stream.read_dword()
        self.rune_groupee_monster_types = [
            stream.read_dword() for _ in range(num_groupees)
        ]
        num_helpers = stream.read_dword()
        self.rune_helper_monster_types = [
            stream.read_dword() for _ in range(num_helpers)
        ]
        num_not_enemies = stream.read_dword()
        self.rune_not_enemy_monster_types = [
            stream.read_dword() for _ in range(num_not_enemies)
        ]
        num_genders = stream.read_dword()
        self.rune_enemy_gender = [
            stream.read_dword() for _ in range(num_genders)
        ]
        num_skill_grants = stream.read_dword()
        self.rune_skill_grant = [SkillGrant() for _ in range(num_skill_grants)]
        for grant in self.rune_skill_grant:
            grant.load_binary(stream)
        num_skill_adjusts = stream.read_dword()
        self.rune_skill_adj = [SkillAdjust() for _ in range(num_skill_adjusts)]
        for adjust in self.rune_skill_adj:
            adjust.load_binary(stream)
        num_attr_adjusts = stream.read_dword()
        self.rune_attr_adj = [AttributeValue() for _ in range(num_attr_adjusts)]
        for attr in self.rune_attr_adj:
            attr.load_binary(stream)
        num_max_attr_adjusts = stream.read_dword()
        self.rune_max_attr_adj = [AttributeValue() for _ in range(num_max_attr_adjusts)]
        for attr in self.rune_max_attr_adj:
            attr.load_binary(stream)
        num_natural_attacks = stream.read_dword()
        self.rune_naturalattacks = [
            [
                stream.read_float(),
                stream.read_float(),
                stream.read_dword(),
                stream.read_dword(),
                stream.read_qword(),
                stream.read_float(),
                stream.read_dword(),
                stream.read_dword(),
                stream.read_dword(),
                stream.read_dword(),
            ] for _ in range(num_natural_attacks)
        ]
        num_enchants = stream.read_dword()
        self.rune_enchantment_type = [
            stream.read_dword() for _ in range(num_enchants)
        ]
        num_contents = stream.read_dword()
        self.rune_inventory_contents = [Inventory() for _ in range(num_contents)]
        for content in self.rune_inventory_contents:
            content.load_binary(stream)

        self.rune_renderable = stream.read_bool()
        if self.rune_renderable:
            self.rune_scale_factor = stream.read_tuple()
            self.rune_skeleton = stream.read_qword()
            self.rune_slope_hugger = stream.read_bool()
            self.rune_can_fly = stream.read_bool()
            self.rune_death_effect = stream.read_qword()
            self.rune_tombstone_id = stream.read_qword()
            num_body_parts = stream.read_dword()
            self.rune_body_parts = [BodyPart() for _ in range(num_body_parts)]
            for part in self.rune_body_parts:
                part.load_binary(stream)
            num_hair = stream.read_dword()
            self.rune_hair = [
                stream.read_qword() for _ in range(num_hair)
            ]
            num_beard = stream.read_dword()
            self.rune_beard = [
                stream.read_qword() for _ in range(num_beard)
            ]
        self.rune_natural_power_attack = stream.read_dword()
        self.rune_sparse_data = SparseData()
        self.rune_sparse_data.load_binary(stream)

    def save_binary(self, stream: ResStream):
        super().save_binary(stream)
        stream.write_dword(self.rune_type)
        stream.write_dword(self.rune_sub_type)
        stream.write_bool(self.rune_is_standard_character_creation)
        stream.write_dword(self.rune_creation_cost)
        stream.write_dword(self.rune_rank)
        stream.write_dword(self.rune_pracs_per_level)
        stream.write_float(self.rune_exp_req_to_level)
        stream.write_dword(self.rune_sex)
        stream.write_qword(self.rune_class_icon)
        stream.write_dword(self.rune_health)
        stream.write_dword(self.rune_mana)
        stream.write_dword(self.rune_stamina)
        stream.write_float(self.rune_min_damage)
        stream.write_float(self.rune_max_damage)
        stream.write_dword(self.rune_attack)
        stream.write_dword(self.rune_defense)
        stream.write_dword(self.rune_level)
        for i in range(7):
            stream.write_float(self.rune_speed[i])
        self.rune_group.save_binary(stream)
        stream.write_string(self.rune_dsc)
        stream.write_string(self.rune_fx_txt)
        stream.write_dword(self.rune_group_tactics)
        stream.write_dword(self.rune_group_role_set)
        stream.write_dword(len(self.rune_enemy_monster_types))
        for monster in self.rune_enemy_monster_types:
            stream.write_dword(monster)
        stream.write_dword(len(self.rune_groupee_monster_types))
        for monster in self.rune_groupee_monster_types:
            stream.write_dword(monster)
        stream.write_dword(len(self.rune_helper_monster_types))
        for monster in self.rune_helper_monster_types:
            stream.write_dword(monster)
        stream.write_dword(len(self.rune_not_enemy_monster_types))
        for monster in self.rune_not_enemy_monster_types:
            stream.write_dword(monster)
        stream.write_dword(len(self.rune_enemy_gender))
        for gender in self.rune_enemy_gender:
            stream.write_dword(gender)
        stream.write_dword(len(self.rune_skill_grant))
        for grant in self.rune_skill_grant:
            grant.save_binary(stream)
        stream.write_dword(len(self.rune_skill_adj))
        for adjust in self.rune_skill_adj:
            adjust.save_binary(stream)
        stream.write_dword(len(self.rune_attr_adj))
        for attr in self.rune_attr_adj:
            attr.save_binary(stream)
        stream.write_dword(len(self.rune_max_attr_adj))
        for attr in self.rune_max_attr_adj:
            attr.save_binary(stream)
        stream.write_dword(len(self.rune_naturalattacks))
        for attack in self.rune_naturalattacks:
            stream.write_float(attack[0])
            stream.write_float(attack[1])
            stream.write_dword(attack[2])
            stream.write_dword(attack[3])
            stream.write_qword(attack[4])
            stream.write_float(attack[5])
            stream.write_dword(attack[6])
            stream.write_dword(attack[7])
            stream.write_dword(attack[8])
            stream.write_dword(attack[9])
        stream.write_dword(len(self.rune_enchantment_type))
        for enchantment in self.rune_enchantment_type:
            stream.write_dword(enchantment)
        stream.write_dword(len(self.rune_inventory_contents))
        for content in self.rune_inventory_contents:
            content.save_binary(stream)
        stream.write_bool(self.rune_renderable)
        if self.rune_renderable:
            stream.write_tuple(self.rune_scale_factor)
            stream.write_qword(self.rune_skeleton)
            stream.write_bool(self.rune_slope_hugger)
            stream.write_bool(self.rune_can_fly)
            stream.write_qword(self.rune_death_effect)
            stream.write_qword(self.rune_tombstone_id)
            stream.write_dword(len(self.rune_body_parts))
            for part in self.rune_body_parts:
                part.save_binary(stream)
            stream.write_dword(len(self.rune_hair))
            for hair in self.rune_hair:
                stream.write_qword(hair)
            stream.write_dword(len(self.rune_beard))
            for beard in self.rune_beard:
                stream.write_qword(beard)
        stream.write_dword(self.rune_natural_power_attack)
        self.rune_sparse_data.save_binary(stream)

    def load_json(self, data):
        super().load_json(data)
        self.rune_type = STRING_TO_RUNE_TYPE[data['rune_type']]
        self.rune_sub_type = string_to_hash(data['rune_sub_type'])
        self.rune_is_standard_character_creation = data['rune_is_standard_character_creation']
        self.rune_creation_cost = data['rune_creation_cost']
        self.rune_rank = data['rune_rank']
        self.rune_pracs_per_level = data['rune_pracs_per_level']
        self.rune_exp_req_to_level = data['rune_exp_req_to_level']
        self.rune_sex = STRING_TO_RUNE_SEX[data['rune_sex']]
        self.rune_class_icon = data['rune_class_icon']
        self.rune_health = data['rune_health']
        self.rune_mana = data['rune_mana']
        self.rune_stamina = data['rune_stamina']
        self.rune_min_damage = data['rune_min_damage']
        self.rune_max_damage = data['rune_max_damage']
        self.rune_attack = data['rune_attack']
        self.rune_defense = data['rune_defense']
        self.rune_level = data['rune_level']
        self.rune_speed = []
        for i in range(7):
            self.rune_speed.append(data['rune_speed'][SPEED_TYPE_TO_STRING[i]])
        self.rune_group = Group()
        self.rune_group.load_json(data['rune_group'])
        self.rune_dsc = data['rune_dsc']
        self.rune_fx_txt = data['rune_fx_txt']
        self.rune_group_tactics = data['rune_group_tactics']
        self.rune_group_role_set = data['rune_group_role_set']
        self.rune_enemy_monster_types = []
        for monster in data['rune_enemy_monster_types']:
            self.rune_enemy_monster_types.append(string_to_hash(monster))
        self.rune_groupee_monster_types = []
        for monster in data['rune_groupee_monster_types']:
            self.rune_groupee_monster_types.append(string_to_hash(monster))
        self.rune_helper_monster_types = []
        for monster in data['rune_helper_monster_types']:
            self.rune_helper_monster_types.append(string_to_hash(monster))
        self.rune_not_enemy_monster_types = []
        for monster in data['rune_not_enemy_monster_types']:
            self.rune_not_enemy_monster_types.append(string_to_hash(monster))
        self.rune_enemy_gender = []
        for gender in data['rune_enemy_gender']:
            self.rune_enemy_gender.append(gender)
        self.rune_skill_grant = []
        for grant_data in data['rune_skill_grant']:
            grant = SkillGrant()
            grant.load_json(grant_data)
            self.rune_skill_grant.append(grant)
        self.rune_skill_adj = []
        for adjust_data in data['rune_skill_adj']:
            adjust = SkillAdjust()
            adjust.load_json(adjust_data)
            self.rune_skill_adj.append(adjust)
        self.rune_attr_adj = []
        for attr_data in data['rune_attr_adj']:
            attr = AttributeValue()
            attr.load_json(attr_data)
            self.rune_attr_adj.append(attr)
        self.rune_max_attr_adj = []
        for attr_data in data['rune_max_attr_adj']:
            attr = AttributeValue()
            attr.load_json(attr_data)
            self.rune_max_attr_adj.append(attr)
        self.rune_naturalattacks = data['rune_naturalattacks']
        self.rune_enchantment_type = []
        for enchantment in data['rune_enchantment_type']:
            self.rune_enchantment_type.append(enchantment)
        self.rune_inventory_contents = []
        for content_data in data['rune_inventory_contents']:
            content = Inventory()
            content.load_json(content_data)
            self.rune_inventory_contents.append(content)
        self.rune_renderable = data['rune_renderable']
        if self.rune_renderable:
            self.rune_scale_factor = data['rune_scale_factor']
            self.rune_skeleton = data['rune_skeleton']
            self.rune_slope_hugger = data['rune_slope_hugger']
            self.rune_can_fly = data['rune_can_fly']
            self.rune_death_effect = data['rune_death_effect']
            self.rune_tombstone_id = data['rune_tombstone_id']
            self.rune_body_parts = []
            for part_data in data['rune_body_parts']:
                part = BodyPart()
                part.load_json(part_data)
                self.rune_body_parts.append(part)
            self.rune_hair = data['rune_hair']
            self.rune_beard = data['rune_beard']
        self.rune_natural_power_attack = data['rune_natural_power_attack']
        self.rune_sparse_data = SparseData()
        self.rune_sparse_data.load_json(data['rune_sparse_data'])

    def save_json(self):
        data = super().save_json()
        data['rune_type'] = RUNE_TYPE_TO_STRING[self.rune_type]
        data['rune_sub_type'] = hash_to_string(self.rune_sub_type)
        data['rune_is_standard_character_creation'] = self.rune_is_standard_character_creation
        data['rune_creation_cost'] = self.rune_creation_cost
        data['rune_rank'] = self.rune_rank
        data['rune_pracs_per_level'] = self.rune_pracs_per_level
        data['rune_exp_req_to_level'] = self.rune_exp_req_to_level
        data['rune_sex'] = RUNE_SEX_TO_STRING[self.rune_sex]
        data['rune_class_icon'] = self.rune_class_icon
        data['rune_health'] = self.rune_health
        data['rune_mana'] = self.rune_mana
        data['rune_stamina'] = self.rune_stamina
        data['rune_min_damage'] = self.rune_min_damage
        data['rune_max_damage'] = self.rune_max_damage
        data['rune_attack'] = self.rune_attack
        data['rune_defense'] = self.rune_defense
        data['rune_level'] = self.rune_level
        data['rune_speed'] = OrderedDict()
        for i in range(7):
            data['rune_speed'][SPEED_TYPE_TO_STRING[i]] = self.rune_speed[i]
        data['rune_group'] = self.rune_group.save_json()
        data['rune_dsc'] = self.rune_dsc
        data['rune_fx_txt'] = self.rune_fx_txt
        data['rune_group_tactics'] = self.rune_group_tactics
        data['rune_group_role_set'] = self.rune_group_role_set
        data['rune_enemy_monster_types'] = []
        for monster in self.rune_enemy_monster_types:
            data['rune_enemy_monster_types'].append(hash_to_string(monster))
        data['rune_groupee_monster_types'] = []
        for monster in self.rune_groupee_monster_types:
            data['rune_groupee_monster_types'].append(hash_to_string(monster))
        data['rune_helper_monster_types'] = []
        for monster in self.rune_helper_monster_types:
            data['rune_helper_monster_types'].append(hash_to_string(monster))
        data['rune_not_enemy_monster_types'] = []
        for monster in self.rune_not_enemy_monster_types:
            data['rune_not_enemy_monster_types'].append(hash_to_string(monster))
        data['rune_enemy_gender'] = []
        for gender in self.rune_enemy_gender:
            data['rune_enemy_gender'].append(gender)
        data['rune_skill_grant'] = []
        for grant in self.rune_skill_grant:
            data['rune_skill_grant'].append(grant.save_json())
        data['rune_skill_adj'] = []
        for adjust in self.rune_skill_adj:
            data['rune_skill_adj'].append(adjust.save_json())
        data['rune_attr_adj'] = []
        for attr in self.rune_attr_adj:
            data['rune_attr_adj'].append(attr.save_json())
        data['rune_max_attr_adj'] = []
        for attr in self.rune_max_attr_adj:
            data['rune_max_attr_adj'].append(attr.save_json())
        data['rune_naturalattacks'] = self.rune_naturalattacks
        data['rune_enchantment_type'] = []
        for enchantment in self.rune_enchantment_type:
            data['rune_enchantment_type'].append(enchantment)
        data['rune_inventory_contents'] = []
        for content in self.rune_inventory_contents:
            data['rune_inventory_contents'].append(content.save_json())
        data['rune_renderable'] = self.rune_renderable
        if self.rune_renderable:
            data['rune_scale_factor'] = self.rune_scale_factor
            data['rune_skeleton'] = self.rune_skeleton
            data['rune_slope_hugger'] = self.rune_slope_hugger
            data['rune_can_fly'] = self.rune_can_fly
            data['rune_death_effect'] = self.rune_death_effect
            data['rune_tombstone_id'] = self.rune_tombstone_id
            data['rune_body_parts'] = []
            for part in self.rune_body_parts:
                data['rune_body_parts'].append(part.save_json())
            data['rune_hair'] = self.rune_hair
            data['rune_beard'] = self.rune_beard
        data['rune_natural_power_attack'] = self.rune_natural_power_attack
        data['rune_sparse_data'] = self.rune_sparse_data.save_json()
        return data
