import Actor
import Spell
import Characteristics
from Log import LOG
from Gui import GUI
import utils
from Reader import reader

class Entity():
    entities = {}
    def __init__(self, actor=None):
        self.pre_boost = False
        self.is_playing = False
        self.alive = True
        self.name = ""
        self.sequences = []
        self.summons = []
        self.spells = []
        self.effects = []
        self.spells.append(Spell.Spell())
        self.spells[0].spell_name = "HoT/DoT"
        self.is_player = False
        self.active_spell = self.spells[0]
        self.turns = 0
        self.summoner = None
        self.contextual_id = 0
        self.base_pa = 0
        self.lost_pa = 0
        self.current_pa = 0
        self.spent_pa = 0
        self.base_pm = 0
        self.lost_pm = 0
        self.current_pm = 0
        self.spent_pm = 0
        self.updated_stat_list = None
        if actor is not None:
            self.load_actor(actor)
        return

    def load_actor(self, actor, summoned_by=None):
        if hasattr(actor.entity,'breed'):
            self.breed = actor.entity.breed
        else:
            self.breed = -1
        self.actor = actor
        self.contextual_id = actor.entity.contextual_id
        if self.contextual_id > 0:
            self.is_player = True
        self.name = actor.entity.name
        self.team = actor.entity.spawn_info.team_id
        self.alive = actor.entity.spawn_info.alive
        self.summoner = summoned_by
        self.base_pm = 20 #to re-do
        self.base_pa = 20 #to re-do
        self.summons = []
        self.sequences = []
        Entity.entities[self.contextual_id] = self

    def parse_sequence(self):
        return
        #sequence = Sequence.parse_sequence()
        #self.sequences.append(sequence)

    def entity_death(self):
        self.alive = False

    def sequence_end(self):
        if self.is_playing == True:
            self.compute_effects()

    def parse_attack(self):
        spell = Spell.Spell().parse_attack()
        self.spells.append(spell)
        return spell

    def add_effect(self, effect):
        stacks = 0
        for existing in self.effects:
            if effect.effect_id == existing.effect_id:
                stacks += 1
        #this assumes delay prevents from replacing a buff (see precipitation, rekop)
        if stacks == 0 or effect.max_stack <= 0:
            self.effects.append(effect)
        if stacks >= effect.max_stack and stacks > 0 and effect.max_stack > 0:
            if effect.delay > 0:
                return
            for existing in self.effects:
                if effect.effect_id == existing.effect_id:
                    self.effects.remove(existing)
                    break

    def dispell_single_effect(self, uid):
        for effect in self.effects[::]:
            if effect.uid == uid:
                self.effects.remove(effect)
                break

    def dispell_effects(self, delta):
        for effect in self.effects[::]:
            if effect.delay > 0 or effect.turn_duration <= -1000:
                continue
            effect.turn_duration += delta
            if effect.turn_duration <= 0:
                self.effects.remove(effect)

    def dispell_effects_from_source(self, uid):
        for effect in self.effects[::]:
            if effect.source_id == uid:
                self.effects.remove(effect)

    def compute_effects(self):
        self.current_pa = self.base_pa - self.spent_pa
        self.current_pm = self.base_pm - self.spent_pm
        LOG.debug(f"Currently {len(self.effects)} on {self.name} ({self.base_pa}/{self.base_pm})")
        for effect in self.effects:
            if effect.delay > 0:
                continue
            elif effect.effect_type == 1:
                if effect.negative == True:
                    self.current_pa -= effect.delta
                else:
                    self.current_pa += effect.delta
            elif effect.effect_type == 23:
                if effect.negative == True:
                    self.current_pm -= effect.delta
                else:
                    self.current_pm += effect.delta
            else:
                continue
            LOG.debug(f"({effect.uid}){effect.effect_id} {effect.name}({effect.spell_id}) {effect.delta} {effect.effect_type} ({effect.operator}) {effect.turn_duration} turns")
        LOG.debug(f"{self.name}, {self.current_pa}/{self.current_pm} (compute)")
        self.update_overview_gui()

    def reset_used_papm(self):
        self.current_pa = self.base_pa - self.spent_pa
        self.current_pm = self.base_pm - self.spent_pm


    def cycle_effects_from_source(self, uid):
        for effect in self.effects[::]:
            if effect.source_id == uid:
                if effect.delay > 0:
                    effect.delay -= 1
                elif effect.turn_duration <= -1000:
                    continue
                else:
                    if effect.turn_duration <= 1:
                        self.effects.remove(effect)
                    else:
                        effect.turn_duration -= 1

    def determine_base_papm(self):
        max_pa = self.actor.entity.stats.max_action_points
        max_pm = self.actor.entity.stats.max_movement_points
        curr_pa = self.actor.entity.stats.action_points
        curr_pm = self.actor.entity.stats.movement_points
        self.spent_pa = max_pa - curr_pa
        self.spent_pm = max_pm - curr_pm
        for effect in self.effects:
            if effect.delay >= 1:
                continue
            elif effect.effect_type == 1:
                if effect.negative == False:
                    max_pa -= effect.delta
                else:
                    max_pa += effect.delta
            elif effect.effect_type == 23:
                if effect.negative == False:
                    max_pm -= effect.delta
                else:
                    max_pm += effect.delta
        self.base_pa = max_pa
        self.base_pm = max_pm
        self.current_pa = curr_pa
        self.current_pm = curr_pm
        LOG.debug(f"{self.name} ({self.base_pa}/{self.base_pm}) {curr_pa}/{curr_pm} (spent: {self.spent_pa}/{self.spent_pm})")


    def parse_spell(self):
        spell = Spell.Spell().parse_spell()
        self.spells.append(spell)
        return spell

    def parse_points_variation(self, delta, action_id):
        if self.is_playing == False:
            if action_id == 101:
                action_id = 102
            if action_id == 127:
                action_id = 129
        if action_id == 101:
            self.current_pa += delta
            self.lost_pa += delta * -1
        if action_id == 127:
            self.current_pm += delta
            self.lost_pm += delta * -1
        if action_id == 102:
            self.current_pa += delta
            self.spent_pa += delta * -1
        if action_id == 129:
            self.current_pm += delta
            self.spent_pm += delta * -1
        return

    def set_active_spell(self):
        self.active_spell = self.spells[-1]

    def set_inactive_spell(self):
        self.active_spell = self.spells[0]

    def parse_damage(self, latent, shield=False):
        if latent == True:
            self.spells[0].parse_damage(shield)
            curr = self.spells[0]
        else:
            self.spells[-1].parse_damage(shield)
            curr = self.spells[-1]
        return curr

    def update_stats(self):
        self.updated_stat_list = Characteristics.Characteristics()
        self.current_pa = self.updated_stat_list.action_points_current
        self.current_pm = self.updated_stat_list.movement_points_current

    def get_damage(self):
        dmg = 0
        for spell in self.spells:
            for damage in spell.damage:
                if damage['target_id'] in Entity.entities and Entity.entities[damage['target_id']].team == self.team:
                    continue
                dmg += damage['loss']
        return dmg

    def get_elem(self):
        dmg = 0
        elem = -1
        for spell in self.spells:
            for damage in spell.damage:
                if elem == -1:
                    elem = damage['element_id']
                if elem != damage['element_id']:
                    return 'multi'
        return dmg

    def get_breed(self):
        breeds = {
        "0": "monster",
        "1": "feca",
        "2": "osamodas",
        "3": "enutrof",
        "4": "sram",
        "5": "xelor",
        "6": "ecaflip",
        "7": "eniripsa",
        "8": "iop",
        "9": "cra",
        "10": "sadida",
        "11": "sacrieur",
        "12": "pandawa",
        "13": "roublard",
        "14": "zobal",
        "15": "steamer",
        "16": "eliotrope",
        "17": "huppermage",
        "18": "ouginak",
        }
        return breeds[str(self.breed)]
    def update_overview_gui(self):
        effects = []
        for e in self.effects:
            if e.effect_type == 1 or e.effect_type == 23:
                effects.append(e)
        GUI.update_overview_html(self.name, str(self.current_pa), str(self.current_pm), "", effects)
