from core.helper import getRandomPath
from core.tuning.skill import SkillTuning

from siege import game
from siege.component import AxisType
from siege.util import Random, seconds, Sizes, Vector
from siege.world.realm import Layer


class Plant(object):
    # A list of 0 argument callables that returns a plant stage derived from PlantStage.
    stages = []
    # Index of the stage to change to. None denotes destroy entity on harvest.
    stageAfterHarvest = None
    # Life
    life = 3
    # Recovery time
    recoveryTime = seconds(10)
    # Number of variations for the plant
    variations = 3
    # Allow harvesting through collection
    allowCollection = True
    # Allow support removal or not
    allowSupportRemoval = False
    # Sounds when the plant is being harvested
    onHarvestSound = "mods/core/audio/sfx/foliage/plant.ogg"

    def __init__(self, entity, organic):
        self.organic = organic
        self.animation = entity.animation
        self.variation = Random.get(1, self.variations)
        self.stageIndex = 0
        self.stage = self.stages[self.stageIndex](self)
        entity.event["harvest"].listen(self.onHarvest)
        entity.event["can_remove_support"].listen(self.onCanRemoveSupport)
        entity.event['remove_support'].listen(self.handleRemoveSupport)

    def canGrow(self):
        return self.stageIndex < len(self.stages) - 1

    def grow(self):
        pass

    def setStage(self, stage):
        if stage >= len(self.stages):
            raise RuntimeError("{0} is not a valid stage.".format(stage))
        self.stage.expire()
        self.stageIndex = stage
        self.stage = self.stages[stage](self)

    def onHarvest(self, player, entity, tool, power, position):
        if not self.stage.harvestLoot or (not self.allowCollection and tool.isSkill() and tool.skill.name == SkillTuning.COLLECT.NAME):
            return
        if self.onHarvestSound:
            game.audio.playAt(getRandomPath(self.onHarvestSound), position, broadcast=True)
        if not game.damage(str(entity.id), 1, self.life, self.recoveryTime):
            return
        self.harvest(entity)

    def harvest(self, entity):
        if self.stage.harvestLoot and self.stage.harvestLoot[0] is not None:
            for loot in self.stage.harvestLoot:
                if loot.dropped():
                    entity.realm.dropped.create(loot.retrieve(), entity.render.getCenter(), Vector(0, -6))
        if self.stageAfterHarvest is None:
            self.stage.expire()
            entity.destroy()
        else:
            self.setStage(self.stageAfterHarvest)

    def isSupportTile(self, args):
        position = args['entity'].getPosition()
        if self.organic.axis == AxisType.AXIS_FLOOR:
            position.y += Sizes.TILE / 2
        elif self.organic.axis == AxisType.AXIS_CEILING:
            position.y -= Sizes.TILE / 2
        else:
            assert False, "Unsupported axis type defined for plant."
        tilePos = args["layers"][Layer.Ground].getTilePosition(position)
        return tilePos.x is args['tilePos'].x and tilePos.y is args['tilePos'].y

    def onCanRemoveSupport(self, args):
        if self.allowSupportRemoval or args['layer'].type == Layer.Wall:
            return
        if self.isSupportTile(args):
            args['cancel'] = True

    def handleRemoveSupport(self, args):
        if self.isSupportTile(args) and args['forced']:
            entity = args['entity']
            self.harvest(entity)
            self.stage.expire()
            entity.destroy()

    def write(self, stream):
        stream.writeInt8(self.variation)
        stream.writeInt8(self.stageIndex)
        self.stage.write(stream)

    def read(self, stream):
        self.variation = stream.readInt8()
        self.setStage(stream.readInt8())
        self.stage.read(stream)


class PlantStage(object):
    # Animations is a dictionary of animation type to a list of variations of that animation type.
    def __init__(self, animations, harvestLoot, nextStage, plant):
        self.animations = animations
        self.harvestLoot = harvestLoot
        self.nextStage = nextStage
        plant.animation.play(self.animations['idle'].format(plant.variation))

    def expire(self):
        pass

    def grow(self):
        pass

    def write(self, stream):
        pass

    def read(self, stream):
        pass


class PlantStageTimer(PlantStage):
    def __init__(self, animations, harvestLoot, nextStage, time, plant):
        super(PlantStageTimer, self).__init__(animations, harvestLoot, nextStage, plant)
        self.plant = plant
        self.growJobId = game.timer.add(time.getRandom(), self.grow)

    def expire(self):
        game.timer.cancel(self.growJobId)
        self.plant = None

    def grow(self):
        self.plant.setStage(self.nextStage)

    def write(self, stream):
        stream.writeInt32(game.timer.getTime(self.growJobId))

    def read(self, stream):
        game.timer.cancel(self.growJobId)
        self.growJobId = game.timer.add(stream.readInt32(), self.grow)


def useSeed(plant, allowUnderground=False, args=None):
    """ First calculate the position of the new plant and see if it is ok. """
    position = args["position"]
    realm = args["realm"]
    ground = realm.layers[Layer.Ground]
    player = args["player"]
    if player.entity.physics.getCenter().getDistance(position, realm.size.width) > player.entity.stats.reach.get():
        return
    #If the tile clicked on is solid as well as the tile above it then do not allow seed planting
    tilePos = ground.getTilePosition(position)
    if ground.isSolid(tilePos):
        tilePos.move(0, -1)
        if ground.isSolid(tilePos):
            return
        position.y -= Sizes.TILE
    if not allowUnderground and tilePos.getWorld().y > realm.groundLevel:
        return
    hasSupport = False
    for y in xrange(2):
        tilePos.move(0, 1)
        if ground.isSolid(tilePos):
            hasSupport = True
            break
    if hasSupport:
        position.y = tilePos.getWorld().y * Sizes.TILE
        if game.organic.plant(game.content.get(plant), realm, Layer.Back, position):
            args['decrement'] = 1


def handlePlantRemoveSupport(args):
    entity = args['entity']
    position = entity.getPosition()
    position.y += entity.render.getSize().y + 1
    tilePos = args["layers"][Layer.Ground].getTilePosition(position)
    if tilePos.x is not args['tilePos'].x or tilePos.y is not args['tilePos'].y:
        return
    if args['forced']:
        #Harvest?
        entity.destroy()
