"""Loader module: contains the Loader class"""

from pandac.PandaModules import *
from direct.directnotify.DirectNotifyGlobal import *

# You can specify a phaseChecker callback to check
# a modelPath to see if it is being loaded in the correct
# phase
phaseChecker = None

class Loader:
    """
    Load models, textures, sounds, and code.
    """
    notify = directNotify.newCategory("Loader")
    modelCount = 0
    
    # special methods
    def __init__(self, base):
        self.base = base
        self.loader = PandaLoader()

    def destroy(self):
        del self.base
        del self.loader
        
    # model loading funcs
    def loadModel(self, modelPath, fMakeNodeNamesUnique = 0):
        """
        modelPath is a string.

        Attempt to load a model from given file path, return
        a nodepath to the model if successful or None otherwise."""
        assert(Loader.notify.debug("Loading model: %s" % (modelPath) ))
        if phaseChecker:
            phaseChecker(modelPath)
        node = self.loader.loadSync(Filename(modelPath))
        if (node != None):
            nodePath = NodePath(node)
            if fMakeNodeNamesUnique:
                self.makeNodeNamesUnique(nodePath, 0)
        else:
            nodePath = None
        return nodePath

    def loadModelOnce(self, modelPath):
        """
        modelPath is a string.

        Attempt to load a model from modelPool, if not present
        then attempt to load it from disk. Return a nodepath to
        the model if successful or None otherwise"""
        assert(Loader.notify.debug("Loading model once: %s" % (modelPath)))
        if phaseChecker:
            phaseChecker(modelPath)
        node = ModelPool.loadModel(modelPath)
        if (node != None):
            nodePath = NodePath.anyPath(node)
        else:
            nodePath = None
        return nodePath

    def loadModelOnceUnder(self, modelPath, underNode):
        """loadModelOnceUnder(self, string, string | node | NodePath)
        Behaves like loadModelOnce, but also implicitly creates a new
        node to attach the model under, which helps to differentiate
        different instances.

        underNode may be either a node name, or a NodePath or a Node
        to an already-existing node.

        This is useful when you want to load a model once several
        times before parenting each instance somewhere, or when you
        want to load a model and immediately set a transform on it.
        But also consider loadModelCopy().
        """
        assert(Loader.notify.debug("Loading model once: %s under %s" % (modelPath, underNode)))
        if phaseChecker:
            phaseChecker(modelPath)
        node = ModelPool.loadModel(modelPath)
        if (node != None):
            nodePath = NodePath(underNode)
            nodePath.attachNewNode(node)
        else:
            nodePath = None
        return nodePath
    
    def loadModelCopy(self, modelPath):
        """loadModelCopy(self, string)
        Attempt to load a model from modelPool, if not present
        then attempt to load it from disk. Return a nodepath to
        a copy of the model if successful or None otherwise"""
        assert(Loader.notify.debug("Loading model copy: %s" % (modelPath)))
        if phaseChecker:
            phaseChecker(modelPath)
        node = ModelPool.loadModel(modelPath)
        if (node != None):
            return NodePath(node.copySubgraph())
        else:
            return None

    def loadModelNode(self, modelPath):
        """
        modelPath is a string.
        
        This is like loadModelOnce in that it loads a model from the
        modelPool, but it does not then instance it to hidden and it
        returns a Node instead of a NodePath.  This is particularly
        useful for special models like fonts that you don't care about
        where they're parented to, and you don't want a NodePath
        anyway--it prevents accumulation of instances of the font
        model under hidden.

        However, if you're loading a font, see loadFont(), below.
        """
        assert(Loader.notify.debug("Loading model once node: %s" % (modelPath)))
        if phaseChecker:
            phaseChecker(modelPath)
        return ModelPool.loadModel(modelPath)

    def unloadModel(self, modelPath):
        """
        modelPath is a string.
        """
        assert(Loader.notify.debug("Unloading model: %s" % (modelPath)))
        ModelPool.releaseModel(modelPath)

    # font loading funcs
    def loadFont(self, modelPath,
                 spaceAdvance = None, pointSize = None,
                 pixelsPerUnit = None, scaleFactor = None,
                 textureMargin = None, polyMargin = None,
                 minFilter = None, magFilter = None,
                 anisotropicDegree = None,
                 lineHeight = None):
        """
        modelPath is a string.

        This loads a special model as a TextFont object, for rendering
        text with a TextNode.  A font file must be either a special
        egg file (or bam file) generated with egg-mkfont, or a
        standard font file (like a TTF file) that is supported by
        FreeType.
        """
        assert(Loader.notify.debug("Loading font: %s" % (modelPath)))
        if phaseChecker:
            phaseChecker(modelPath)

        font = FontPool.loadFont(modelPath)
        if font == None:
            # If we couldn't load the model, at least return an
            # empty font.
            font = StaticTextFont(PandaNode("empty"))

        # The following properties may only be set for dynamic fonts.
        if hasattr(font, "setPointSize"):
            if pointSize != None:
                font.setPointSize(pointSize)
            if pixelsPerUnit != None:
                font.setPixelsPerUnit(pixelsPerUnit)
            if scaleFactor != None:
                font.setScaleFactor(scaleFactor)
            if textureMargin != None:
                font.setTextureMargin(textureMargin)
            if polyMargin != None:
                font.setPolyMargin(polyMargin)
            if minFilter != None:
                font.setMinFilter(minFilter)
            if magFilter != None:
                font.setMagFilter(magFilter)
            if anisotropicDegree != None:
                font.setAnisotropicDegree(anisotropicDegree)

        if lineHeight is not None:
            # If the line height is specified, it overrides whatever
            # the font itself thinks the line height should be.  This
            # and spaceAdvance should be set last, since some of the
            # other parameters can cause these to be reset to their
            # default.
            font.setLineHeight(lineHeight)

        if spaceAdvance is not None:
            font.setSpaceAdvance(spaceAdvance)

        return font

    # texture loading funcs
    def loadTexture(self, texturePath, alphaPath = None):
        """
        texturePath is a string.

        Attempt to load a texture from the given file path using
        TexturePool class. Returns None if not found
        """
        if alphaPath is None:
            assert(Loader.notify.debug("Loading texture: %s" % (texturePath) ))
            if phaseChecker:
                phaseChecker(texturePath)
            texture = TexturePool.loadTexture(texturePath)
        else:
            assert(Loader.notify.debug("Loading texture: %s %s" % (texturePath, alphaPath) ))
            if phaseChecker:
                phaseChecker(texturePath)
            texture = TexturePool.loadTexture(texturePath, alphaPath)
        return texture

    def unloadTexture(self, texture):
        assert(Loader.notify.debug("Unloading texture: %s" % (texture) ))
        TexturePool.releaseTexture(texture)

    # sound loading funcs
    def loadSfx(self, name):
        assert(Loader.notify.debug("Loading sound: %s" % (name) ))
        if phaseChecker:
            phaseChecker(name)
        # should return a valid sound obj even if soundMgr is invalid
        sound = None
        if (name):
            # showbase-created sfxManager should always be at front of list
            sound=base.sfxManagerList[0].getSound(name)
        if sound == None:
            Loader.notify.warning("Could not load sound file %s." % name)
        return sound

    def loadMusic(self, name):
        assert(Loader.notify.debug("Loading sound: %s" % (name) ))
        # should return a valid sound obj even if musicMgr is invalid
        sound = None
        if (name):
            sound=base.musicManager.getSound(name)
        if sound == None:
            Loader.notify.warning("Could not load music file %s." % name)
        return sound


    def makeNodeNamesUnique(self, nodePath, nodeCount):
        if nodeCount == 0:
            Loader.modelCount += 1
        nodePath.setName(nodePath.getName() +
                         ('_%d_%d' % (Loader.modelCount, nodeCount)))
        for i in range(nodePath.getNumChildren()):
            nodeCount += 1
            self.makeNodeNamesUnique(nodePath.getChild(i), nodeCount)

