# Fuck you Disyer. Stealing my fucking paypal. GET FUCKED: toontown.hood.InteractiveAnimatedProp
from panda3d.core import Filename, TextNode, Vec3
from direct.actor import Actor
from direct.fsm import FSM
from direct.interval.IntervalGlobal import Sequence, ActorInterval, Wait, Func, SoundInterval, Parallel
from direct.showbase.PythonUtil import weightedChoice
from toontown.toonbase import ToontownGlobals
import GenericAnimatedProp
import math, random

def clearPythonIvals(ival):
    if hasattr(ival, 'function'):
        ival.function = None
    if hasattr(ival, 'pythonIvals'):
        for oneIval in ival.pythonIvals:
            clearPythonIvals(oneIval)

        ival.pythonIvals = []
    return


class InteractiveAnimatedProp(GenericAnimatedProp.GenericAnimatedProp, FSM.FSM):
    ZoneToIdles = {}
    ZoneToIdleIntoFightAnims = {}
    ZoneToFightAnims = {}
    ZoneToVictoryAnims = {}
    ZoneToSadAnims = {}
    IdlePauseTime = config.GetFloat('prop-idle-pause-time', 0.0)
    HpTextGenerator = TextNode('HpTextGenerator')
    BattleCheerText = '+'

    def __init__(self, node):
        FSM.FSM.__init__(self, 'InteractiveProp-%s' % str(node))
        self.numIdles = 0
        self.numFightAnims = 0
        self.idleInterval = None
        self.battleCheerInterval = None
        self.sadInterval = None
        self.victoryInterval = None
        self.lastIdleAnimName = ''
        self.lastIdleTime = 0
        self.curIval = None
        self.okToStartNextAnim = False
        cellIndexStr = node.getTag('DNACellIndex')
        self.cellIndex = ord(cellIndexStr)
        self.origAnimNameToSound = {}
        self.lastPlayingAnimPhase = 0
        self.buildingsMakingMeSad = set()
        GenericAnimatedProp.GenericAnimatedProp.__init__(self, node)
        return

    def delete(self):
        self.exit()
        GenericAnimatedProp.GenericAnimatedProp.delete(self)
        self.idleInterval = None
        self.battleCheerInterval = None
        self.sadInterval = None
        self.victoryInterval = None
        return

    def getCellIndex(self):
        return self.cellIndex

    def playBattleCheerAnim(self):
        self.node.loop('battleCheer')

    def setupActor(self, node):
        if self.hoodId in self.ZoneToIdles:
            self.numIdles = len(self.ZoneToIdles[self.hoodId])
        if self.hoodId in self.ZoneToFightAnims:
            self.numFightAnims = len(self.ZoneToFightAnims[self.hoodId])
        self.idleInterval = None
        anim = node.getTag('DNAAnim')
        self.trashcan = Actor.Actor(node, copy=0)
        self.trashcan.reparentTo(node)
        animDict = {}
        animDict['anim'] = '%s/%s' % (self.path, anim)
        for i in xrange(self.numIdles):
            baseAnim = self.ZoneToIdles[self.hoodId][i]
            if isinstance(baseAnim, tuple):
                baseAnim = baseAnim[0]
            animStr = self.path + '/' + baseAnim
            animKey = 'idle%d' % i
            animDict[animKey] = animStr
            settleName = self.getSettleName(i)
            if settleName:
                settleStr = self.path + '/' + settleName
                settleKey = 'settle%d' % i
                animDict[settleKey] = settleStr

        for i in xrange(self.numFightAnims):
            animStr = self.path + '/' + self.ZoneToFightAnims[self.hoodId][i]
            animKey = 'fight%d' % i
            animDict[animKey] = animStr

        if self.hoodId in self.ZoneToIdleIntoFightAnims:
            animStr = self.path + '/' + self.ZoneToIdleIntoFightAnims[self.hoodId]
            animKey = 'idleIntoFight'
            animDict[animKey] = animStr
        if self.hoodId in self.ZoneToIdleIntoFightAnims:
            animStr = self.path + '/' + self.ZoneToVictoryAnims[self.hoodId]
            animKey = 'victory'
            animDict[animKey] = animStr
        if self.hoodId in self.ZoneToSadAnims:
            animStr = self.path + '/' + self.ZoneToSadAnims[self.hoodId]
            animKey = 'sad'
            animDict[animKey] = animStr
        self.trashcan.loadAnims(animDict)
        self.trashcan.pose('anim', 0)
        self.node = self.trashcan
        self.idleInterval = self.createIdleInterval()
        self.battleCheerInterval = self.createBattleCheerInterval()
        self.victoryInterval = self.createVictoryInterval()
        self.sadInterval = self.createSadInterval()
        if settings['smoothAnimations']:
            self.trashcan.setBlend(frameBlend=True)
        return

    def createIdleInterval(self):
        result = Sequence()
        if self.numIdles >= 3:
            numberOfAnimsAbove2 = self.numIdles - 2
            for rareIdle in xrange(2, self.numIdles):
                for i in xrange(2):
                    result.append(ActorInterval(self.node, 'idle0'))
                    result.append(Wait(self.IdlePauseTime))
                    result.append(ActorInterval(self.node, 'idle1'))
                    result.append(Wait(self.IdlePauseTime))

                result.append(ActorInterval(self.node, 'idle%d' % rareIdle))
                result.append(Wait(self.IdlePauseTime))

        else:
            for i in xrange(self.numIdles):
                result.append(ActorInterval(self.node, 'idle%d' % i))

        self.notify.debug('idle interval=%s' % result)
        return result

    def createBattleCheerText(self):
        self.HpTextGenerator.setFont(ToontownGlobals.getSignFont())
        self.HpTextGenerator.setText(self.BattleCheerText)
        self.HpTextGenerator.clearShadow()
        self.HpTextGenerator.setAlign(TextNode.ACenter)
        r = 0
        g = 0
        b = 1
        a = 1
        self.HpTextGenerator.setTextColor(r, g, b, a)
        self.hpTextNode = self.HpTextGenerator.generate()
        self.hpText = self.node.attachNewNode(self.hpTextNode)
        self.hpText.setScale(1)
        self.hpText.setBillboardPointEye()
        self.hpText.setBin('fixed', 100)
        self.hpText.setPos(0, 0, 4)
        self.hpText.hide()

    def createBattleCheerInterval(self):
        result = Sequence()
        for i in xrange(self.numFightAnims):
            animKey = 'fight%d' % i
            animAndSoundIval = self.createAnimAndSoundIval(animKey)
            origAnimName = self.node.getAnimFilename(animKey).split('/')[-1]
            if self.hasOverrideIval(origAnimName):
                result.append(self.getOverrideIval(origAnimName))
            elif self.hasSpecialIval(origAnimName):
                result.append(Parallel(animAndSoundIval, self.getSpecialIval(origAnimName)))
            else:
                result.append(animAndSoundIval)

        self.createBattleCheerText()
        battleCheerTextIval = Sequence(Func(self.hpText.show), self.hpText.posInterval(duration=4.0, pos=Vec3(0, 0, 7), startPos=(0, 0, 3)), Func(self.hpText.hide))
        ivalWithText = Parallel(battleCheerTextIval, result)
        return ivalWithText

    def createSadInterval(self):
        result = Sequence()
        if self.hoodId in self.ZoneToSadAnims:
            result = self.createAnimAndSoundIval('sad')
        return result

    def hasSpecialIval(self, origAnimName):
        return False

    def getSpecialIval(self, origAnimName):
        return Sequence()

    def hasOverrideIval(self, origAnimName):
        return False

    def getOverrideIval(self, origAnimName):
        return Sequence()

    def createVictoryInterval(self):
        result = Sequence()
        if self.hoodId in self.ZoneToVictoryAnims:
            animAndSoundIval = self.createAnimAndSoundIval('victory')
            result.append(animAndSoundIval)
        return result

    def enter(self):
        GenericAnimatedProp.GenericAnimatedProp.enter(self)
        if config.GetBool('props-buff-battles', True):
            self.notify.debug('props buff battles is true')
            self.node.stop()
            self.node.pose('idle0', 0)
            self.requestIdleOrSad()
        else:
            self.notify.debug('props do not buff battles')
            self.node.stop()
            self.node.pose('idle0', 0)

    def exit(self):
        self.okToStartNextAnim = False
        self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
        GenericAnimatedProp.GenericAnimatedProp.exit(self)
        self.request('Off')

    def requestIdleOrSad(self):
        if not hasattr(self, 'node') or not self.node:
            self.notify.warning("requestIdleOrSad  returning hasattr(self,'node')=%s" % hasattr(self, 'node'))
            return
        if self.buildingsMakingMeSad:
            self.request('Sad')
        else:
            self.request('DoIdleAnim')

    def enterDoIdleAnim(self):
        self.notify.debug('enterDoIdleAnim numIdels=%d' % self.numIdles)
        self.okToStartNextAnim = True
        self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
        self.startNextIdleAnim()

    def exitDoIdleAnim(self):
        self.notify.debug('exitDoIdlesAnim numIdles=%d' % self.numIdles)
        self.okToStartNextAnim = False
        self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
        self.calcLastIdleFrame()
        self.clearCurIval()

    def calcLastIdleFrame(self):
        if self.curIval and self.curIval.ivals:
            firstIval = self.curIval.ivals[0]
            if isinstance(firstIval, ActorInterval):
                self.lastIdleFrame = firstIval.getCurrentFrame()
                self.lastIdleAnimName = firstIval.animName
            elif isinstance(firstIval, Parallel):
                for testIval in firstIval.ivals:
                    if isinstance(firstIval, ActorInterval):
                        self.lastIdleTime = testIval.getT()
                        self.lastIdleAnimName = testIval.animName
                        break

    def chooseIdleAnimToRun(self):
        result = self.numIdles - 1
        if config.GetBool('randomize-interactive-idles', True):
            pairs = []
            for i in xrange(self.numIdles):
                reversedChance = self.numIdles - i - 1
                pairs.append((math.pow(2, reversedChance), i))

            sum = math.pow(2, self.numIdles) - 1
            result = weightedChoice(pairs, sum=sum)
            self.notify.debug('chooseAnimToRun numIdles=%s pairs=%s result=%s' % (self.numIdles, pairs, result))
        else:
            result = self.lastPlayingAnimPhase + 1
            if result >= len(self.ZoneToIdles[self.hoodId]):
                result = 0
        return result

    def startNextIdleAnim(self):
        if not hasattr(self, 'node') or not self.node:
            self.notify.warning("startNextIdleAnim returning hasattr(self,'node')=%s" % hasattr(self, 'node'))
            return
        else:
            self.curIval = None
            if self.okToStartNextAnim:
                self.notify.debug('got pass okToStartNextAnim')
                whichAnim = self.chooseIdleAnimToRun()
                self.lastPlayingAnimPhase = whichAnim
                self.curIval = self.createIdleAnimSequence(whichAnim)
                self.notify.debug('starting curIval of length %s' % self.curIval.getDuration())
                self.curIval.start()
            else:
                self.curIval = Wait(3)
                self.notify.debug('false self.okToStartNextAnim=%s' % self.okToStartNextAnim)
            return

    def createIdleAnimAndSoundInterval(self, whichIdleAnim, startingTime = 0):
        animIval = self.node.actorInterval('idle%d' % whichIdleAnim, startTime=startingTime)
        animIvalDuration = animIval.getDuration()
        origAnimName = self.ZoneToIdles[self.hoodId][whichIdleAnim]
        if isinstance(origAnimName, tuple):
            origAnimName = origAnimName[0]
        soundIval = self.createSoundInterval(origAnimName, animIvalDuration)
        soundIvalDuration = soundIval.getDuration()
        if self.hasSpecialIval(origAnimName):
            specialIval = self.getSpecialIval(origAnimName)
            return Parallel(animIval, soundIval, specialIval)
        else:
            return Parallel(animIval, soundIval)

    def createIdleAnimSequence(self, whichIdleAnim):
        dummyResult = Sequence(Wait(self.IdlePauseTime))
        if not hasattr(self, 'node') or not self.node:
            self.notify.warning("createIdleAnimSequence returning dummyResult hasattr(self,'node')=%s" % hasattr(self, 'node'))
            return dummyResult
        idleAnimAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim)
        result = Sequence(idleAnimAndSound, Wait(self.IdlePauseTime), Func(self.startNextIdleAnim))
        if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 2:
            info = self.ZoneToIdles[self.hoodId][whichIdleAnim]
            origAnimName = info[0]
            minLoop = info[1]
            maxLoop = info[2]
            settleAnim = info[3]
            minPauseTime = info[4]
            maxPauseTime = info[5]
            numberOfLoops = random.randrange(minLoop, maxLoop + 1)
            pauseTime = random.randrange(minPauseTime, maxPauseTime + 1)
            result = Sequence()
            for i in xrange(numberOfLoops):
                result.append(idleAnimAndSound)

            if self.getSettleName(whichIdleAnim):
                result.append(self.node.actorInterval('settle%d' % whichIdleAnim))
            result.append(Wait(pauseTime))
            result.append(Func(self.startNextIdleAnim))
        return result

    def gotoBattleCheer(self):
        self.notify.debugStateCall(self)
        self.request('BattleCheer')

    def gotoIdle(self):
        self.notify.debugStateCall(self)
        self.request('DoIdleAnim')

    def gotoVictory(self):
        self.notify.debugStateCall(self)
        self.request('Victory')

    def gotoSad(self, buildingDoId):
        self.notify.debugStateCall(self)
        self.buildingsMakingMeSad.add(buildingDoId)
        self.request('Sad')

    def buildingLiberated(self, buildingDoId):
        self.buildingsMakingMeSad.discard(buildingDoId)
        if not self.buildingsMakingMeSad:
            self.gotoIdle()

    def calcWhichIdleAnim(self, animName):
        result = 0
        info = self.ZoneToIdles[self.hoodId]
        for index, curInfo in enumerate(info):
            if isinstance(curInfo, tuple):
                if curInfo[0] == animName:
                    result = index
                    break
            elif isinstance(curInfo, str):
                if curInfo == animName:
                    result = index
                    breal

        return result

    def enterBattleCheer(self):
        self.notify.debugStateCall(self)
        self.curIval = self.battleCheerInterval
        if self.curIval:
            self.curIval.loop()

    def exitBattleCheer(self):
        self.notify.debugStateCall(self)
        if self.curIval:
            self.curIval.finish()
            self.curIval = None
        return

    def enterVictory(self):
        self.notify.debugStateCall(self)
        self.curIval = self.victoryInterval
        if self.curIval:
            self.curIval.loop()

    def exitVictory(self):
        self.notify.debugStateCall(self)
        if self.curIval:
            self.curIval.finish()
            self.curIval = None
        return

    def enterSad(self):
        self.notify.debugStateCall(self)
        self.curIval = self.sadInterval
        if self.curIval:
            self.curIval.loop()

    def exitSad(self):
        self.notify.debugStateCall(self)
        if self.curIval:
            self.curIval.finish()
            self.curIval = None
        return

    def getSettleName(self, whichIdleAnim):
        if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 3:
            return self.ZoneToIdles[self.hoodId][whichIdleAnim][3]
        else:
            return None

    def getOrigIdleAnimName(self, whichIdleAnim):
        anim = self.ZoneToIdles[self.hoodId][whichIdleAnim]
        if isinstance(anim, tuple):
            return anim[0]
        return anim

    def createAnimAndSoundIval(self, animKey):
        animIval = self.node.actorInterval(animKey)
        animIvalDuration = animIval.getDuration()
        origAnimName = self.node.getAnimFilename(animKey)
        soundIval = self.createSoundInterval(origAnimName, animIvalDuration)
        soundIvalDuration = soundIval.getDuration()
        if self.hasSpecialIval(origAnimName):
            specialIval = self.getSpecialIval(origAnimName)
            return Parallel(animIval, soundIval, specialIval)
        else:
            return Parallel(animIval, soundIval)

    def clearCurIval(self):
        if self.curIval:
            self.curIval.finish()
        clearPythonIvals(self.curIval)
        self.curIval = None
        return