NpcState = enum("Idle", "Interact", "Roam", "Move", "Death")


class BasicNpcStates(object):
    @staticmethod
    def initializeIdle(npc):
        npc.targetTime = npc.idleTimes.getRandom()
        npc.idle(npc.entity.animation)
        npc.entity.physics.setVelocity(Vector(0, 0))

    @staticmethod
    def idle(npc, frameTime):
        if npc.stateTime >= npc.targetTime:
            npc.changeState(NpcState.Roam)

    @staticmethod
    def initializeRoam(npc):
        npc.direction.x = 1 if Random.getBool() else -1
        npc.targetTime = npc.roamTimes.getRandom()

    @staticmethod
    def roam(idleChance, movement, npc, frameTime):
        if npc.stateTime >= npc.targetTime and npc.isTouchingGround and not npc.entity.animation.isPlaying("fall"):
            #State time has expired. Either start roaming again or possibly idle for a moment
            if Random.getFloat() <= idleChance:
                npc.changeState(NpcState.Idle)
            else:
                npc.changeState(NpcState.Roam)
        else:
            movement(npc, npc.roamSpeed)


class BaseNpc(Sentient):
    defaultInitializers = {
            NpcState.Idle: BasicNpcStates.initializeIdle, \
            NpcState.Interact: BasicNpcStates.initializeIdle, \
            NpcState.Roam: BasicNpcStates.initializeRoam, \
        }
    defaultHandlers = {
            NpcState.Idle: BasicNpcStates.idle, \
            NpcState.Interact: BasicNpcStates.idle, \
            NpcState.Roam: partial(BasicNpcStates.roam, 0.3, MovementHelpers.walkJump), \
        }

    body = 'human_male'
    outfit = []
    names = []

    def __init__(self, entity, initializers={}, handlers={}):
        # Add in components from body and then finally body component itself
        bodyEntity = game.content.get(self.body).entity
        if not bodyEntity:
            # Could not find body which most certainly means content entities have not been populate yet (world not loaded)
            return
        for component in bodyEntity.body.components:
            if component.getType() not in ['gear', 'talents']:
                game.entity.addComponent(entity, component)
        entity.add(bodyEntity.body)

        super(BaseNpc, self).__init__(entity, NpcState.Idle, initializers, handlers)

        # Apply sprite substitutions to give the NPC the correct outfit
        for piece in self.outfit:
            entity.render.changeSprite(piece)

        # Pick a name for the NPC
        names = list(self.names)
        npcs = game.npc.get(entity.content.id)
        while names:
            name = select(names)
            for npc in npcs:
                if npc.name is name:
                    continue
            break
        entity.setName(name)

        # Misc. setup
        self.interactCount = 0
        entity.event['interact'].listen(self.interact)
        #Entity id of home crystal
        self.home = 0

    def update(self, frameTime):
        super(BaseNpc, self).update(frameTime)

    def updateInteraction(self, player):
        pass

    def changeState(self, state):
        super(BaseNpc, self).changeState(state)

    def interact(self, player, entity, position):
        success = game.npc.interact(player, entity)
        if not success:
            return False
        if self.state is not NpcState.Interact:
            self.changeState(NpcState.Interact)
        self.interactCount += 1
        packet = DataStream(Message.NPC_INTERACT)
        packet.writeUint32(self.entity.id)
        self.packDialog(player, position, packet)
        game.network.getServer().send(player.networkId, packet)
        return True

    def dismiss(self, player):
        self.interactCount -= 1
        if self.interactCount == 0:
            self.changeState(NpcState.Idle)

    def packDialog(self, player, position, packet):
        pass

    def showDialog(self, gameplay, player, packet):
        pass

    def setHome(self, entity):
        self.home = entity.id

    def write(self, stream):
        stream.writeString(self.entity.name)
        stream.writeUint32(self.home)

    def read(self, stream):
        self.entity.setName(stream.readString())
        self.home = stream.readUint32()

    def idle(self, animation):
        animation.play("idle")
        animation.play("idle_arm_back", "arm_back")
        animation.play("idle_arm_front", "arm_front")

    def move(self, animation):
        animation.play("run")
        animation.play("run_arm_back", "arm_back")
        animation.play("run_arm_front", "arm_front")

    def jump(self, animation):
        animation.play("jump")
        animation.play("jump_arm_back", "arm_back")
        animation.play("jump_arm_front", "arm_front")

    def fall(self, animation):
        animation.play("fall")
        animation.play("fall_arm_back", "arm_back")
        animation.play("fall_arm_front", "arm_front")
