
# TODO:
#   check for incomplete parts (missing views)
#   check if svgs are plain copies of already existing ones and link them instead?
#   check for conflicting names

# lots of borrowing from http://code.activestate.com/recipes/252508-file-unzip/

import getopt
import sys
import os
import os.path
import re
import zipfile


def usage():
    print("""
usage:
    fzpzclean.py -f [fzpz directory] -d [output directory] -o [core | contrib | user] (-r)

    -f   directory of input fzpzs
    -d   directory for output of the cleanup process
    -o   parent folder name, depending on fzp category
    -r   optional: take the fzpz filename to rename the fzp filename

Unzips fzpz files into the output directory and cleans up file names and references.
""")


def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hf:d:o:r", [
                                   "help", "fzpzs", "directory", "output", "rename"])
    except getopt.GetoptError as err:
        # print help information and exit:
        print(str(err))  # will print something like "option -a not recognized"
        usage()
        sys.exit(2)
    inputdir = None
    outputdir = None
    outputPrefix = None
    rename = False

    for o, a in opts:
        # print o
        # print a
        if o in ("-f", "--fzpzs"):
            inputdir = a
        elif o in ("-d", "--directory"):
            outputdir = a
        elif o in ("-o", "--output"):
            outputPrefix = a
        elif o in ("-r", "--rename"):
            rename = True
        elif o in ("-h", "--help"):
            usage()
            sys.exit(2)
        else:
            assert False, "unhandled option"

    if(not(inputdir)):
        usage()
        sys.exit(2)

    if(not(outputPrefix)):
        usage()
        sys.exit(2)

    if(not(outputdir)):
        usage()
        sys.exit(2)

    if not outputdir.endswith(':') and not os.path.exists(outputdir):
        os.mkdir(outputdir)

    for fn in os.listdir(inputdir):
        if fn.endswith('.fzpz'):
            print(fn)
            file = os.path.join(inputdir, fn)
            zf = zipfile.ZipFile(file)

            # create directory structure to house files
            createstructure(file, outputdir, outputPrefix)

            # record svg renamings and fzp location for fixing paths later
            svgrenames = []
            fzplocation = None

            # extract files to directory structure
            for i, name in enumerate(zf.namelist()):
                if not name.endswith('/'):

                    # identify file type
                    ending = name.split('.')[-1]
                    if ending == 'svg':
                        pass
                    elif ending == 'fzp':
                        pass
                    else:
                        print("WARNING wrong file type:", name)
                        return

                    # sort files into subdirectories
                    subdir = None
                    if ending == 'fzp':
                        subdir = outputPrefix
                    elif name.find('icon') >= 0:
                        subdir = 'svg/' + outputPrefix + '/icon'
                    elif name.find('pcb') >= 0:
                        subdir = 'svg/' + outputPrefix + '/pcb'
                    elif name.find('schem') >= 0:
                        subdir = 'svg/' + outputPrefix + '/schematic'
                    elif name.find('bread') >= 0:
                        subdir = 'svg/' + outputPrefix + '/breadboard'

                    # remove junk from filenames
                    outname = name
                    outname = re.sub(
                        '^svg\.((icon)|(breadboard)|(schematic)|(pcb))\.', '', outname, 1)
                    refname = outname  # this is how the svgs are referenced in the fzp
                    outname = re.sub('^part\.', '', outname, 1)
                    outname = re.sub('((__)|(_))[0-9a-fA-F]{32}', '', outname)
                    outname = re.sub('((__)|(_))[0-9a-fA-F]{27}', '', outname)
                    outname = re.sub('((__)|(_))[0-9a-fA-F]{20}', '', outname)
                    outname = re.sub(
                        '((__)|(_))((icon)|(breadboard)|(schematic)|(pcb))', '', outname)
                    # remove duplicate file endings
                    outname = re.sub('.' + ending, '', outname)
                    outname += '.' + ending

                    # optionally rename the fzp to that of the fzpz
                    if ending == 'fzp' and rename:
                        base = os.path.basename(zf.filename)
                        outname = os.path.splitext(base)[0]
                        outname += '.fzp'

                    # write new files
                    filelocation = os.path.join(outputdir, subdir, outname)
                    outfile = open(filelocation, 'wb')
                    outfile.write(zf.read(name))
                    outfile.flush()
                    outfile.close()

                    # store paths for reference update
                    if ending == 'svg':
                        svgrenames.append((refname, outname))
                    elif ending == 'fzp':
                        fzplocation = filelocation

            # update svg references in fzp
            fzpfile = open(fzplocation, 'r+')
            s = fzpfile.read()
            for svgre in svgrenames:
                if s.find(svgre[0]) == -1:
                    print("WARNING reference could not be found:", svgre[0])
                s = s.replace(svgre[0], svgre[1])
            fzpfile.seek(0)
            fzpfile.truncate()
            fzpfile.write(s)
            fzpfile.flush()
            fzpfile.close()
            zf.close()


def createstructure(file, dir, outputPrefix):
    # makedirs(listdirs(file), dir)
    dirs = [outputPrefix, 'svg/' + outputPrefix + '/icon', 'svg/' + outputPrefix +
            '/breadboard', 'svg/' + outputPrefix + '/schematic', 'svg/' + outputPrefix + '/pcb']
    makedirs(dirs, dir)


def makedirs(directories, basedir):
    """ Create any directories that don't currently exist """
    for dir in directories:
        curdir = os.path.join(basedir, dir)
        if not os.path.exists(curdir):
            os.makedirs(curdir)


def listdirs(file):
    """ Grabs all the directories in the zip structure
    This is necessary to create the structure before trying
    to extract the file to it. """
    zf = zipfile.ZipFile(file)

    dirs = []

    for name in zf.namelist():
        if name.endswith('/'):
            dirs.append(name)

    zf.close()
    dirs.sort()
    return dirs


if __name__ == "__main__":
    main()
