from core.helper import enum
from core.talent import chanceToGainTalentPoints

from siege import game, InventoryItem
from siege.component import ModularSprite
from siege.graphic import Color, PixelCheck
from siege.particle import PhysicsParticleEmitter
from siege.util import minutes, Random, RangeColor, RangeInt, RangeVector, Rect, seconds, Sizes, Vector
from siege.world.realm import Layer


class TreeImage:
    def __init__(self, path, center, height):
        self.path = path
        self.center = center
        self.height = height

TreePart = enum(BASE=1, TRUNK=2, CROWN=3, SAPLING=4)


class TreeSprite:
    def __init__(self, part, index):
        self.index = index
        self.part = part
        self.modular = ModularSprite()


class Tree(object):
    bases = []
    trunks = []
    crowns = []
    saplings = []
    #Path to the seed content script
    seed = ""
    #The range of the number of seeds that will drop from the crown
    seedRange = (1, 3)
    wood = "wood"
    growthTimeMin = minutes(4)
    growthTimeMax = minutes(10)
    #How much time it takes to recover from a hit
    recoveryTime = seconds(10)
    #How much life each part of the tree has - how many hits it can take
    partLife = 200
    trunkRange = (2, 6)
    level = 1
    particleColor = Color(146, 101, 70)
    dropsFeather = True

    def __init__(self, entity, organic):
        self.render = entity.render
        self.organic = organic
        spriteId = 1
        for image in self.bases + self.trunks + self.crowns + self.saplings:
            self.render.addTexture(spriteId, image.path)
            spriteId += 1

        if not game.network.isHost() or entity.isContentEntity():
            return

        entity.event["harvest"].listen(self.onHarvest)
        entity.event["can_remove_support"].listen(self.onCanRemoveSupport)
        entity.event["remove_support"].listen(self.onRemoveSupport)
        self.addGrowthTimer()

        #Start it off as a sapling
        self.stage = 0
        self.maxStage = Random.get(self.trunkRange[0], self.trunkRange[1])
        self.sprites = [TreeSprite(TreePart.SAPLING, Random.get(len(self.saplings) - 1))]
        self.construct()

    def addGrowthTimer(self):
        self.growJobId = game.timer.add(Random.get(self.growthTimeMin, self.growthTimeMax), self.grow)

    def canGrow(self):
        return True

    def grow(self):
        if self.stage == -1 or self.stage > self.maxStage or not self.render.getParent():
            return
        previousStage = list(self.sprites)
        if self.stage is 1:
            #Clear out the sapling and append the tree base and crown
            self.sprites = []
            if self.bases:
                self.sprites.append(TreeSprite(TreePart.BASE, Random.get(len(self.bases) - 1)))
            if self.crowns:
                self.sprites.append(TreeSprite(TreePart.CROWN, Random.get(len(self.crowns) - 1)))
        if self.stage >= 1:
            if self.trunks:
                trunk = TreeSprite(TreePart.TRUNK, Random.get(len(self.trunks) - 1))
                if self.crowns:
                    self.sprites.insert(-1, trunk)
                else:
                    self.sprites.append(trunk)
        self.construct()
        #TODO: Check if there is enough space for the tree to grow
        area = self.render.getRect()
        area.height -= 4
        realm = self.render.getParent().realm
        tiles = realm.layers[Layer.Ground].overlaps(area, True, False)
        if tiles:
            for tile in tiles:
                tilePos = tile.getWorld()
                tilePos.x *= Sizes.TILE
                tilePos.y *= Sizes.TILE
                tileRect = Rect(tilePos.x, tilePos.y, Sizes.TILE, Sizes.TILE)
                if PixelCheck.contains(self.render, tileRect, realm.size.width):
                    self.sprites = previousStage
                    self.construct()
                    self.addGrowthTimer()
                    return
        self.stage += 1
        if self.stage < self.maxStage:
            self.addGrowthTimer()

    def getImage(self, sprite):
        if sprite.part is TreePart.BASE:
            return self.bases[sprite.index]
        elif sprite.part is TreePart.TRUNK:
            return self.trunks[sprite.index]
        elif sprite.part is TreePart.CROWN:
            return self.crowns[sprite.index]
        elif sprite.part is TreePart.SAPLING:
            return self.saplings[sprite.index]
        else:
            raise ValueError("TreeSprite contains unknown tree part: ", sprite.part)

    def construct(self):
        height = 1
        sprites = []
        for index, sprite in enumerate(self.sprites):
            sprite.modular.id = index
            image = self.getImage(sprite)
            height -= image.height
            sprite.modular.position.x = -image.center
            sprite.modular.position.y = height
            self.organic.setImage(image.path, sprite.modular)
            sprites.append(sprite.modular)
        self.organic.onConstruct(sprites)

    def canBeHarvested(self, player, entity, tool, power, position):
        return tool.isItem()

    def onHarvest(self, player, entity, tool, power, position):
        if not self.canBeHarvested(player, entity, tool, power, position):
            return
        sprite = entity.modularRender.getTopSprite(position)
        index = 0
        for s in self.sprites:
            if s.modular.id is sprite.modular.id:
                break
            index += 1
        damageId = "%d-%d" % (entity.id, index)
        self.emitWoodChips(position)
        if game.damage(damageId, power, self.partLife, self.recoveryTime):
            game.audio.playAt("mods/core/audio/sfx/wood/treefell1.ogg", position, broadcast=True)
            #ignore if at the top of the tree
            if index == len(self.sprites):
                return
            count = len(self.sprites) - index
            for x in xrange(count):
                chanceToGainTalentPoints(player, player.entity.talents.gather, self.level, rate=25, amount=15)
            self.destroyAtIndex(index)
        else:
            game.audio.playAt("mods/core/audio/sfx/wood/treechop1.ogg", position, broadcast=True)

    def destroyAtIndex(self, index):
        #Create the dropped items in the world
        woodContent = game.content.get(self.wood)
        renderSprites = self.render.getSprites()
        realm = self.render.getParent().realm

        def getSpritePosition(modular):
            for sprite in renderSprites:
                if sprite.modular.id == modular.id:
                    position = sprite.sprite.getPosition()
                    return Vector(position.x, position.y)

        for sprite in self.sprites[index:]:
            image = self.getImage(sprite)
            centerY = image.height / 2
            position = getSpritePosition(sprite.modular)
            position.x += image.center
            position.y += centerY
            if woodContent:
                realm.dropped.create(InventoryItem(woodContent, 1), position, Vector(Random.get(-3, 3), -6))
            if sprite.part is TreePart.CROWN:

                def dropItem(content):
                    worldPos = Vector(position)
                    worldPos.move(Random.get(-8, 8), Random.get(-8, 8))
                    realm.dropped.create(InventoryItem(content, 1), worldPos, Vector(Random.get(-3, 3), -6))
                if self.dropsFeather and Random.get(5) == 0:
                    dropItem(game.content.get('feather'))
                if self.seed:
                    seedContent = game.content.get(self.seed)
                    for x in xrange(0, Random.get(self.seedRange[0], self.seedRange[1])):
                        dropItem(seedContent)

        #remove the targeted sprite and all of the sprites above it
        self.sprites = self.sprites[:index]

        if self.sprites:
            #Reconstruct the graphics for the tree
            self.organic.onConstruct([sprite.modular for sprite in self.sprites])
            #Set the stage to -1 signifying that it cannot grow anymore
            self.stage = -1
        else:
            self.destroy()

    def onCanRemoveSupport(self, args):
        # Do not care about wall tile changes
        if args['layer'].type == Layer.Wall:
            return
        #First check to see if the base (or bottom trunk) is actually over the tile being changed
        sprites = self.render.getSprites()
        if sprites:
            sprite = self.render.getSprites()[0]
            tile = game.tile.getTile(args["currentTileId"])
            if not tile or not tile.isSolid:
                return
            if not PixelCheck.contains(sprite.sprite, sprite.texture, args["vertical"], args['layer'].realmSize.width):
                #No overlap so this is a false alarm
                return
            #Cancel the request
            args["cancel"] = True

    def onRemoveSupport(self, args):
        #First check to see if the base (or bottom trunk) is actually over the tile being changed
        sprites = self.render.getSprites()
        if sprites:
            sprite = self.render.getSprites()[0]
            tile = game.tile.getTile(args["currentTileId"])
            if not tile or not tile.isSolid:
                return
            if not PixelCheck.contains(sprite.sprite, sprite.texture, args["vertical"], args['layer'].realmSize.width):
                #No overlap so this is a false alarm
                return
            if self.stage == 0:
                #Sapling is destroyed on support removal
                self.destroy()
            if args["forced"]:
                #The support is being forcefully removed. Destroy the entire tree
                self.destroyAtIndex(0)

    def emitWoodChips(self, position):
        realm = self.render.getParent().realm
        emitter = PhysicsParticleEmitter(realm.uid, realm.size.width, position, 1, 0)
        emitter.amount = Random.get(4, 6)
        emitter.colorStart = RangeColor(self.particleColor, self.particleColor)
        emitter.colorEnd = emitter.colorStart
        emitter.particleLife = RangeInt(seconds(2), seconds(3))
        emitter.velocityStart = RangeVector(Vector(-4, -3), Vector(4, -4.5))
        emitter.scaleStart = RangeVector(Vector(2, 2), Vector(2, 2))
        emitter.friction = Vector(0.15, 0.0)
        emitter.restitution = Vector(0.2, 0.3)
        game.particles.add(emitter)

    def destroy(self):
        game.timer.cancel(self.growJobId)
        parent = self.organic.getParent()
        if parent:
            parent.destroy()

    def write(self, stream):
        stream.writeInt8(self.stage)
        stream.writeInt8(self.maxStage)
        stream.writeUint8(len(self.sprites))
        for sprite in self.sprites:
            stream.writeUint8(sprite.part)
            stream.writeUint8(sprite.index)

    def read(self, stream):
        self.stage = stream.readInt8()
        self.maxStage = stream.readInt8()
        spriteCount = stream.readUint8()
        self.sprites = []
        for x in xrange(0, spriteCount):
            part = stream.readUint8()
            index = stream.readUint8()
            self.sprites.append(TreeSprite(part, index))
        self.construct()
