from pandac.PandaModules import *
from toontown.toonbase.ToontownGlobals import *
from direct.interval.IntervalGlobal import *
from direct.fsm import ClassicFSM, State
from toontown.safezone import SafeZoneLoader
import random
from toontown.launcher import DownloadForceAcknowledge
import House
import Estate
import HouseGlobals
import random
import math
from toontown.coghq import MovingPlatform
from direct.directnotify import DirectNotifyGlobal

class EstateLoader(SafeZoneLoader.SafeZoneLoader):
    notify = DirectNotifyGlobal.directNotify.newCategory('EstateLoader')

    def __init__(self, hood, parentFSM, doneEvent):
        SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent)
        del self.fsm
        self.fsm = ClassicFSM.ClassicFSM('EstateLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'estate', 'house']),
         State.State('estate', self.enterEstate, self.exitEstate, ['quietZone']),
         State.State('house', self.enterHouse, self.exitHouse, ['quietZone']),
         State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['house', 'estate']),
         State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final')
        self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.mid'
        self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.mid'
        self.dnaFile = 'phase_5.5/dna/estate_1.dna'
        self.safeZoneStorageDNAFile = None
        self.cloudSwitch = 0
        self.id = MyEstate
        self.estateOwnerId = None
        self.branchZone = None
        self.houseDoneEvent = 'houseDone'
        self.estateDoneEvent = 'estateDone'
        self.enteredHouse = None
        self.houseNode = [None] * 6
        self.houseModels = [None] * HouseGlobals.NUM_HOUSE_TYPES
        self.houseId2house = {}
        self.barrel = None
        self.clouds = []
        self.cloudTrack = None
        self.sunMoonNode = None
        self.fsm.enterInitialState()
        return

    def load(self):
        SafeZoneLoader.SafeZoneLoader.load(self)
        self.music = base.loader.loadMusic('phase_4/audio/bgm/TC_nbrhood.mid')
        self.underwaterSound = base.loader.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3')
        self.swimSound = base.loader.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.mp3')
        self.submergeSound = base.loader.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.mp3')
        self.birdSound = map(base.loader.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3'])
        self.cricketSound = map(base.loader.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3'])
        if base.goonsEnabled:
            invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons')
            self.invModels = []
            from toontown.toonbase import ToontownBattleGlobals
            for track in range(len(ToontownBattleGlobals.AvPropsNew)):
                itemList = []
                for item in range(len(ToontownBattleGlobals.AvPropsNew[track])):
                    itemList.append(invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[track][item]))

                self.invModels.append(itemList)

            invModel.removeNode()
            del invModel

    def unload(self):
        self.ignoreAll()
        base.cr.estateMgr.leaveEstate()
        self.estateOwnerId = None
        self.estateZoneId = None
        if self.place:
            self.place.exit()
            self.place.unload()
            del self.place
        del self.underwaterSound
        del self.swimSound
        del self.submergeSound
        del self.birdSound
        del self.cricketSound
        for node in self.houseNode:
            node.removeNode()

        del self.houseNode
        for model in self.houseModels:
            model.removeNode()

        del self.houseModels
        del self.houseId2house
        if self.sunMoonNode:
            self.sunMoonNode.removeNode()
            del self.sunMoonNode
            self.sunMoonNode = None
        if self.clouds:
            for cloud in self.clouds:
                cloud[0].removeNode()
                del cloud[1]

            del self.clouds
        if self.barrel:
            self.barrel.removeNode()
        SafeZoneLoader.SafeZoneLoader.unload(self)
        return

    def enter(self, requestStatus):
        self.estateOwnerId = requestStatus.get('ownerId', base.localAvatar.doId)
        base.localAvatar.inEstate = 1
        self.loadCloudPlatforms()
        if base.cloudPlatformsEnabled and 0:
            self.setCloudSwitch(1)
        if self.cloudSwitch:
            self.setCloudSwitch(self.cloudSwitch)
        SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus)

    def exit(self):
        self.ignoreAll()
        base.cr.cache.flush()
        base.localAvatar.stopChat()
        base.localAvatar.inEstate = 0
        SafeZoneLoader.SafeZoneLoader.exit(self)

    def createSafeZone(self, dnaFile):
        SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile)
        self.loadHouses()
        self.loadSunMoon()

    def loadHouses(self):
        for i in range(HouseGlobals.NUM_HOUSE_TYPES):
            self.houseModels[i] = loader.loadModel(HouseGlobals.houseModels[i])

        for i in range(6):
            posHpr = HouseGlobals.houseDrops[i]
            self.houseNode[i] = self.geom.attachNewNode('esHouse_' + str(i))
            self.houseNode[i].setPosHpr(*posHpr)

    def loadSunMoon(self):
        self.sun = loader.loadModel('phase_4/models/props/sun.bam')
        self.moon = loader.loadModel('phase_5.5/models/props/moon.bam')
        self.sunMoonNode = self.geom.attachNewNode('sunMoon')
        self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0)
        if self.sun:
            self.sun.reparentTo(self.sunMoonNode)
            self.sun.setY(270)
            self.sun.setScale(2)
            self.sun.setBillboardPointEye()
        if self.moon:
            self.moon.setP(180)
            self.moon.reparentTo(self.sunMoonNode)
            self.moon.setY(-270)
            self.moon.setScale(15)
            self.moon.setBillboardPointEye()
        self.sunMoonNode.setP(30)

    def enterEstate(self, requestStatus):
        self.notify.debug('enterEstate: requestStatus = %s' % requestStatus)
        ownerId = requestStatus.get('ownerId')
        if ownerId:
            self.estateOwnerId = ownerId
        zoneId = requestStatus['zoneId']
        self.notify.debug('enterEstate, ownerId = %s, zoneId = %s' % (self.estateOwnerId, zoneId))
        self.accept(self.estateDoneEvent, self.handleEstateDone)
        self.place = Estate.Estate(self, self.estateOwnerId, zoneId, self.fsm.getStateNamed('estate'), self.estateDoneEvent)
        base.cr.playGame.setPlace(self.place)
        self.place.load()
        self.place.enter(requestStatus)
        self.estateZoneId = zoneId

    def exitEstate(self):
        self.notify.debug('exitEstate')
        self.ignore(self.estateDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        base.cr.cache.flush()
        return

    def handleEstateDone(self, doneStatus = None):
        if not doneStatus:
            doneStatus = self.place.getDoneStatus()
        how = doneStatus['how']
        shardId = doneStatus['shardId']
        hoodId = doneStatus['hoodId']
        zoneId = doneStatus['zoneId']
        avId = doneStatus.get('avId', -1)
        ownerId = doneStatus.get('ownerId', -1)
        if shardId != None or hoodId != MyEstate:
            self.notify.debug('estate done, and we are backing out to a different hood/shard')
            self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId))
            self.doneStatus = doneStatus
            messenger.send(self.doneEvent)
            return
        if how in ['tunnelIn',
         'teleportIn',
         'doorIn',
         'elevatorIn']:
            self.notify.debug('staying in estateloader')
            self.fsm.request('quietZone', [doneStatus])
        else:
            self.notify.error('Exited hood with unexpected mode %s' % how)
        return

    def enterHouse(self, requestStatus):
        ownerId = requestStatus.get('ownerId')
        if ownerId:
            self.estateOwnerId = ownerId
        self.acceptOnce(self.houseDoneEvent, self.handleHouseDone)
        self.place = House.House(self, self.estateOwnerId, self.fsm.getStateNamed('house'), self.houseDoneEvent)
        base.cr.playGame.setPlace(self.place)
        self.place.load()
        self.place.enter(requestStatus)

    def exitHouse(self):
        self.ignore(self.houseDoneEvent)
        self.place.exit()
        self.place.unload()
        self.place = None
        base.cr.playGame.setPlace(self.place)
        return

    def handleHouseDone(self, doneStatus = None):
        if not doneStatus:
            doneStatus = self.place.getDoneStatus()
        shardId = doneStatus['shardId']
        hoodId = doneStatus['hoodId']
        if shardId != None or hoodId != MyEstate:
            self.doneStatus = doneStatus
            messenger.send(self.doneEvent)
            return
        how = doneStatus['how']
        if how in ['tunnelIn',
         'teleportIn',
         'doorIn',
         'elevatorIn']:
            self.fsm.request('quietZone', [doneStatus])
        else:
            self.notify.error('Exited hood with unexpected mode %s' % how)
        return

    def handleQuietZoneDone(self):
        status = self.quietZoneStateData.getRequestStatus()
        self.fsm.request(status['where'], [status])

    def atMyEstate(self):
        if self.estateOwnerId != None:
            if self.estateOwnerId == base.localAvatar.getDoId():
                return 1
            else:
                return 0
        else:
            self.notify.warning("We aren't in an estate")
        return

    def setHouse(self, houseId):
        try:
            houseDo = base.cr.doId2do[houseId]
            self.enteredHouse = houseDo.house
        except KeyError:
            self.notify.debug("can't find house: %d" % houseId)

    def startCloudPlatforms(self):
        return
        if len(self.clouds):
            self.cloudTrack = self.__cloudTrack()
            self.cloudTrack.loop()

    def stopCloudPlatforms(self):
        if self.cloudTrack:
            self.cloudTrack.pause()
            del self.cloudTrack
            self.cloudTrack = None
        return

    def __cloudTrack(self):
        track = Parallel()
        for cloud in self.clouds:
            axis = cloud[1]
            pos = cloud[0].getPos(render)
            newPos = pos + axis * 30
            reversePos = pos - axis * 30
            track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos)))

        return track

    def debugGeom(self, decomposed):
        print 'numPrimitives = %d' % decomposed.getNumPrimitives()
        for primIndex in range(decomposed.getNumPrimitives()):
            prim = decomposed.getPrimitive(primIndex)
            print 'prim = %s' % prim
            print 'isIndexed = %d' % prim.isIndexed()
            print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives()
            for basicPrim in range(prim.getNumPrimitives()):
                print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim))
                print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim))

    def loadOnePlatform(self, version, radius, zOffset, score, multiplier):
        self.notify.debug('loadOnePlatform version=%d' % version)
        cloud = NodePath('cloud-%d-%d' % (score, multiplier))
        cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud')
        cc = cloudModel.copyTo(cloud)
        colCube = cc.find('**/collision')
        colCube.setName('cloudSphere-0')
        dTheta = 2.0 * math.pi / self.numClouds
        cloud.reparentTo(self.cloudOrigin)
        axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)]
        cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset)
        cloud.setScale(4.0)
        self.clouds.append([cloud, random.choice(axes)])

    def loadSkyCollision(self):
        plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 300)))
        plane.setTangible(0)
        planeNode = CollisionNode('cloudSphere-0')
        planeNode.addSolid(plane)
        self.cloudOrigin.attachNewNode(planeNode)

    def loadCloudPlatforms(self):
        self.cloudOrigin = self.geom.attachNewNode('cloudOrigin')
        self.cloudOrigin.setZ(30)
        self.loadSkyCollision()
        self.numClouds = 12
        pinballScore = PinballScoring[PinballCloudBumperLow]
        for i in range(12):
            self.loadOnePlatform(i, 40, 0, pinballScore[0], pinballScore[1])

        pinballScore = PinballScoring[PinballCloudBumperMed]
        for i in range(12):
            self.loadOnePlatform(i, 60, 40, pinballScore[0], pinballScore[1])

        pinballScore = PinballScoring[PinballCloudBumperHigh]
        for i in range(12):
            self.loadOnePlatform(i, 20, 80, pinballScore[0], pinballScore[1])

        self.cloudOrigin.stash()

    def setCloudSwitch(self, on):
        self.cloudSwitch = on
        if hasattr(self, 'cloudOrigin'):
            if on:
                self.cloudOrigin.unstash()
            else:
                self.cloudOrigin.stash()
