from functools import partial

from core.helper import sendMessage
from core.talent import applyEffectSpell, castSpell, castSpellClient, concludeSkillUse, createSkill, getAllyTarget, getTarget, usableSkillCost, visualSpell
from core.tuning.skill import SkillTuning

from siege import Locale, game, Talent
from siege.graphic import Color, TextureCache
from siege.particle import ParticleEmitter
from siege.util import PixelRect, seconds, Random, RangeColor, RangeInt, RangeVector, Vector


def getTalent():
    # Diving Magic
    talent = Talent("divine", "divine/icon.png", [1000] * 20)
    talent.skills.append(Heal.create())
    talent.skills.append(Protect.create())
    talent.skills.append(Shell.create())
    talent.skills.append(Regenerate.create())
    talent.skills.append(Dispel.create())
    talent.skills.append(Invisible.create())
    talent.skills.append(SPRegenBoost.create())
    talent.skills.append(Haste.create())
    talent.skills.append(Stoneskin.create())
    talent.skills.append(Veil.create())
    talent.skills.append(Quicken.create())
    # talent.skills.append(Raise.create())
    game.events['create_talent'].invoke(talent)
    return talent


def emitSupportParticles(realm, target, particlePath):
    emitter = ParticleEmitter(realm.uid, realm.size.width, Vector(0, -10), 300, 64)
    emitter.amount = Random.get(3, 4)
    coords = [
        PixelRect(0, 0, 7, 7),
        PixelRect(7, 1, 5, 5),
        PixelRect(8, 2, 3, 3)
    ]
    emitter.setTexture(TextureCache.get(particlePath), coords)
    emitter.attachTo(target)
    emitter.particlePosition = RangeVector(Vector(-7, 2), Vector(7, -2))
    emitter.particleLife = RangeInt(seconds(1), seconds(2))
    emitter.velocityStart = RangeVector(Vector(-0.25, -0.5), Vector(0.25, -1.0))
    emitter.velocityChange = RangeVector(Vector(0, -0.3), Vector(0, -0.1))
    emitter.colorStart = RangeColor(Color(255, 255, 255), Color(255, 255, 150))
    emitter.colorEnd = RangeColor(Color(255, 255, 255, 0), Color(255, 255, 255, 0))
    game.particles.add(emitter)


class Heal(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.HEAL.RANGE)
        if target:
            callback = partial(Heal.apply, player, level, tool, slot, target)
            castSpell(player, level, tool, slot, SkillTuning.HEAL, callback)

    @staticmethod
    def apply(player, level, tool, slot, target):
        realm = player.entity.realm
        hp = target.stats.HP
        #TODO: Use MIND
        value = int(hp.adjust(SkillTuning.HEAL.HP_AMOUNT[level - 1]) * Random.get(0.9, 1.1))
        game.combat.showCombatNumber(target, value, False)
        emitSupportParticles(realm, target, "divine/heal_particles.png")
        concludeSkillUse(player, player.entity.talents.divine, level, tool, slot, SkillTuning.HEAL)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.HEAL)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.HEAL.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("HealDesc")

    @staticmethod
    def create():
        return createSkill(Heal, SkillTuning.HEAL)


class Protect(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.PROTECT.RANGE)
        callback = partial(applyEffectSpell, player, player.entity.talents.divine, level, tool, slot, target, 'protect', SkillTuning.PROTECT.NAME, SkillTuning.PROTECT, Vector(0, -8))
        castSpell(player, level, tool, slot, SkillTuning.PROTECT, callback)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.PROTECT)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.PROTECT)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.PROTECT.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("ProtectDesc")

    @staticmethod
    def create():
        return createSkill(Protect, SkillTuning.PROTECT)


class Shell(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.SHELL.RANGE)
        callback = partial(applyEffectSpell, player, player.entity.talents.divine, level, tool, slot, target, 'shell', SkillTuning.SHELL.NAME, SkillTuning.SHELL)
        castSpell(player, level, tool, slot, SkillTuning.SHELL, callback)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.SHELL)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.SHELL)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.SHELL.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("ShellDesc")

    @staticmethod
    def create():
        return createSkill(Shell, SkillTuning.SHELL)


class Regenerate(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.REGENERATE.RANGE)
        if target:
            callback = partial(Regenerate.apply, player, level, tool, slot, target)
            castSpell(player, level, tool, slot, SkillTuning.REGENERATE, callback)

    @staticmethod
    def apply(player, level, tool, slot, target):
        player.entity.effects.add("Regen", level, SkillTuning.REGENERATE.DURATION)
        emitSupportParticles(player.entity.realm, target, "divine/regenerate_particles.png")
        concludeSkillUse(player, player.entity.talents.divine, level, tool, slot, SkillTuning.REGENERATE)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.REGENERATE)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.REGENERATE.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("RegenerateDesc")

    @staticmethod
    def create():
        return createSkill(Regenerate, SkillTuning.REGENERATE)


