from functools import partial

from core.ui import boolStr, DialogUi

from siege import game, Locale, GrabbedItemLocation
from siege.io import DataStream, Keyboard
from siege.network import Message
from siege.util import PixelRect


class EquipmentUi(DialogUi):
    def __init__(self, player, tooltip):
        self.player = player
        windowSize = game.render.getWindowSize()
        width = 380
        height = 370
        x = (windowSize.x / 2) - (width / 2)
        y = (windowSize.y / 2) - (height / 2)
        DialogUi.__init__(self, 'equipment', PixelRect(x, y, width, height))

        #Register UI callbacks
        self.view.register('populate', self.onPopulate)
        self.view.register('clickedSlot', self.onClickedSlot)
        self.view.register('splitSlot', self.onSplitSlot)
        self.view.register('showTooltip', tooltip.onShow)

        self.load('html/equipment.html')

        #Register listeners for stats
        combat = self.player.entity.combat
        combat.level.onChange.listen(self.onLevelChange)
        combat.experience.onChange.listen(self.onExperienceChange)
        component = self.player.entity.stats
        for stat in component.stats:
            stat.onChange.listen(self.onStatChange)
            stat.onChangeMax.listen(self.onStatMaxChange)
        #Register listeners for talents
        for talent in self.player.entity.talents.ordered:
            talent.level.onChange.listen(partial(self.onTalentLevelChange, talent))
            talent.points.onChange.listen(partial(self.onTalentPointsChange, talent))
        #Register listeners for gear
        self.player.entity.gear.onChange.listen(self.onGearChange)

    def onPopulate(self, view, args):
        commands = []
        #Populate character name
        commands.append("App.character.set('name', '{}');".format(self.player.info.name))
        combat = self.player.entity.combat
        commands.append("App.character.clevel.set('level', {});".format(combat.level.get()))
        commands.append("App.character.clevel.points.set('value', '{}');".format(combat.experience.get()))
        commands.append("App.character.clevel.points.set('max', '{}');".format(combat.getExperienceToLevel()))
        #Populate max stats and other stats
        component = self.player.entity.stats
        for stat in component.stats:
            if not stat.isVisible:
                continue
            commands.append("var stat = App.StatBar.create({{name: '{}'}});".format(stat.fullName.upper()))
            if stat.hasMax():
                commands.append("stat.set('value', {});".format(stat.get()))
                commands.append("stat.set('max', {});".format(stat.getMax()))
                commands.append("App.character.maxStats.pushObject(stat);")
            else:
                commands.append("stat.set('value', {});".format(stat.get()))
                commands.append("App.character.stats.pushObject(stat);")
        #Populate talents
        for talent in self.player.entity.talents.ordered:
            commands.append("var talent = App.LevelTracker.create({{name: '{}', level: {} }});".format(talent.name.upper(), talent.level.get()))
            commands.append("talent.points.set('value', {});".format(talent.points.get()))
            commands.append("talent.points.set('max', {});".format(talent.getPointsToLevel()))
            commands.append("App.character.talents.pushObject(talent);")
        #Populate gear slots
        component = self.player.entity.gear
        for slot in component.ordered:
            if not slot.icon:
                #Skip slots that do not have an icon
                continue
            slotText = "{{key: '{}', name: '{}', placeholder: 'asset://crea/{}' }}".format(slot.name, Locale.getEscaped(slot.name), slot.icon)
            commands.append("var slot = App.GearSlot.create({});".format(slotText))
            item = component.get(slot.name).item
            commands.extend(self.getSlotCommands(item))
            commands.append("App.character.slots.pushObject(slot);")
        self.view.execute(commands)

    def onClickedSlot(self, view, args):
        #Check if we can swap the gear slot with the grabbed item (empty grabbed means unequip)
        slot = args[1].toString()
        if Keyboard.isKeyPressed(Keyboard.LControl):
            item = self.player.entity.gear.get(slot).item
            #If there is an item equipped and the player's inventory is not full then request server to unequip item
            if item and not self.player.entity.inventory.isFull():
                request = DataStream(Message.ITEM_UNEQUIP)
                request.writeString(slot)
                game.network.getClient().send(request)
            else:
                #TODO: Provide some feedback to notify the player that they cannot unequip the item
                pass
        elif self.player.canEquip(slot, self.player.grabbed.item):
            if self.player.grabbed.item:
                game.audio.play("mods/core/audio/sfx/ui/equip.ogg")
            else:
                game.audio.play("mods/core/audio/sfx/ui/unequip.ogg")
            #Send a request to the server to swap items with the gear slot
            request = DataStream(Message.ITEM_SWAP_WITH_GRAB)
            request.writeUint8(GrabbedItemLocation.GEAR)
            request.writeUint32(self.player.entity.id)
            request.writeString(slot)
            game.network.getClient().send(request)

    def onSplitSlot(self, view, args):
        slot = args[1].toString()
        item = self.player.entity.gear.get(slot).item
        if item:
            game.audio.play("mods/core/audio/sfx/ui/unequip.ogg")
            request = DataStream(Message.ITEM_SPLIT)
            request.writeUint8(GrabbedItemLocation.GEAR)
            request.writeUint32(self.player.entity.id)
            request.writeString(slot)
            game.network.getClient().send(request)

    def onLevelChange(self, entity, previous, current):
        commands = []
        commands.append("App.character.clevel.set('level', {});".format(current))
        commands.append("App.character.clevel.points.set('max', {});".format(self.player.entity.combat.getExperienceToLevel()))
        self.view.execute(commands)

    def onExperienceChange(self, entity, previous, current):
        self.view.execute("App.character.clevel.points.set('value', {});".format(current))

    def onStatChange(self, stat, oldValue):
        commands = []
        maxPrefix = "maxS" if stat.hasMax() else "s"
        commands.append("var stat = App.character.{}tats.findProperty('name', '{}');".format(maxPrefix, stat.fullName.upper()))
        commands.append("stat.set('value', {});".format(stat.get()))
        self.view.execute(commands)

    def onStatMaxChange(self, stat, oldValue):
        commands = []
        commands.append("var stat = App.character.maxStats.findProperty('name', '{}');".format(stat.fullName.upper()))
        commands.append("stat.set('max', {});".format(stat.getMax()))
        self.view.execute(commands)

    def onTalentLevelChange(self, talent, previous, current):
        commands = []
        commands.append("var talent = App.character.talents.findProperty('name', '{}');".format(talent.name.upper()))
        commands.append("talent.set('level', {});".format(current))
        commands.append("talent.points.set('max', {});".format(talent.getPointsToLevel()))
        self.view.execute(commands)

    def onTalentPointsChange(self, talent, previous, current):
        commands = []
        commands.append("var talent = App.character.talents.findProperty('name', '{}');".format(talent.name.upper()))
        commands.append("talent.points.set('value', {});".format(current))
        self.view.execute(commands)

    def onGearChange(self, gearSlot, item):
        # Skip gear slots that do not show in the UI (such as 'active1' and 'active2')
        if not gearSlot.icon:
            return
        commands = []
        commands.append("var slot = App.character.slots.findProperty('key', '{}');".format(gearSlot.name))
        commands.extend(self.getSlotCommands(item))
        self.view.execute(commands)

    def getSlotCommands(self, item):
        commands = []
        commands.append("slot.beginPropertyChanges();")
        if item:
            commands.append("slot.set('id', {});".format(item.entity.id))
            commands.append("slot.set('content', {});".format(item.content.id))
            commands.append("slot.set('icon', 'asset://crea/{}');".format(item.entity.render.getIconPath()))
            commands.append("slot.set('quantity', {});".format(item.quantity))
            commands.append("slot.set('isUnique', {});".format(boolStr(item.entity.item.isUnique)))
        else:
            commands.append("slot.set('id', 0);")
            commands.append("slot.set('content', 0);")
            commands.append("slot.set('icon', '');")
            commands.append("slot.set('quantity', 0);")
            commands.append("slot.set('isUnique', false);")
        commands.append("slot.endPropertyChanges();")
        return commands
