from functools import partial

from core.talent import concludeSkillUse, createSkill, setSkillCooldown
from core.tuning.skill import SkillTuning

from siege import Locale, game, Talent
from siege.component import ToolbarSlot
from siege.graphic import PixelCheck
from siege.util import Random, Rect
from siege.world.realm import EntityLayer


def getTalent():
    talent = Talent("gather", "gather/icon.png", [1000] * 20)
    talent.skills.append(Collect.create())
    talent.skills.append(ToolSpeed.create())
    talent.skills.append(ToolPower.create())
    talent.skills.append(ToolReach.create())
    talent.skills.append(PickupReach.create())
    talent.skills.append(Scavenge.create())
    talent.skills.append(Precision.create())
    # Lockpick
    # Detect (Treasure|Plant|Ore)
    game.events['create_talent'].invoke(talent)
    return talent


class Collect(object):
    @staticmethod
    def onClientUse(player, level, position, index, slot, tool):
        player.entity.render.hideSprite("item")
        player.entity.animation.play("thrust", "arm_front")
        setSkillCooldown(player, level, tool, slot, SkillTuning.COLLECT)

    @staticmethod
    def onUse(player, level, position, index, slot, tool):
        Collect.onClientUse(player, level, position, index, slot, tool)
        realm = player.entity.realm
        if player.entity.stats.reach.get() < player.entity.physics.getCenter().getDistance(position, realm.size.width):
            #The item is out of range
            return
        area = Rect(position.x, position.y, 1, 1)
        #Find the topmost entity that has a placement component and damage it
        for layer in reversed(realm.layers.getOrdered()):
            if not isinstance(layer, EntityLayer):
                continue
            nearby = layer.getNearby(area)
            for entity in nearby:
                if entity.has("placement") and entity.placement.allowCollection:
                    if PixelCheck.contains(entity.render, area, realm.size.width):
                        game.placement.damage(entity)
                        return
                elif entity.has("event") and entity.event.has("harvest"):
                    if PixelCheck.contains(entity.render, area, realm.size.width):
                        entity.event["harvest"].invoke(player, entity, tool.left if slot == ToolbarSlot.LEFT else tool.right, 1, position)
                        return
        concludeSkillUse(player, player.entity.talents.gather, level, tool, slot, SkillTuning.COLLECT)

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

    @staticmethod
    def create():
        return createSkill(Collect, SkillTuning.COLLECT)


class ToolSpeed(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.event["use_delay"].listen(partial(ToolSpeed.handleUseDelay, level))

    @staticmethod
    def onDeactivate(level, player):
        player.entity.event["use_delay"].remove(partial(ToolSpeed.handleUseDelay, level))

    @staticmethod
    def handleUseDelay(level, item, delay):
        if item.has("tool"):
            delay.final -= int(delay.start * (level * SkillTuning.TOOL_SPEED.AMOUNT_PER_LEVEL / 100.0))

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

    @staticmethod
    def create():
        return createSkill(ToolSpeed, SkillTuning.TOOL_SPEED)


class ToolPower(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.event["tool_power"].listen(partial(ToolPower.handleToolPower, level))

    @staticmethod
    def onDeactivate(level, player):
        player.entity.event["tool_power"].remove(partial(ToolPower.handleToolPower, level))

    @staticmethod
    def handleToolPower(level, item, power):
        power.final += int((power.start * level * SkillTuning.TOOL_POWER.AMOUNT_PER_LEVEL) / 100.0)

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

    @staticmethod
    def create():
        return createSkill(ToolPower, SkillTuning.TOOL_POWER)


class ToolReach(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.event["tool_reach"].listen(partial(ToolReach.handleToolReach, level))

    @staticmethod
    def onDeactivate(level, player):
        player.entity.event["tool_reach"].remove(partial(ToolReach.handleToolReach, level))

    @staticmethod
    def handleToolReach(level, item, reach):
        reach.final += level * SkillTuning.TOOL_REACH.AMOUNT_PER_LEVEL

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

    @staticmethod
    def create():
        return createSkill(ToolReach, SkillTuning.TOOL_REACH)


class PickupReach(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.stats.reach.adjust(level * SkillTuning.PICKUP_REACH.AMOUNT_PER_LEVEL)

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

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

    @staticmethod
    def create():
        return createSkill(PickupReach, SkillTuning.PICKUP_REACH)


class Scavenge(object):
    @staticmethod
    def onActivate(level, player):
        game.events["gather_tile"].listen(partial(Scavenge.handleGatherTile, level, player.uid))

    @staticmethod
    def onDeactivate(level, player):
        game.events["gather_tile"].remove(partial(Scavenge.handleGatherTile, level, player.uid))

    @staticmethod
    def handleGatherTile(level, ignore, player, tile, item, tilePosition):
        amount = level * SkillTuning.SCAVENGE.AMOUNT_PER_LEVEL
        if amount >= Random.get(100):
            item.dropped.quantity += 1

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

    @staticmethod
    def create():
        return createSkill(Scavenge, SkillTuning.SCAVENGE)


class Precision(object):
    @staticmethod
    def onActivate(level, player):
        player.entity.event["tool_reach"].listen(partial(Precision.handleToolPower, level))

    @staticmethod
    def onDeactivate(level, player):
        player.entity.event["tool_reach"].remove(partial(Precision.handleToolPower, level))

    @staticmethod
    def handleToolPower(level, item, power):
        chance = level * SkillTuning.PRECISION.AMOUNT_PER_LEVEL
        if chance >= Random.get(100):
            power.final = 9999

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

    @staticmethod
    def create():
        return createSkill(Precision, SkillTuning.PRECISION)
