from core.ui import DialogUi

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


class BaseInventoryUi(DialogUi):
    SLOT_HEIGHT = 36

    def __init__(self, viewId, area, player, owner, tooltip):
        DialogUi.__init__(self, viewId, area)
        self.player = player
        self.owner = owner
        self.ownerId = owner.id
        self.inventory = owner.inventory
        self.inventory.onChange.listen(self.onItemChanged)
        #Register UI callbacks
        self.view.register('populate', self.onPopulate)
        self.view.register('showTooltip', tooltip.onShow)
        self.view.register('clickedItem', self.onClickedItem)
        self.view.register('splitItem', self.onSplitItem)

    def populate(self, title, bags):
        #Go through the entire bag and populate the inventory
        commands = []
        for index, bag in enumerate(bags):
            size = len(bag.items) if bag else 0
            icon = self.getBagIcon(bag)
            commands.append("var bag = App.inventory.addBag({}, {}, '{}');".format(index, size, icon))
            if not bag:
                continue
            #Next go through and update all of the items in the bag
            for index, item in enumerate(bag.items):
                commands.extend(self.updateItem(bag, index))
        #Set the title
        commands.append("$('#handles .tab-title').text('{}');".format(Locale.getEscaped(title)))
        return commands

    def updateItem(self, bag, index):
        item = bag.items[index]
        #Handle an itemslot changing (either item or just quantity)
        commands = []
        #Construct the javascript code that we'll execute in UI context
        commands.append("var tempItem = App.inventory.bags.content[{}].items.content[{}];".format(bag.index, index))
        commands.append("tempItem.beginPropertyChanges();")
        commands.append("tempItem.set('quantity', {});".format(item.quantity))
        commands.append("tempItem.set('index', {});".format(index))
        if item:
            commands.append("tempItem.set('id', {});".format(item.entity.id))
            commands.append("tempItem.set('content', {});".format(item.content.id))
            commands.append("tempItem.set('icon', 'asset://crea/{}');".format(item.entity.render.getIconPath()))
        else:
            commands.append("tempItem.set('id', 0);")
            commands.append("tempItem.set('content', 0);")
            commands.append("tempItem.set('icon', '');")
        commands.append("tempItem.endPropertyChanges();")
        return commands

    def handleClickedItem(self, bag, index):
        item = bag.get(index)
        #Handle when the player clicks on an item
        if Keyboard.isKeyPressed(Keyboard.LControl):
            #Attempt to equip the item
            successful = False
            if item.entity and item.entity.has('equipment'):
                if self.player.canEquip(item.entity.equipment.getSlot(), item):
                    game.audio.play("mods/core/audio/sfx/ui/equip.ogg")
                    request = DataStream(Message.ITEM_INVENTORY_EQUIP)
                    request.writeInt32(self.ownerId)
                    request.writeInt32(bag.index)
                    request.writeInt32(index)
                    game.network.getClient().send(request)
                    successful = True
            if not successful:
                #TODO: Provide some feedback to notify the player that they cannot equip the item
                pass
        else:
            #Send a request to the server to swap the item in the bag with the grabbed item
            if item or self.player.grabbed:
                self.sendItemGrabRequest(bag, index)

    def handleSplitItem(self, bag, index):
        item = bag.get(index)
        if item and (not self.player.grabbed or item.content == self.player.grabbed.item.content):
            game.audio.play("mods/core/audio/sfx/ui/split.ogg")
            packet = DataStream(Message.ITEM_SPLIT)
            packet.writeUint8(GrabbedItemLocation.BAG)
            packet.writeUint32(self.ownerId)
            packet.writeUint32(bag.index)
            packet.writeUint32(index)
            game.network.getClient().send(packet)

    def sendItemGrabRequest(self, bag, index):
        game.audio.play("mods/core/audio/sfx/ui/grab1.ogg")
        packet = DataStream(Message.ITEM_SWAP_WITH_GRAB)
        packet.writeUint8(GrabbedItemLocation.BAG)
        packet.writeUint32(self.ownerId)
        packet.writeUint32(bag.index)
        packet.writeInt32(index)
        game.network.getClient().send(packet)

    def getBagIcon(self, bag):
        return "asset://crea/{}".format(bag.entity.render.getIconPath()) if bag else ''


