class InventoryGui(DirectObject):
    directNotify = DirectNotify().newCategory('InventoryGui')
    HiddenPos = (0.2, 0, 0)
    VisiblePos = (-0.1725, 0, 0)
    SwitchTime = 0.3
    AutoShowTime = 1.5
    DELETED = False

    def __init__(self):
        DirectObject.__init__(self)
        self.backpack = base.localAvatar.backpack
        if not self.backpack:
            return
        self.backpack.loadoutGUI = self
        self.oneSlotPos = [(0, 0, 0)]
        self.twoSlotsPos = [(0, 0, 0.3), (0, 0, -0.2)]
        self.threeSlotsPos = [(0, 0, 0.5), (0, 0, 0), (0, 0, -0.5)]
        self.fourSlotPos = [(0, 0, 0.5), (0, 0, 0.15), (0, 0, -0.2),
                            (0, 0, -0.55)]
        self.availableSlot = 0
        self.slots = []
        self.activeSlot = None
        self.defaultSlots = 3
        self.prevSlot = None
        self.ammoLabel = None
        self.inventoryFrame = DirectFrame(parent=base.a2dRightCenter,
                                          pos=(-0.1725, 0, 0))
        self.visibilityBtn = DirectButton(text='',
                                          relief=None,
                                          text_bg=(1, 1, 1, 0),
                                          parent=base.a2dRightCenter,
                                          pos=(-0.1725, 0, 0),
                                          frameSize=(-0.2, 0.2, -0.725, 0.7),
                                          clickSound=None,
                                          rolloverSound=None)
        self.visibilityBtn.bind(DGG.WITHIN, self.__handleVisEnter)
        self.visibilityBtn.bind(DGG.WITHOUT, self.__handleVisExit)
        self.visibilityBtn.setBin('background', 10)
        self.visibilityBtn = None
        self.visibilityBtnStatus = 0
        self.switchSound = True
        self.switchSoundSfx = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.visibilityFSM = ClassicFSM('InventoryGui-VisibilityFSM', [
            State('off', self.enterOff, self.exitOff),
            State('hidden', self.enterHidden, self.exitHidden),
            State('hidden2visible', self.enterHidden2Visible,
                  self.exitHidden2Visible),
            State('visible', self.enterVisible, self.exitVisible),
            State('visible2hidden', self.enterVisible2Hidden,
                  self.exitVisible2Hidden)
        ], 'off', 'off')
        self.visibilityFSM.enterInitialState()
        self.visibilityFSM.request('hidden')
        return

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterHidden(self):
        self.inventoryFrame.setPos(InventoryGui.HiddenPos)
        self.inventoryFrame.hide()

    def exitHidden(self):
        pass

    def enterVisible(self, autoShow=False):
        self.inventoryFrame.setPos(InventoryGui.VisiblePos)
        self.inventoryFrame.show()
        if self.visibilityBtnStatus == 0:
            if autoShow is False:
                self.visibilityFSM.request('visible2hidden')

    def exitVisible(self):
        pass

    def enterHidden2Visible(self, autoShow=False):
        self.inventoryFrame.show()
        self.moveIval = LerpPosInterval(self.inventoryFrame,
                                        duration=InventoryGui.SwitchTime,
                                        pos=InventoryGui.VisiblePos,
                                        startPos=InventoryGui.HiddenPos)
        self.moveIval.setDoneEvent('hidden2visible')
        self.acceptOnce('hidden2visible', self.visibilityFSM.request,
                        ['visible', [autoShow]])
        self.moveIval.start()

    def exitHidden2Visible(self):
        self.ignore('hidden2visible')
        self.moveIval.finish()
        del self.moveIval

    def enterVisible2Hidden(self):
        self.moveIval = LerpPosInterval(self.inventoryFrame,
                                        duration=InventoryGui.SwitchTime,
                                        pos=InventoryGui.HiddenPos,
                                        startPos=InventoryGui.VisiblePos)
        self.moveIval.setDoneEvent('visible2hidden')
        self.acceptOnce('visible2hidden', self.visibilityFSM.request,
                        ['hidden'])
        self.moveIval.start()

    def exitVisible2Hidden(self):
        self.ignore('visible2hidden')
        self.moveIval.finish()
        del self.moveIval

    def click_setWeapon(self, slot, cmd):
        self.setWeapon(slot, playSound=False)

    def setWeapon(self, slot, playSound=True, showUpIfHidden=False):
        if isinstance(slot, str):
            for iSlot in self.slots:
                if iSlot.getGag():
                    if iSlot.getGag().getID() == slot:
                        slot = iSlot

        if self.activeSlot and slot != self.activeSlot:
            self.activeSlot.setOutlineImage('idle')
            self.prevSlot = self.activeSlot
        if slot.getGag() and self.backpack.getSupply(slot.getGag().getID(
        )) > 0 and not slot.getGag().getState() == GagState.RECHARGING:
            if self.activeSlot != slot:
                gagId = slot.getGag().getID()
                base.localAvatar.needsToSwitchToGag = gagId
                if base.localAvatar.gagsTimedOut == False:
                    base.localAvatar.b_equip(gagId)
                    base.localAvatar.enableGagKeys()
                slot.setOutlineImage('selected')
                self.activeSlot = slot
            else:
                if self.activeSlot == slot and slot.getGag().getState() in [
                        GagState.LOADED, GagState.RECHARGING
                ]:
                    base.localAvatar.needsToSwitchToGag = 'unequip'
                    if base.localAvatar.gagsTimedOut == False:
                        base.localAvatar.b_unEquip()
                        base.localAvatar.enableGagKeys()
                    self.activeSlot = None
            self.update()
            if self.switchSound and playSound:
                base.playSfx(self.switchSoundSfx)
        if showUpIfHidden:
            base.taskMgr.remove('showUpIfHidden')
            self.__autoVisEnter()
            base.taskMgr.doMethodLater(InventoryGui.AutoShowTime,
                                       self.__autoVisExitTask,
                                       'showUpIfHidden')
        return

    def __autoVisExitTask(self, task):
        if self.visibilityBtnStatus == 0:
            self.__handleVisExit(None, updateBtnStatus=False)
        return task.done

    def __autoVisEnter(self):
        self.__handleVisEnter(None, True, False)
        return

    def __handleVisEnter(self, foo, autoShow=False, updateBtnStatus=True):
        if updateBtnStatus:
            self.visibilityBtnStatus = 1
        if self.visibilityFSM.getCurrentState().getName() == 'hidden':
            self.visibilityFSM.request('hidden2visible', [autoShow])
        else:
            if self.visibilityFSM.getCurrentState().getName(
            ) == 'visible2hidden':
                self.visibilityFSM.request('visible')

    def __handleVisExit(self, foo, updateBtnStatus=True):
        if updateBtnStatus:
            self.visibilityBtnStatus = 0
        base.taskMgr.remove('showUpIfHidden')
        if self.visibilityFSM.getCurrentState().getName() == 'visible':
            self.visibilityFSM.request('visible2hidden')

    def createGui(self):
        self.deleteGui()
        posGroup = self.threeSlotsPos
        if self.defaultSlots == 4:
            posGroup = self.fourSlotPos
        for slot in range(len(posGroup) + 1):
            if slot == 3:
                posGroup = self.fourSlotPos
            slotObj = Slot(self, slot + 1, posGroup[slot], self.inventoryFrame)
            self.slots.append(slotObj)
            if slot == 3:
                slotObj.hide()

        self.ammoLabel = DirectLabel(text='Ammo: 0',
                                     text_fg=(1, 1, 1, 1),
                                     relief=None,
                                     text_shadow=(0, 0, 0, 1),
                                     text_scale=0.08,
                                     pos=(0.2, 0, 0.35),
                                     parent=base.a2dBottomLeft)
        self.ammoLabel.hide()
        self.enableWeaponSwitch()
        self.resetScroll()
        self.update()
        return

    def deleteGui(self):
        self.disableWeaponSwitch()
        for slot in self.slots:
            slot.destroy()

        self.slots = []
        if self.ammoLabel:
            self.ammoLabel.destroy()
            self.ammoLabel = None
        self.DELETED = True
        return

    def resetScroll(self):
        nextGag = 0
        prevGag = -1
        curGag = -1
        if self.prevSlot:
            prevGag = self.slots.index(self.prevSlot)
        if self.activeSlot:
            curGag = self.slots.index(self.activeSlot)
        if curGag == len(self.slots) - 1:
            nextGag = 0
            prevGag = curGag - 1
        else:
            if curGag == 0:
                nextGag = 1
                prevGag = len(self.slots) - 1
            else:
                if curGag == -1:
                    prevGag = len(self.slots) - 1
                else:
                    nextGag = curGag + 1
                    prevGag = curGag - 1
        self.accept('wheel_down',
                    self.setWeapon,
                    extraArgs=[self.slots[nextGag], True, True])
        self.accept('wheel_up',
                    self.setWeapon,
                    extraArgs=[self.slots[prevGag], True, True])

    def update(self):
        if not self.backpack:
            return
        for element in [self.ammoLabel, self.inventoryFrame]:
            if not element:
                return

        updateSlots = list(self.slots)
        for slot in self.slots:
            gag = slot.getGag()
            if not gag:
                updateSlots.remove(slot)
                slot.hide()
                continue
            supply = self.backpack.getSupply(gag.getID())
            index = self.slots.index(slot)
            if not gag and len(self.backpack.getGags()) - 1 >= index:
                gag = self.backpack.getGagByIndex(index)
                slot.setGag(gag)
                if self.backpack.getSupply(gag.getID(
                )) > 0 and not gag.getState() == GagState.RECHARGING:
                    slot.setOutlineImage('idle')
                else:
                    slot.setOutlineImage('no_ammo')
            elif slot == self.activeSlot:
                self.ammoLabel['text_fg'] = (1, 1, 1, 1)
                if supply > 0 and not gag.getState() == GagState.RECHARGING:
                    slot.setOutlineImage('selected')
                else:
                    if supply <= 0:
                        self.ammoLabel['text_fg'] = (0.9, 0, 0, 1)
                    slot.setOutlineImage('no_ammo')
                    self.activeSlot = None
                self.ammoLabel.show()
                self.ammoLabel['text'] = 'Ammo: %s' % self.backpack.getSupply(
                    slot.getGag().getID())
            elif self.backpack.getSupply(slot.getGag().getID(
            )) > 0 and not gag.getState() == GagState.RECHARGING:
                slot.setOutlineImage('idle')
            else:
                slot.setOutlineImage('no_ammo')

        numSlots = len(updateSlots)
        posGroup = {
            1: self.oneSlotPos,
            2: self.twoSlotsPos,
            3: self.threeSlotsPos,
            4: self.fourSlotPos
        }.get(numSlots)
        for i in xrange(len(updateSlots)):
            updateSlots[i].setPos(posGroup[i])
            updateSlots[i].show()

        if self.activeSlot == None:
            self.ammoLabel.hide()
            self.ammoLabel['text'] = 'Ammo: 0'
        self.resetScroll()
        return

    def setBackpack(self, backpack):
        self.backpack = backpack

    def updateLoadout(self):
        if self.backpack:
            loadout = self.backpack.getLoadout()
            if len(loadout) <= 3:
                self.reseatSlots()
            else:
                if len(loadout) == 4:
                    self.reseatSlots(slots=4)
            for i in range(len(self.slots)):
                slot = self.slots[i]
                if i < len(loadout):
                    slot.setGag(loadout[i])
                else:
                    slot.setGag(None)

            self.update()
        return

    def reseatSlots(self, slots=3):
        for slot in range(len(self.slots) - 1):
            if slots == 4:
                self.slots[slot].setPos(self.fourSlotPos[slot])
            else:
                self.slots[slot].setPos(self.threeSlotsPos[slot])

    def enableWeaponSwitch(self):
        for index in range(len(self.slots)):
            self.accept(str(index + 1),
                        self.setWeapon,
                        extraArgs=[self.slots[index], True, True])

    def disableWeaponSwitch(self):
        for key in ['1', '2', '3', '4', 'wheel_down', 'wheel_up']:
            self.ignore(key)

    def getSlots(self):
        return self.slots

    def getActiveSlot(self):
        return self.activeSlot

    def isDeleted(self):
        return self.DELETED