class Dispel(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getTarget(player, position, SkillTuning.DISPEL.RANGE)
        callback = partial(Dispel.apply, player, level, tool, slot, target)
        castSpell(player, level, tool, slot, SkillTuning.DISPEL, callback)

    @staticmethod
    def apply(player, level, tool, slot, target):
        visualSpell(player, player.entity.talents.divine, level, tool, slot, target, 'dispel', SkillTuning.DISPEL)
        isPositive = player.entity.combat.team is not target.combat.team
        for effect in target.effects.getEffects():
            name = effect.key()
            activeEffect = effect.data()
            if activeEffect.effect.isPositive() == isPositive:
                target.effects.remove(name)
                sendMessage("general", "System", "DispelMsg", player.networkId, target=target.name, effect=name)
                return
        sendMessage("general", "System", "DispelFailMsg", player.networkId, target=target.name)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.DISPEL)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.DISPEL)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.DISPEL.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("DispelDesc")

    @staticmethod
    def create():
        return createSkill(Dispel, SkillTuning.DISPEL)


class Invisible(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.INVISIBLE.RANGE)
        callback = partial(applyEffectSpell, player, player.entity.talents.divine, level, tool, slot, target, 'invisible', SkillTuning.INVISIBLE.NAME, SkillTuning.INVISIBLE)
        castSpell(player, level, tool, slot, SkillTuning.INVISIBLE, callback)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.INVISIBLE)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.INVISIBLE)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.INVISIBLE.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("InvisibleDesc")

    @staticmethod
    def create():
        return createSkill(Invisible, SkillTuning.INVISIBLE)


class Haste(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.HASTE.RANGE)
        if target:
            callback = partial(Haste.apply, player, level, tool, slot, target)
            castSpell(player, level, tool, slot, SkillTuning.HASTE, callback)

    @staticmethod
    def apply(player, level, tool, slot, target):
        player.entity.effects.add("Haste", level, SkillTuning.HASTE.DURATION)
        emitSupportParticles(player.entity.realm, target, "divine/haste_particles.png")
        concludeSkillUse(player, player.entity.talents.divine, level, tool, slot, SkillTuning.HASTE)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.HASTE)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.HASTE.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("HasteDesc")

    @staticmethod
    def create():
        return createSkill(Haste, SkillTuning.HASTE)


class Stoneskin(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.STONESKIN.RANGE)
        callback = partial(applyEffectSpell, player, player.entity.talents.divine, level, tool, slot, target, 'stoneskin', SkillTuning.STONESKIN.NAME, SkillTuning.STONESKIN, Vector(2, -4))
        castSpell(player, level, tool, slot, SkillTuning.STONESKIN, callback)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.STONESKIN)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.STONESKIN)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.STONESKIN.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("StoneskinDesc")

    @staticmethod
    def create():
        return createSkill(Stoneskin, SkillTuning.STONESKIN)


class Veil(object):
    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        target = getAllyTarget(player, position, SkillTuning.VEIL.RANGE)
        callback = partial(applyEffectSpell, player, player.entity.talents.divine, level, tool, slot, target, 'veil', SkillTuning.VEIL.NAME, SkillTuning.VEIL)
        castSpell(player, level, tool, slot, SkillTuning.VEIL, callback)

    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        castSpellClient(player, level, tool, slot, SkillTuning.VEIL)

    @staticmethod
    def isAvailable(player, level, index, slot, tool):
        return usableSkillCost(player, level, SkillTuning.VEIL)

    @staticmethod
    def isUsable(player, level, position, index, slot, tool):
        return getAllyTarget(player, position, SkillTuning.VEIL.RANGE) is not None

    @staticmethod
    def description(level):
        return Locale.getEscaped("VeilDesc")

    @staticmethod
    def create():
        return createSkill(Veil, SkillTuning.VEIL)


class Quicken(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.event['skill_cooldown'].listen(partial(Quicken.handleSkillCooldown, player.entity, level))

    @staticmethod
    def onDeactivate(level, player):
        player.entity.event['skill_cooldown'].remove(partial(Quicken.handleSkillCooldown, player.entity, level))

    @staticmethod
    def handleSkillCooldown(entity, level, skill, cooldown):
        talents = entity.talents
        if talents.divine.has(skill.name) or talents.arcane.has(skill.name):
            percentage = level * SkillTuning.QUICKEN.AMOUNT_PER_LEVEL
            cooldown.duration -= int(cooldown.duration * percentage)

    @staticmethod
    def description(level):
        return Locale.getEscaped("QuickenDesc")

    @staticmethod
    def create():
        return createSkill(Quicken, SkillTuning.QUICKEN)


class SPRegenBoost(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.stats.SPRegen.adjust(SkillTuning.SP_REGEN_BOOST.AMOUNT_PER_LEVEL * level)

    @staticmethod
    def onDeactivate(level, player):
        player.entity.stats.SPRegen.adjust(-SkillTuning.SP_REGEN_BOOST.AMOUNT_PER_LEVEL * level)

    @staticmethod
    def description(level):
        return Locale.getEscaped("SPRegenBoostDesc")

    @staticmethod
    def create():
        return createSkill(SPRegenBoost, SkillTuning.SP_REGEN_BOOST)
