from direct.directnotify.DirectNotifyGlobal import directNotify
import fnmatch
import os
import yaml
from panda3d.core import Multifile, Filename, VirtualFileSystem

APPLICABLE_FILE_PATTERNS = ('*.mf', 'ambience.yaml')
CONTENT_EXT_WHITELIST = ('.jpg', '.jpeg', '.rgb', '.png', '.ogg', '.ttf')

class ContentPackError(Exception):
    pass

class ContentPacksManager:
    notify = directNotify.newCategory('ContentPacksManager')
    notify.setInfo(True)

    def __init__(self, filepath='contentpacks/', sortFilename='sort.yaml'):
        self.filepath = filepath
        self.sortFilename = os.path.join(self.filepath, sortFilename)

        if __debug__:
            self.mountPoint = '../resources'
        else:
            self.mountPoint = '/'

        self.vfs = VirtualFileSystem.getGlobalPtr()

        self.sort = []
        self.ambience = {}

    def isApplicable(self, filename):
        """
        Returns whether or not the specified file is applicable.
        """
        # Does this file exist?
        if not os.path.exists(filename):
            return False

        # Does this file match one of the applicable file patterns?
        basename = os.path.basename(filename)
        for pattern in APPLICABLE_FILE_PATTERNS:
            if fnmatch.fnmatch(basename, pattern):
                return True

        return False

    def applyMultifile(self, filename):
        """
        Apply the specified multifile.
        """
        mf = Multifile()
        mf.openReadWrite(Filename(filename))

        # Discard content with non-whitelisted extensions:
        for subfileName in mf.getSubfileNames():
            ext = os.path.splitext(subfileName)[1]
            if ext not in CONTENT_EXT_WHITELIST:
                mf.removeSubfile(subfileName)

        self.vfs.mount(mf, self.mountPoint, 0)

    def applyAmbience(self, filename):
        """
        Apply the specified ambience configuration file.
        """
        with open(os.path.join(self.filepath, filename), 'r') as f:
            self.ambience.update(yaml.load(f) or {})

    def apply(self, filename):
        """
        Apply the specified content pack file.
        """
        self.notify.info('Applying %s...' % filename)
        basename = os.path.basename(filename)
        if basename.endswith('.mf'):
            self.applyMultifile(os.path.join(self.filepath, filename))
        elif basename == 'ambience.yaml':
            self.applyAmbience(filename)

    def applyAll(self):
        """
        Using the sort configuration, recursively apply all applicable content
        pack files under the configured content packs directory.
        """
        # First, read the sort configuration:
        self.readSortConfig()

        # Next, apply the sorted files:
        for filename in self.sort[:]:
            if self.isApplicable(os.path.join(self.filepath, filename)):
                self.apply(filename)
            else:
                self.notify.warning('Invalidating %s...' % filename)
                self.sort.remove(filename)

        # Apply the non-sorted files:
        for root, _, filenames in os.walk(self.filepath):
            root = root[len(self.filepath):]
            for filename in filenames:
                filename = os.path.join(root, filename).replace('\\', '/')

                # Ensure this file isn't sorted:
                if filename in self.sort:
                    continue

                # Ensure this file is applicable:
                if not self.isApplicable(os.path.join(self.filepath, filename)):
                    continue

                # Apply this file, and add it to the sort configuration:
                self.apply(filename)
                self.sort.append(filename)

        # Finally, write the new sort configuration:
        self.writeSortConfig()

    def readSortConfig(self):
        """
        Read the sort configuration.
        """
        if not os.path.exists(self.sortFilename):
            return
        with open(self.sortFilename, 'r') as f:
            self.sort = yaml.load(f) or []

    def writeSortConfig(self):
        """
        Write the sort configuration to disk.
        """
        with open(self.sortFilename, 'w') as f:
            for filename in self.sort:
                f.write('- %s\n' % filename)

    def getAmbience(self, group):
        """
        Returns the ambience configurations for the specified group.
        """
        return self.ambience.get(group, {})