from toontown.toonbase import ToontownGlobals
import PhoneGlobals
from toontown.catalog import CatalogScreen
from toontown.catalog import CatalogItem
from toontown.toontowngui import TTDialog
from toontown.toonbase import TTLocalizer
import DistributedHouseInterior
from direct.actor import Actor
import DistributedFurnitureItem
from direct.distributed import ClockDelta
from direct.showbase import PythonUtil
from direct.showutil import Rope
from direct.directnotify.DirectNotifyGlobal import *
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
import string
from toontown.quest import Quests
from direct.task import Task

class DistributedPhone(DistributedFurnitureItem.DistributedFurnitureItem):
    notify = directNotify.newCategory('DistributedPhone')
    movieDelay = 0.5

    def __init__(self, cr):
        DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr)
        self.lastAvId = 0
        self.hasLocalAvatar = 0
        self.lastTime = 0
        self.initialScale = None
        self.usedInitialScale = 0
        self.toonScale = None
        self.phoneGui = None
        self.phoneDialog = None
        self.model = None
        self.cord = None
        self.receiverGeom = None
        self.receiverJoint = None
        self.phoneSphereEvent = 'phoneSphere'
        self.phoneSphereEnterEvent = 'enter' + self.phoneSphereEvent
        self.phoneGuiDoneEvent = 'phoneGuiDone'
        self.pickupMovieDoneEvent = 'phonePickupDone'
        self.numHouseItems = None
        self.interval = None
        self.intervalAvatar = None
        self.phoneInUse = 0
        self.origToonHpr = None
        return

    def announceGenerate(self):
        self.notify.debug('announceGenerate')
        DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self)
        self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere)
        self.load()
        taskMgr.doMethodLater(6, self.ringIfHasPhoneQuest, self.uniqueName('ringDoLater'))

    def loadModel(self):
        self.model = Actor.Actor('phase_5.5/models/estate/prop_phone-mod', {'SS_phoneOut': 'phase_5.5/models/estate/prop_phone-SS_phoneOut',
         'SS_takePhone': 'phase_5.5/models/estate/prop_phone-SS_takePhone',
         'SS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SS_phoneNeutral',
         'SS_phoneBack': 'phase_5.5/models/estate/prop_phone-SS_phoneBack',
         'SM_phoneOut': 'phase_5.5/models/estate/prop_phone-SM_phoneOut',
         'SM_takePhone': 'phase_5.5/models/estate/prop_phone-SM_takePhone',
         'SM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SM_phoneNeutral',
         'SM_phoneBack': 'phase_5.5/models/estate/prop_phone-SM_phoneBack',
         'SL_phoneOut': 'phase_5.5/models/estate/prop_phone-SL_phoneOut',
         'SL_takePhone': 'phase_5.5/models/estate/prop_phone-SL_takePhone',
         'SL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SL_phoneNeutral',
         'SL_phoneBack': 'phase_5.5/models/estate/prop_phone-SL_phoneBack',
         'MS_phoneOut': 'phase_5.5/models/estate/prop_phone-MS_phoneOut',
         'MS_takePhone': 'phase_5.5/models/estate/prop_phone-MS_takePhone',
         'MS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MS_phoneNeutral',
         'MS_phoneBack': 'phase_5.5/models/estate/prop_phone-MS_phoneBack',
         'MM_phoneOut': 'phase_5.5/models/estate/prop_phone-MM_phoneOut',
         'MM_takePhone': 'phase_5.5/models/estate/prop_phone-MM_takePhone',
         'MM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MM_phoneNeutral',
         'MM_phoneBack': 'phase_5.5/models/estate/prop_phone-MM_phoneBack',
         'ML_phoneOut': 'phase_5.5/models/estate/prop_phone-ML_phoneOut',
         'ML_takePhone': 'phase_5.5/models/estate/prop_phone-ML_takePhone',
         'ML_phoneNeutral': 'phase_5.5/models/estate/prop_phone-ML_phoneNeutral',
         'ML_phoneBack': 'phase_5.5/models/estate/prop_phone-ML_phoneBack',
         'LS_phoneOut': 'phase_5.5/models/estate/prop_phone-LS_phoneOut',
         'LS_takePhone': 'phase_5.5/models/estate/prop_phone-LS_takePhone',
         'LS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LS_phoneNeutral',
         'LS_phoneBack': 'phase_5.5/models/estate/prop_phone-LS_phoneBack',
         'LM_phoneOut': 'phase_5.5/models/estate/prop_phone-LM_phoneOut',
         'LM_takePhone': 'phase_5.5/models/estate/prop_phone-LM_takePhone',
         'LM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LM_phoneNeutral',
         'LM_phoneBack': 'phase_5.5/models/estate/prop_phone-LM_phoneBack',
         'LL_phoneOut': 'phase_5.5/models/estate/prop_phone-LL_phoneOut',
         'LL_takePhone': 'phase_5.5/models/estate/prop_phone-LL_takePhone',
         'LL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LL_phoneNeutral',
         'LL_phoneBack': 'phase_5.5/models/estate/prop_phone-LL_phoneBack'})
        self.model.pose('SS_phoneOut', 0)
        self.receiverJoint = self.model.find('**/joint_receiver')
        self.receiverGeom = self.receiverJoint.getChild(0)
        mount = loader.loadModel('phase_5.5/models/estate/phoneMount-mod')
        mount.setTransparency(0, 1)
        self.model.reparentTo(mount)
        self.ringSfx = loader.loadSfx('phase_3.5/audio/sfx/telephone_ring.ogg')
        self.handleSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_handle2.ogg')
        self.hangUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_hang_up.ogg')
        self.pickUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_pickup1.ogg')
        if self.initialScale:
            mount.setScale(*self.initialScale)
            self.usedInitialScale = 1
        phoneSphere = CollisionSphere(0, -0.66, 0, 0.2)
        phoneSphere.setTangible(0)
        phoneSphereNode = CollisionNode(self.phoneSphereEvent)
        phoneSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
        phoneSphereNode.addSolid(phoneSphere)
        mount.attachNewNode(phoneSphereNode)
        if not self.model.find('**/CurveNode7').isEmpty():
            self.setupCord()
        return mount

    def setupCamera(self, mode):
        camera.wrtReparentTo(render)
        if mode == PhoneGlobals.PHONE_MOVIE_PICKUP:
            quat = Quat()
            quat.setHpr((35, -8, 0))
            LerpPosQuatInterval(camera, 1, (4, -4, base.localAvatar.getHeight() - 0.5), quat, blendType='easeOut', other=base.localAvatar).start()

    def setupCord(self):
        if self.cord:
            self.cord.detachNode()
            self.cord = None
        self.cord = Rope.Rope(self.uniqueName('phoneCord'))
        self.cord.setColor(0, 0, 0, 1)
        self.cord.setup(4, ((self.receiverGeom, (0, 0, 0)),
         (self.model.find('**/joint_curveNode1'), (0, 0, 0)),
         (self.model.find('**/joint_curveNode2'), (0, 0, 0)),
         (self.model.find('**/joint_curveNode3'), (0, 0, 0)),
         (self.model.find('**/joint_curveNode4'), (0, 0, 0)),
         (self.model.find('**/joint_curveNode5'), (0, 0, 0)),
         (self.model.find('**/joint_curveNode6'), (0, 0, 0)),
         (self.model.find('**/CurveNode7'), (0, 0, 0))))
        self.cord.reparentTo(self.model)
        self.cord.node().setBounds(BoundingSphere(Point3(-1.0, -3.2, 2.6), 2.0))
        return

    def disable(self):
        self.notify.debug('disable')
        taskMgr.remove(self.uniqueName('ringDoLater'))
        self.clearInterval()
        if self.phoneGui:
            self.phoneGui.hide()
            self.phoneGui.unload()
            self.phoneGui = None
        if self.phoneDialog:
            self.phoneDialog.cleanup()
            self.phoneDialog = None
        self.__receiverToPhone()
        if self.hasLocalAvatar:
            self.freeAvatar()
        self.ignoreAll()
        DistributedFurnitureItem.DistributedFurnitureItem.disable(self)
        return

    def delete(self):
        self.notify.debug('delete')
        self.model.cleanup()
        DistributedFurnitureItem.DistributedFurnitureItem.delete(self)

    def setInitialScale(self, sx, sy, sz):
        self.initialScale = (sx, sy, sz)
        if not self.usedInitialScale and self.model:
            self.setScale(*self.initialScale)
            self.usedInitialScale = 1

    def __handleEnterSphere(self, collEntry):
        if self.smoothStarted:
            return
        if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5:
            self.notify.debug('Ignoring duplicate entry for avatar.')
            return
        if self.hasLocalAvatar:
            self.freeAvatar()
        self.notify.debug('Entering Phone Sphere....')
        taskMgr.remove(self.uniqueName('ringDoLater'))
        self.ignore(self.phoneSphereEnterEvent)
        self.cr.playGame.getPlace().detectedPhoneCollision()
        self.hasLocalAvatar = 1
        self.sendUpdate('avatarEnter', [])

    def __handlePhoneDone(self):
        self.sendUpdate('avatarExit', [])
        self.ignore(self.phoneGuiDoneEvent)
        self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere)
        self.phoneGui = None
        return

    def freeAvatar(self):
        if self.hasLocalAvatar:
            base.localAvatar.speed = 0
            taskMgr.remove(self.uniqueName('lerpCamera'))
            base.localAvatar.posCamera(0, 0)
            if base.cr.playGame.place != None:
                base.cr.playGame.getPlace().setState('walk')
            self.hasLocalAvatar = 0
        self.ignore(self.pickupMovieDoneEvent)
        self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere)
        self.lastTime = globalClock.getFrameTime()
        return

    def setLimits(self, numHouseItems):
        self.numHouseItems = numHouseItems

    def setMovie(self, mode, avId, timestamp):
        elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp, bits=32)
        elapsed = max(elapsed - self.movieDelay, 0)
        self.ignore(self.pickupMovieDoneEvent)
        if avId != 0:
            self.lastAvId = avId

        self.lastTime = globalClock.getFrameTime()
        isLocalToon = avId == base.localAvatar.doId
        avatar = self.cr.doId2do.get(avId)
        self.notify.debug('setMovie: %s %s %s' % (mode, avId, isLocalToon))

        if mode == PhoneGlobals.PHONE_MOVIE_CLEAR:
            self.notify.debug('setMovie: clear')
            self.numHouseItems = None
            if self.phoneInUse:
                self.clearInterval()
            self.phoneInUse = 0

        elif mode == PhoneGlobals.PHONE_MOVIE_EMPTY:
            self.notify.debug('setMovie: empty')
            if isLocalToon:
                self.phoneDialog = TTDialog.TTDialog(dialogName='PhoneEmpty', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedPhoneEmpty, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog)
            self.numHouseItems = None
            self.phoneInUse = 0

        elif mode == PhoneGlobals.PHONE_MOVIE_PICKUP:
            self.notify.debug('setMovie: gui')
            if avatar:
                interval = self.takePhoneInterval(avatar)
                if isLocalToon:
                    self.setupCamera(mode)
                    interval.setDoneEvent(self.pickupMovieDoneEvent)
                    self.acceptOnce(self.pickupMovieDoneEvent, self.__showPhoneGui)
                self.playInterval(interval, elapsed, avatar)
                self.phoneInUse = 1

        elif mode == PhoneGlobals.PHONE_MOVIE_HANGUP:
            self.notify.debug('setMovie: gui')
            if avatar:
                interval = self.replacePhoneInterval(avatar)
                self.playInterval(interval, elapsed, avatar)
            self.numHouseItems = None
            self.phoneInUse = 0

        else:
            self.notify.warning('unknown mode in setMovie: %s' % mode)
        return

    def __showPhoneGui(self):
        if self.toonScale:
            self.sendUpdate('setNewScale', [self.toonScale[0], self.toonScale[1], self.toonScale[2]])
        self.phoneGui = CatalogScreen.CatalogScreen(phone=self, doneEvent=self.phoneGuiDoneEvent)
        self.phoneGui.show()
        self.accept(self.phoneGuiDoneEvent, self.__handlePhoneDone)
        self.accept('phoneAsleep', self.__handlePhoneAsleep)

    def __handlePhoneAsleep(self):
        self.ignore('phoneAsleep')
        if self.phoneGui:
            self.phoneGui.unload()
        self.__handlePhoneDone()

    def requestPurchase(self, item, callback, optional = -1):
        blob = item.getBlob(store=CatalogItem.Customization)
        context = self.getCallbackContext(callback, [item])
        self.sendUpdate('requestPurchaseMessage', [context, blob, optional])

    def requestGiftPurchase(self, item, targetDoID, callback, optional = -1):
        print 'in the client phone'
        blob = item.getBlob(store=CatalogItem.Customization)
        context = self.getCallbackContext(callback, [item])
        self.sendUpdate('requestGiftPurchaseMessage', [context, targetDoID, blob, optional])

    def requestPurchaseResponse(self, context, retcode):
        self.doCallbackContext(context, [retcode])

    def requestGiftPurchaseResponse(self, context, retcode):
        self.doCallbackContext(context, [retcode])

    def __clearDialog(self, event):
        self.phoneDialog.cleanup()
        self.phoneDialog = None
        self.freeAvatar()
        return

    def takePhoneInterval(self, toon):
        torso = TextEncoder.upper(toon.style.torso[0])
        legs = TextEncoder.upper(toon.style.legs[0])
        phoneOutAnim = '%s%s_phoneOut' % (torso, legs)
        takePhoneAnim = '%s%s_takePhone' % (torso, legs)
        phoneNeutralAnim = '%s%s_phoneNeutral' % (torso, legs)
        self.toonScale = toon.getGeomNode().getChild(0).getScale(self.getParent())
        walkTime = 1.0
        scaleTime = 1.0
        origScale = self.getScale()
        origToonPos = toon.getPos()
        origToonHpr = toon.getHpr()
        self.origToonHpr = origToonHpr
        self.setScale(self.toonScale)
        toon.setPosHpr(self, 0, -4.5, 0, 0, 0, 0)
        destToonPos = toon.getPos()
        destToonHpr = toon.getHpr()
        destToonHpr = VBase3(PythonUtil.fitSrcAngle2Dest(destToonHpr[0], origToonHpr[0]), destToonHpr[1], destToonHpr[2])
        self.setScale(origScale)
        toon.setPos(origToonPos)
        toon.setHpr(origToonHpr)
        walkToPhone = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), Func(base.playSfx, base.localAvatar.soundWalk), toon.posHprInterval(walkTime, destToonPos, destToonHpr, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth))
        interval = Sequence(Parallel(walkToPhone, ActorInterval(self.model, phoneOutAnim), self.scaleInterval(scaleTime, self.toonScale, blendType='easeInOut')), Parallel(ActorInterval(self.model, takePhoneAnim), ActorInterval(toon, 'takePhone'), Sequence(Wait(0.625), Func(base.playSfx, self.pickUpSfx), Func(self.__receiverToHand, toon), Wait(1), Func(base.playSfx, self.handleSfx))), Func(self.model.loop, phoneNeutralAnim), Func(toon.loop, 'phoneNeutral'), Func(base.playSfx, self.ringSfx))
        return interval

    def replacePhoneInterval(self, toon):
        torso = TextEncoder.upper(toon.style.torso[0])
        legs = TextEncoder.upper(toon.style.legs[0])
        phoneBackAnim = '%s%s_phoneBack' % (torso, legs)
        scaleTime = 1.0
        interval = Sequence(Parallel(ActorInterval(self.model, phoneBackAnim), ActorInterval(toon, 'phoneBack'), Sequence(Wait(1.0), Func(self.__receiverToPhone), Func(base.playSfx, self.hangUpSfx))), self.scaleInterval(scaleTime, localAvatar.getGeomNode().getScale()[2], blendType='easeInOut'), Func(toon.loop, 'neutral'))
        if self.origToonHpr:
            interval.append(Func(toon.setHpr, self.origToonHpr))
            self.origToonHpr = None
        if toon == base.localAvatar:
            interval.append(Func(self.freeAvatar))
        return interval

    def __receiverToHand(self, toon):
        self.receiverGeom.reparentTo(toon.leftHand)
        self.receiverGeom.setPosHpr(0.0906813, 0.380375, 0.1, 32.41, 70.68, 137.04)

    def __receiverToPhone(self):
        self.receiverGeom.reparentTo(self.receiverJoint)
        self.receiverGeom.setPosHpr(0, 0, 0, 0, 0, 0)

    def playInterval(self, interval, elapsed, avatar):
        if self.interval != None:
            self.interval.finish()
            self.interval = None
        self.interval = interval
        self.interval.start(elapsed)
        if self.intervalAvatar != avatar:
            if self.intervalAvatar:
                self.ignore(self.intervalAvatar.uniqueName('disable'))
            if avatar:
                self.accept(avatar.uniqueName('disable'), self.clearInterval)
            self.intervalAvatar = avatar
        return

    def clearInterval(self):
        if self.interval != None:
            self.interval.finish()
            self.interval = None
        if self.intervalAvatar:
            self.ignore(self.intervalAvatar.uniqueName('disable'))
            self.intervalAvatar = None
        self.__receiverToPhone()
        self.model.pose('SS_phoneOut', 0)
        self.phoneInUse = 0
        return

    def ringIfHasPhoneQuest(self, task):
        if Quests.avatarHasPhoneQuest(base.localAvatar) and not Quests.avatarHasCompletedPhoneQuest(base.localAvatar):
            self.ring()
        return Task.done

    def ring(self):
        if self.phoneInUse:
            return 0
        phone = self.find('**/prop_phone')
        r = 2.0
        w = 0.05
        shakeOnce = Sequence(Func(phone.setR, r), Wait(w), Func(phone.setR, -r), Wait(w))
        shakeSeq = Sequence()
        for i in range(16):
            shakeSeq.append(shakeOnce)

        ringIval = Parallel(Func(base.playSfx, self.ringSfx), shakeSeq, Func(phone.setR, 0))
        self.playInterval(ringIval, 0.0, None)
        return