
# auto.py

"""
This module is an integeral part of the program
MMA - Musical Midi Accompaniment.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Bob van der Poel <bob@mellowood.ca>

"""


import os
import sys
import pickle

import MMA.midi
import gbl
import MMA.parse
from   MMA.common import *

grooveDir = {}
mmadir = ".mmaDB"    # constant, name of the lib database file
fileCount = 0
grooveCount = 0
gdDate = None
processedFiles = []
mkGrooveList = []

def updateGrooveList(n):
    """ Called from parse when new grooves are defined in a -g. """

    global mkGrooveList

    mkGrooveList.append(n)


def libUpdate():
    """ Update the mma library database file(s) with -g or -G option.

        This is called from the main program after the initialization
        and other option parsing. No RETURN.
    """

    global fileCount, gdDate, grooveDir, processedfiles

    print "Creating MMA groove directory database(s). Standby..."

    """ gbl.libPath points to one main directory tree which should include
        gbl.autoLib (defaults to 'stdlib'). We create a separate .mmaDB
        file for each directory found in the main tree. IE. if we have the
        directories stdlib and bvstuff we end up with stdlib/.mmaDB and
        bvstuff/.mmaDB.

    """

    for d in os.listdir(gbl.libPath):
        libpath = os.path.join(gbl.libPath, d)

        if not os.path.isdir(libpath):    # skip files, just process directories
            continue


        """ Attempt to read existing database
            There is a flag gbl.makeGrvDefs set to 0, 1, 2
            0 - there was no -g or -G so we're not here
            1 - -g - read existing database and update
            2 - -G - don't read existing, create new
        """

        grooveDir = {}
        gdDate = None

        if gbl.makeGrvDefs == 1:
            try:
                infile = os.path.join(libpath, mmadir)
                f=file(infile, "rb")
                f.readline()    # Read/discard comment line
                grooveDir = pickle.load(f)
                f.close()
                gdDate = os.path.getmtime(infile)
            except:
                pass

        dolibupdate(libpath, '')

        # Strip out defs of deleted (not found) files.

        for f in grooveDir.keys():
            if f not in processedFiles:
                print "       Deleting: %s" % f
                del grooveDir[f]

        try:
            outpath = file(os.path.join(libpath, mmadir), 'wb')
        except:
            error("Error creating lib-database file '%s'. CRITICAL!" % libpath)

        outpath.write("### mmaDB ... AUTOGENERATED BINARY DATA. "
                      "DO NOT EDIT!!!\n")
        pickle.dump(grooveDir, outpath, pickle.HIGHEST_PROTOCOL )
        outpath.close()

    print
    print "Database update complete."
    print "     Files processed: %s" % fileCount
    print "     Total number of grooves: %s" % grooveCount

    sys.exit(0)


def dolibupdate(root, subdir):
    """ Recursive function to read groove files in a directory. """

    global fileCount, grooveCount, gdDate, grooveDir, processedFiles, mkGrooveList

    if subdir == '.':
        print "Skipping: '.'"
        return

    if subdir:
        print "     Processing library directory '%s'." % subdir


    """ Get a list of the files in this directory. If the list
        includes a file called 'MMAIGNORE' the entire directory
        (and subdirs) is ignored. Otherwise, each file in the
        directory ending in 'mma' is parsed for groove defs.
    """

    p = os.path.join(root,subdir)
    dirfiles = os.listdir(p)

    if "MMAIGNORE" in dirfiles:
        print "Skipping: %s" % p
        return

    for fn in sorted(dirfiles):

        # Ignore hidden files and emacs auto-save and dead.

        if fn.startswith('.') or fn.startswith('#'):
            continue

        # Create full path name

        f=os.path.join(root, subdir, fn)

        if os.path.isdir(f):
            dolibupdate(root, os.path.join(subdir,fn))    # recursive!

        elif f.endswith(gbl.ext):
            ename = os.path.join(subdir, fn)

            processedFiles.append(ename)

            if gdDate and grooveDir.has_key(ename) and \
                    os.path.getmtime(f) < gdDate:
                print "       Existing: %s" % f
                grooveCount += len(grooveDir[ename])
                continue

            if grooveDir.has_key(ename):
                print "       Updating: %s" % f
            else:
                print "       Creating: %s" % f
            mkGrooveList = []
            gbl.mtrks = {}
            for c in gbl.midiAssigns.keys():
                gbl.midiAssigns[c]=[]
            for a,v in enumerate(gbl.midiAvail):
                gbl.midiAvail[a]=0
            gbl.mtrks[0]=MMA.midi.Mtrk(0)
            gbl.tnames = {}
            MMA.parse.parseFile(f)    # read current file, grab grooves

            fileCount += 1            # just so we can report to user
            grooveCount += len(mkGrooveList)

            grooveDir[ename]=mkGrooveList

        else:
            if not f.endswith(mmadir):
                print "       Ignoring: %s" % f



#################################################################


def loadGrooveDir(g):
    """ Try to auto-load a groove from the library.

        The compliation of all the MMADIR files is stored in the dict
        grooveDir{}.

        Check the main libpath directory for the MMADIR file. The
        names of the files and corresponding grooves are extracted.
        This is stored in a dictionary with the filename as the key
        and a list of grooves as the data.
    """

    global grooveDir


    """ If the global dict grooveDir is empty we first load the MMADIR info.
        We're assuming that not much goes wrong here...if we don't find
        the database we set grooveDir{} to a BS value to avoid future
        load attempts. The entire load is in a try, which means it either
        all works, or not ...
    """

    if not grooveDir:
        try:
            infile = os.path.join(gbl.libPath, gbl.autoLib, mmadir)
            f=file(infile, "rb")
            f.readline()    # Read/discard comment line
            grooveDir = pickle.load(f)
            f.close()
        except:
            grooveDir[0]=''


    """ Search the dict for a match. grooveDir{} is a dictionary
        for lists. Each dictionary key is filename (eg: "lib/rhumba.mma")
        and the list associated with it is a list of grooves defined
        in that file. Just a matter of stepping though the dict. and
        returning the proper filename.

        RETURN: Lib-Filename if found
                None if not found
    """

    for filename, namelist in grooveDir.items():
        if g in namelist:
            return filename

    return None



