from functools import partial

import core.plant
import core.plant.tree
from core.template import Template
from core.template.animation import Frames
from core.template.item import Loot

from siege.component import Animation, Event, Organic
from siege.graphic import TextureCache
from siege.util import AssetCache


def plantInit(self, entity, organic):
    core.plant.Plant.__init__(self, entity, organic)


def treeInit(self, entity, organic):
    core.plant.tree.Tree.__init__(self, entity, organic)


class OrganicTemplate(Template):
    def __init__(self, name, spaceRequired, hasAnimation=False, **kwargs):
        Template.__init__(self, name, **kwargs)
        self.cls = None
        if hasAnimation:
            self.add(Animation())
        self.add(Event())
        self.add(Organic(self.factory, spaceRequired, kwargs.get('growsOn', ['block'])))

    def factory(self, entity, organic):
        return self.cls(entity, organic)

    def provides(self, func):
        """Decorator for assigning a function to the created plant class."""
        setattr(self.cls, func.__name__, func)


class Plant(OrganicTemplate):
    def __init__(self, name='', spaceRequired=None, variations=1, stages=None, **kwargs):
        OrganicTemplate.__init__(self, name, spaceRequired, hasAnimation=True, **kwargs)
        self.variations = variations
        stages = stages or []
        for index, stage in enumerate(stages):
            if not stage.animations:
                stage.animations['idle'] = "idle_v{{}}_s{s}".format(s=index + 1)
            if stage.nextStage == -1 and index != len(stages) - 1:
                stage.nextStage = index + 1
        # Dynamically create a class for this plant
        clsAttrs = {
            '__init__': plantInit,
            'stages': [stage.createFactory() for stage in stages],
            'variations': variations
        }
        attrs = ['stageAfterHarvest', 'life', 'recoveryTime', 'allowSupportRemoval', 'onHarvestSound']
        for attr in attrs:
            if attr in kwargs:
                clsAttrs[attr] = kwargs[attr]
        self.cls = type(name, (core.plant.Plant,), clsAttrs)

    def stageAnimations(self, *args):
        animations = {}
        frames = self.getSpriteFrames(frames=args)
        for index, frame in enumerate(args):
            variant, stage = divmod(index, self.variations)
            animation = "idle_v{}_s{}".format(variant + 1, stage + 1)
            animations[animation] = Frames(frames(index + 1))
        self.animations(start='idle_v1_s1', **animations)


class PlantStage(object):
    def __init__(self, animations=None, loot=None, nextStage=-1):
        self.animations = animations or {}
        self.loot = [loot] if isinstance(loot, Loot) else loot
        self.nextStage = nextStage

    def createFactory(self):
        return partial(core.plant.PlantStage, self.animations, self.loot, self.nextStage)


class PlantStageTimer(object):
    def __init__(self, animations=None, loot=None, nextStage=-1, duration=0):
        self.animations = animations or {}
        self.loot = [loot] if isinstance(loot, Loot) else loot
        self.nextStage = nextStage
        self.duration = duration

    def createFactory(self):
        return partial(core.plant.PlantStageTimer, self.animations, self.loot, self.nextStage, self.duration)


class Tree(OrganicTemplate):
    def __init__(self, name='', spaceRequired=None, **kwargs):
        OrganicTemplate.__init__(self, name, spaceRequired, isModular=True, **kwargs)
        clsAttrs = {}
        attrs = [
            'seed',
            'seedRange',
            'wood',
            'growthTimeMin',
            'growthTimeMax',
            'recoveryTime',
            'partLife',
            'trunkRange',
            'level',
            'particleColor',
            'dropsFeather'
        ]
        for attr in attrs:
            if attr in kwargs:
                clsAttrs[attr] = kwargs[attr]
        attrs = ['bases', 'trunks', 'crowns', 'saplings']
        for attr in attrs:
            if attr in kwargs:
                clsAttrs[attr] = [image.get(self.content.getPath()) for image in kwargs[attr]]
        self.cls = type(name, (core.plant.tree.Tree,), clsAttrs)


class TreeImage(object):
    def __init__(self, image='', center=-1):
        self.image = image
        self.center = center

    def get(self, path):
        image = AssetCache.getFullPath(self.image, path)
        texture = TextureCache.get(image)
        if self.center == -1:
            self.center = texture.width / 2
        return core.plant.tree.TreeImage(image, self.center, texture.height)