class InventoryUi(BaseInventoryUi):
    def __init__(self, player, tooltip):
        area = PixelRect(750, 20, 250, 350)
        BaseInventoryUi.__init__(self, 'inventory', area, player, player.entity, tooltip)
        self.selectedBag = None
        self.newItems = {}

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

        #Hook up callback to listen for item changes on the bag
        self.view.register('changeBag', self.onBagChange)
        self.view.register('clickedBag', self.onBagClicked)
        self.inventory.onBagChange.listen(self.handleBagChange)
        self.inventory.onCapacityChange.listen(self.handleCapacityChange)

    def show(self, focus=True):
        DialogUi.show(self, focus)
        if self.newItems:
            commands = []
            bagIndex = self.selectedBag.index
            commands.append("var bag = App.inventory.bags.content[{}];".format(bagIndex))
            for index in self.newItems[bagIndex]:
                commands.append("bag.items.content[{}].flash();".format(index))
            self.view.execute(commands)
            self.newItems[bagIndex] = []

    def onPopulate(self, view, args):
        bags = self.inventory.getBags()
        self.selectedBag = bags[0]
        view.execute(self.populate("InventoryTitle", bags))
        for index, bag in enumerate(bags):
            self.newItems[index] = []

    def onClickedItem(self, view, args):
        index = args[1].toInteger()
        self.handleClickedItem(self.selectedBag, index)

    def onSplitItem(self, view, args):
        index = args[1].toInteger()
        self.handleSplitItem(self.selectedBag, index)

    def onBagChange(self, view, args):
        index = args[1].toInteger()
        self.selectedBag = self.inventory.getBag(index)
        Log.info("Changed bag to index: {}".format(index))

    def onBagClicked(self, view, args):
        index = args[1].toInteger()
        grabbed = self.player.grabbed
        inventory = self.inventory
        if inventory.exists(index):
            bag = inventory.getBag(index)
            if grabbed:
                # Player is holding an item so drop the item into the bag
                if bag.canAdd(grabbed.item.entity):
                    self.sendItemGrabRequest(bag, -1)
            elif bag.isEmpty() and inventory.getBagCount() > 1:
                # If the bag is empty and not the last bag then let the player pick it up
                self.sendBagGrabRequest(index)
        elif grabbed and grabbed.item.entity.has('bag') and grabbed.item.entity.bag.canCarry:
            # Allow the player to drop a bag into an empty bag slot
            self.sendBagGrabRequest(index)

    def sendBagGrabRequest(self, index):
        #Send a request to the server to swap this bag with grabbed
        packet = DataStream(Message.ITEM_SWAP_WITH_GRAB)
        packet.writeUint8(GrabbedItemLocation.BAGSLOT)
        packet.writeUint32(index)
        game.network.getClient().send(packet)

    def onItemChanged(self, inventory, bagIndex, index, previous, item):
        commands = self.updateItem(self.inventory.getBag(bagIndex), index)
        if item and (previous.getContentId() != item.content.id or previous.quantity < item.quantity) and self.player.entity.id == self.ownerId and \
                (self.player.grabbed.elapsed != 0 or (self.player.grabbed.bagIndex != bagIndex and self.player.grabbed.index != index)):
            if self.view.isVisible() and bagIndex == self.selectedBag.index:
                commands.append("App.inventory.bags.content[{}].items.content[{}].flash();".format(bagIndex, index))
            else:
                if bagIndex not in self.newItems:
                    self.newItems[bagIndex] = []
                self.newItems[bagIndex].append(index)
        self.view.execute(commands)

    def handleBagChange(self, index, bag):
        size = bag.size() if bag else 0
        icon = self.getBagIcon(bag)
        self.view.execute("App.inventory.updateBag({}, {}, '{}', true)".format(index, size, icon))

    def handleCapacityChange(self, previous, current):
        #Convert sizes to indices
        previous -= 1
        current -= 1
        if previous < current:
            #Increased inventory bag capacity. Create more bag objects for the UI
            for index in xrange(previous, current):
                self.view.execute("App.inventory.addBag();".format(index, 0, self.getBagIcon(None)))
        else:
            #Decreased inventory bag capacity
            self.view.execute("App.inventory.bags.removeAt({}, {});".format(current, previous - current))


class TreasureUi(BaseInventoryUi):
    def __init__(self, player, owner, bagIndex, tooltip):
        area = PixelRect(525, 20, 240, 250)
        BaseInventoryUi.__init__(self, 'treasure', area, player, owner, tooltip)
        self.bag = self.inventory.getBag(bagIndex)
        self.load('html/treasure.html')
        self.sendHideInteraction = True
        self.isDestroyed = False
        self.view.register('loot', self.onLoot)
        self.view.onHide.listen(self.handleHide)
        self.owner.onDestroyed.listen(self.onDestroyed)

    def onPopulate(self, view, args):
        view.execute(self.populate("TreasureTitle", [self.bag]))

    def repopulate(self, owner, bagIndex):
        # Unregister listeners for previous owner
        if self.inventory is not None:
            self.inventory.onChange.remove(self.onItemChanged)
        if self.owner is not None:
            self.owner.onDestroyed.remove(self.onDestroyed)
        self.owner = owner
        self.ownerId = owner.id
        self.inventory = owner.inventory
        self.inventory.onChange.listen(self.onItemChanged)
        self.owner.onDestroyed.listen(self.onDestroyed)
        self.bag = self.inventory.getBag(bagIndex)
        self.sendHideInteraction = True
        self.isDestroyed = False
        commands = ["App.set('inventory', App.TreasureInventory.create());"]
        commands.extend(self.populate("TreasureTitle", [self.bag]))
        self.view.execute(commands)
        self.view.waitForRefresh()

    def onClickedItem(self, view, args):
        #TODO: How about this not being the player's inventory and the player clicks on an item
        #      If the inventory is not locked then allow grab.
        #TODO: Also handle click plus shift to move the item to the player's inventory
        #      This may work with control + click instead (just like equip but for normal items?)
        index = args[1].toInteger()
        self.handleClickedItem(self.bag, index)

    def onSplitItem(self, view, args):
        index = args[1].toInteger()
        self.handleSplitItem(self.bag, index)

    def onLoot(self, view, args):
        self.sendItemGrabRequest(self.bag, -2)

    def onDestroyed(self, entity, unload):
        self.isDestroyed = True
        self.owner = None
        self.inventory = None

    def destroy(self):
        if self.bag:
            self.bag.onChange.remove(self.onItemChanged)
        if self.owner:
            self.owner.onDestroyed.remove(self.onDestroyed)
        self.hide()

    def handleHide(self):
        if self.sendHideInteraction:
            self.sendHide()
        self.sendHideInteraction = False

    def sendHide(self):
        packet = DataStream(Message.INVENTORY_CLOSE)
        packet.writeUint32(self.ownerId)
        packet.writeUint32(self.bag.index)
        game.network.getClient().send(packet)

    def onItemChanged(self, inventory, bagIndex, index, previous, item):
        self.view.execute(self.updateItem(self.bag, index))
