import copy
from direct.controls.ControlManager import CollisionHandlerRayStart
from direct.directnotify import DirectNotifyGlobal
from direct.fsm import ClassicFSM
from direct.fsm import State
from direct.interval.IntervalGlobal import *
from pandac.PandaModules import *
import string

import CCharChatter
import CCharPaths
from otp.avatar import Avatar
from toontown.char import CharDNA
from toontown.char import DistributedChar
from toontown.chat.ChatGlobals import *
from toontown.effects import DustCloud
from toontown.toonbase import ToontownGlobals
from toontown.toonbase.TTLocalizer import Donald, DonaldDock, WesternPluto, Pluto


class DistributedCCharBase(DistributedChar.DistributedChar):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCCharBase')

    def __init__(self, cr, name, dnaName):
        try:
            self.DistributedCCharBase_initialized
            return
        except:
            self.DistributedCCharBase_initialized = 1

        DistributedChar.DistributedChar.__init__(self, cr)
        dna = CharDNA.CharDNA()
        dna.newChar(dnaName)
        self.setDNA(dna)
        self.setName(name)
        self.setTransparency(TransparencyAttrib.MDual, 1)
        fadeIn = self.colorScaleInterval(0.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0), blendType='easeInOut')
        fadeIn.start()
        self.diffPath = None
        self.transitionToCostume = 0
        self.__initCollisions()
        return

    def __initCollisions(self):
        self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 8.0)
        self.cSphere.setTangible(0)
        self.cSphereNode = CollisionNode(self.getName() + 'BlatherSphere')
        self.cSphereNode.addSolid(self.cSphere)
        self.cSphereNodePath = self.attachNewNode(self.cSphereNode)
        self.cSphereNodePath.hide()
        self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask)
        self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)
        self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
        self.cRayNode = CollisionNode(self.getName() + 'cRay')
        self.cRayNode.addSolid(self.cRay)
        self.cRayNodePath = self.attachNewNode(self.cRayNode)
        self.cRayNodePath.hide()
        self.cRayBitMask = ToontownGlobals.FloorBitmask
        self.cRayNode.setFromCollideMask(self.cRayBitMask)
        self.cRayNode.setIntoCollideMask(BitMask32.allOff())
        self.lifter = CollisionHandlerFloor()
        self.lifter.setOffset(ToontownGlobals.FloorOffset)
        self.lifter.setReach(10.0)
        self.lifter.setMaxVelocity(0.0)
        self.lifter.addCollider(self.cRayNodePath, self)
        self.cTrav = base.localAvatar.cTrav

    def __deleteCollisions(self):
        del self.cSphere
        del self.cSphereNode
        self.cSphereNodePath.removeNode()
        del self.cSphereNodePath
        self.cRay = None
        self.cRayNode = None
        self.cRayNodePath = None
        self.lifter = None
        self.cTrav = None
        return

    def disable(self):
        self.stopBlink()
        self.ignoreAll()
        self.chatTrack.finish()
        del self.chatTrack
        if self.chatterDialogue:
            self.chatterDialogue.stop()
        del self.chatterDialogue
        DistributedChar.DistributedChar.disable(self)
        self.stopEarTask()

    def delete(self):
        try:
            self.DistributedCCharBase_deleted
        except:
            self.setParent(NodePath('Temp'))
            self.DistributedCCharBase_deleted = 1
            self.__deleteCollisions()
            DistributedChar.DistributedChar.delete(self)

    def generate(self, diffPath = None):
        DistributedChar.DistributedChar.generate(self)
        if diffPath == None:
            self.setPos(CCharPaths.getNodePos(CCharPaths.startNode, CCharPaths.getPaths(self.getName(), self.getCCLocation())))
        else:
            self.setPos(CCharPaths.getNodePos(CCharPaths.startNode, CCharPaths.getPaths(diffPath, self.getCCLocation())))
        self.setHpr(0, 0, 0)
        self.setParent(ToontownGlobals.SPRender)
        self.startBlink()
        self.startEarTask()
        self.chatTrack = Sequence()
        self.chatterDialogue = None
        self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)
        self.accept('exitSafeZone', self.__handleExitSafeZone)
        return

    def __handleExitSafeZone(self):
        self.__handleCollisionSphereExit(None)
        return

    def __handleCollisionSphereEnter(self, collEntry):
        self.notify.debug('Entering collision sphere...')
        self.sendUpdate('avatarEnter', [])
        self.accept('chatUpdate', self.__handleChatUpdate)
        self.accept('chatUpdateSC', self.__handleChatUpdateSC)
        self.accept('chatUpdateSCCustom', self.__handleChatUpdateSCCustom)
        self.accept('chatUpdateSCToontask', self.__handleChatUpdateSCToontask)
        self.nametag3d.setBin('transparent', 100)
        self.acceptOnce('exit' + self.cSphereNode.getName(), self.__handleCollisionSphereExit)

    def __handleCollisionSphereExit(self, collEntry):
        self.notify.debug('Exiting collision sphere...')
        self.sendUpdate('avatarExit', [])
        self.ignore('chatUpdate')
        self.ignore('chatUpdateSC')
        self.ignore('chatUpdateSCCustom')
        self.ignore('chatUpdateSCToontask')
        self.acceptOnce('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter)

    def __handleChatUpdate(self, msg, chatFlags):
        self.sendUpdate('setNearbyAvatarChat', [msg])

    def __handleChatUpdateSC(self, msgIndex):
        self.sendUpdate('setNearbyAvatarSC', [msgIndex])

    def __handleChatUpdateSCCustom(self, msgIndex):
        self.sendUpdate('setNearbyAvatarSCCustom', [msgIndex])

    def __handleChatUpdateSCToontask(self, taskId, toNpcId, toonProgress, msgIndex):
        self.sendUpdate('setNearbyAvatarSCToontask', [taskId,
         toNpcId,
         toonProgress,
         msgIndex])

    def makeTurnToHeadingTrack(self, heading):
        curHpr = self.getHpr()
        destHpr = self.getHpr()
        destHpr.setX(heading)
        if destHpr[0] - curHpr[0] > 180.0:
            destHpr.setX(destHpr[0] - 360)
        elif destHpr[0] - curHpr[0] < -180.0:
            destHpr.setX(destHpr[0] + 360)
        turnSpeed = 180.0
        time = abs(destHpr[0] - curHpr[0]) / turnSpeed
        turnTracks = Parallel()
        if time > 0.2:
            turnTracks.append(Sequence(Func(self.loop, 'walk'), Wait(time), Func(self.loop, 'neutral')))
        turnTracks.append(LerpHprInterval(self, time, destHpr, name='lerp' + self.getName() + 'Hpr'))
        return turnTracks

    def setChat(self, category, msg, avId):
        if avId in self.cr.doId2do:
            avatar = self.cr.doId2do[avId]
            chatter = CCharChatter.getChatter(self.getName(), self.getCCChatter())
            if category >= len(chatter):
                self.notify.debug("Chatter's changed")
                return
            elif len(chatter[category]) <= msg:
                self.notify.debug("Chatter's changed")
                return
            str = chatter[category][msg]
            if '%' in str:
                str = copy.deepcopy(str)
                avName = avatar.getName()
                str = str.replace('%', avName)
            track = Sequence()
            if category != CCharChatter.GOODBYE:
                curHpr = self.getHpr()
                self.headsUp(avatar)
                destHpr = self.getHpr()
                self.setHpr(curHpr)
                track.append(self.makeTurnToHeadingTrack(destHpr[0]))
            if self.getName() == Donald or self.getName() == WesternPluto or self.getName() == Pluto:
                chatFlags = CFThought | CFTimeout
                if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
                    holidayIds = base.cr.newsManager.getHolidayIdList()
                    if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds:
                        if self.getName() == Pluto:
                            chatFlags = CFTimeout | CFSpeech
            elif self.getName() == DonaldDock:
                chatFlags = CFTimeout | CFSpeech
                self.nametag3d.hide()
            else:
                chatFlags = CFTimeout | CFSpeech
            self.chatterDialogue = self.getChatterDialogue(category, msg)
            track.append(Func(self.setChatAbsolute, str, chatFlags, self.chatterDialogue))
            self.chatTrack.finish()
            self.chatTrack = track
            self.chatTrack.start()

    def setWalk(self, srcNode, destNode, timestamp):
        pass

    def walkSpeed(self):
        return 0.1

    def enableRaycast(self, enable = 1):
        if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode:
            self.notify.debug('raycast info not found for ' + self.getName())
            return
        self.cTrav.removeCollider(self.cRayNodePath)
        if enable:
            if self.notify.getDebug():
                self.notify.debug('enabling raycast for ' + self.getName())
            self.cTrav.addCollider(self.cRayNodePath, self.lifter)
        elif self.notify.getDebug():
            self.notify.debug('disabling raycast for ' + self.getName())

    def getCCLocation(self):
        return 0

    def getCCChatter(self):
        self.handleHolidays()
        return self.CCChatter

    def handleHolidays(self):
        self.CCChatter = 0
        if hasattr(base.cr, 'newsManager') and base.cr.newsManager:
            holidayIds = base.cr.newsManager.getHolidayIdList()
            if ToontownGlobals.CRASHED_LEADERBOARD in holidayIds:
                self.CCChatter = ToontownGlobals.CRASHED_LEADERBOARD
            elif ToontownGlobals.CIRCUIT_RACING_EVENT in holidayIds:
                self.CCChatter = ToontownGlobals.CIRCUIT_RACING_EVENT
            elif ToontownGlobals.WINTER_CAROLING in holidayIds:
                self.CCChatter = ToontownGlobals.WINTER_CAROLING
            elif ToontownGlobals.WINTER_DECORATIONS in holidayIds:
                self.CCChatter = ToontownGlobals.WINTER_DECORATIONS
            elif ToontownGlobals.WACKY_WINTER_CAROLING in holidayIds:
                self.CCChatter = ToontownGlobals.WACKY_WINTER_CAROLING
            elif ToontownGlobals.WACKY_WINTER_DECORATIONS in holidayIds:
                self.CCChatter = ToontownGlobals.WACKY_WINTER_DECORATIONS
            elif ToontownGlobals.VALENTINES_DAY in holidayIds:
                self.CCChatter = ToontownGlobals.VALENTINES_DAY
            elif ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds:
                self.CCChatter = ToontownGlobals.APRIL_FOOLS_COSTUMES
            elif ToontownGlobals.SILLY_CHATTER_ONE in holidayIds:
                self.CCChatter = ToontownGlobals.SILLY_CHATTER_ONE
            elif ToontownGlobals.SILLY_CHATTER_TWO in holidayIds:
                self.CCChatter = ToontownGlobals.SILLY_CHATTER_TWO
            elif ToontownGlobals.SILLY_CHATTER_THREE in holidayIds:
                self.CCChatter = ToontownGlobals.SILLY_CHATTER_THREE
            elif ToontownGlobals.SILLY_CHATTER_FOUR in holidayIds:
                self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
            elif ToontownGlobals.SILLY_CHATTER_FIVE in holidayIds:
                self.CCChatter = ToontownGlobals.SILLY_CHATTER_FOUR
            elif ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds:
                self.CCChatter = ToontownGlobals.HALLOWEEN_COSTUMES
            elif ToontownGlobals.SPOOKY_COSTUMES in holidayIds:
                self.CCChatter = ToontownGlobals.SPOOKY_COSTUMES
            elif ToontownGlobals.SELLBOT_FIELD_OFFICE in holidayIds:
                self.CCChatter = ToontownGlobals.SELLBOT_FIELD_OFFICE

    def fadeAway(self):
        fadeOut = self.colorScaleInterval(0.5, Vec4(1, 1, 1, 0.5), startColorScale=Vec4(1, 1, 1, 1), blendType='easeInOut')
        fadeOut.start()
        self.loop('neutral')
        if self.fsm:
            self.fsm.addState(State.State('TransitionToCostume', self.enterTransitionToCostume, self.exitTransitionToCostume, ['Off']))
            self.fsm.request('TransitionToCostume', force=1)
        self.ignoreAll()

    def enterTransitionToCostume(self):

        def getDustCloudIval():
            dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1)
            dustCloud.setBillboardAxis(2.0)
            dustCloud.setZ(4)
            dustCloud.setScale(0.6)
            dustCloud.createTrack()
            return Sequence(Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloadIval')

        dust = getDustCloudIval()
        dust.start()

    def exitTransitionToCostume(self):
        pass