#!/usr/bin/python2.6

##############################################################################
#
#    Copyright (c) 2010 Seath Solutions Ltd
#    All Rights Reserved
#    (http://www.seathsolutions.com/)
#
#    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 3 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, see <http://www.gnu.org/licenses/>.
#
##############################################################################
#
# 2010-01-11 Version 1.0

# Utility to process the output from Open ERP's base_module_record to make it readable.
# Operates in text domain, does not use XML parser.

import zipfile
import os, os.path
import sys
import string
import datetime
import tempfile
import shutil
import tkFileDialog
import ConfigParser

DEBUG=False

# This is the default configuration file. name. 
# It will be put in your home directory.
DEFCONF = '.humanise_zippedmodule.conf'
#
# These are default locations if you don't yet have a config file set
SRCDIR="~/Desktop"
HOMEFILE=""

def openzipr(f):
    """ Open the zipfile specified in f, readonly. 
    
    Stop if the file appears not to be a zipfile.
    
    """
    if not os.path.isfile(f):
        print("Zip file does not exist, stopping")
        return sys.exit(-1)
    if not os.path.splitext(f)[1] == '.zip':
        print("File does not have zip file extension, stopping")
        return sys.exit(-1)       
    if not zipfile.is_zipfile(f):
        print("File is not a valid zip file, stopping")
        return sys.exti(-1)
    zf = zipfile.ZipFile(f, 'r')
    return zf

def openzipw(f):
    """ Create zipfile specified in f, readonly. Stop if the file exists.
    """
    if os.path.isfile(f):
        print("Zip file already exists, stopping")
        return sys.exit(-1)
    if not os.path.splitext(f)[1] == '.zip':
        print("File does not have zip file extension, stopping")
        return sys.exit(-1)       
    zf = zipfile.ZipFile(f, 'w')
    return zf

def closezip(f):
    """ Close the original zipfile specified in f.
    """
    f.close()

def createdst(dstdir):
    """ Create the destination directory to put zip contents in. 
    
    Stop if the directory already exists.
    
    """
    if os.path.isdir(dstdir):
        print("Output directory already exists, stopping")
        return sys.exit(-1)
    os.mkdir(dstdir)

def makezip(srcdir, dstfile):
    """ Make a zip file from the source directory.
    """
    return

def processinitfile(src, name, dst):
    """ Process the __init__.py file by appending a comment.
    
    To show that this is a processed module.
    
    """
    terpfilein = src.read(name)
    terpfileout = ''
    for line in terpfilein.splitlines():
        terpfileout += line + "\n"
    terpfileout += "#\n# Processed by " + sys.argv[0] + " from Seath Solutions Ltd on " + datetime.datetime.now().isoformat() + "\n#\n"
    fname = os.path.join(dst, name)
    if os.path.isfile(fname):
        print("File already exists, stopping")
        return sys.exit(-1)
    f = open(fname, 'w')
    f.write(terpfileout)
    f.close()

def processterpfile(src, name, dst):
    """ Process the __terp__.py file by incrementing the version number.
    
    Or adding a new one if there's none there.
    
    """
    terpfilein = src.read(name)
    terpfileout1 = ''
    newversionstring = ''
    for line in terpfilein.splitlines():
        if string.find(line, "version") >= 0 and string.find(line, ":") >= 0:
            versionlinelist = string.split(line, ":")
            oldversionstring = string.strip(versionlinelist[1], "',\" \t")
            try:
                v = float(oldversionstring)
                v += 0.1
                newversionstring = str(v)
            except:
                newversionstring = oldversionstring + ".1"
            terpfileout1 += line[:string.find(line, ":")+1] + ' "' + \
                            newversionstring + '",\n' 
        else:
            terpfileout1 += line + "\n"
    terpfileout2 = ''
    for line in terpfileout1.splitlines():
        terpfileout2 += line + '\n'
        if newversionstring == '' and string.find(line, '"name"') >= 0:
            terpfileout2 += line[:string.find(line, '"') + 1] + \
                            '"version" : "0.1"\n'
    fname = os.path.join(dst, name)
    if os.path.isfile(fname):
        print("File already exists, stopping")
        return sys.exit(-1)
    f = open(fname, 'w')
    f.write(terpfileout2)
    f.close()
    return newversionstring

def processxmlfile(src, name, dst):
    """ Process any.xml file by moving the name attribute forward.
    
    And moving certain eval attributes.
    
    """
    terpfilein = src.read(name)
    terpfileout = ''
    for line in terpfilein.splitlines():
        # always add at least one space before any '/>' sequence
        i0 = string.find(line, '/>')
        if i0 >= 0:
            line = line[:i0] + ' ' + line[i0:]
        # move name to first attribute after field
        i1 = string.find(line, '<field ')    # index of field, if it's there
        len1 = len('<field ')
        i2 = string.find(line, 'name=', i1)    # index of "name"
        linepieces = string.split(line)
        for piece in linepieces:
            if string.find(piece, 'name=') >= 0:
                len2 = len(piece)
                break
        if i1 >= 0:
            if i2 >= 0:
                if DEBUG: print("1>"+line+"<")
                linewithoutname = line[:i2] + line[i2+len2+1:]
                if DEBUG: print("2>"+linewithoutname+"<")
                linewithnameadded = linewithoutname[:i1+len1] + piece + linewithoutname[i1+len1-1:]
                if DEBUG: print("3>"+linewithnameadded+"<")
                line = linewithnameadded
        # replace all 'eval="&quot;&quot;&quot;<words>&quot;&quot;&quot;" sequences
        if i1 >= 0:
            i10 = string.find(line, 'eval=')
            i11 = string.find(line, '"', i10)
            i12 = string.find(line, '"', i11+1)
            if i10 >= 0:
                evalpiece = line[i11:i12]
                i13 = string.find(evalpiece, "&quot;&quot;&quot;")
                i14 = string.find(evalpiece, "&quot;&quot;&quot;", i13+1)
                if i13 >= 0 and i14 >= 0:
                    evalstring = evalpiece[len("&quot;&quot;&quot;")+1:-len("&quot;&quot;&quot;")]
                    # extract evalpiece from xml line
                    linewithouteval = line[:i10] + line[i12+1:]
                    if DEBUG: print("4>"+linewithouteval+"<") 
                    linepieces = string.split(linewithouteval)
                    firstpartlinewithouteval = linewithouteval[:-len(linepieces[-1])]
                    lineeval = firstpartlinewithouteval + ">" + evalstring + "</field>"
                    if DEBUG: print("5>"+lineeval+"<")                     
                    line = lineeval
        terpfileout += line + "\n"
    fname = os.path.join(dst, name)
    if os.path.isfile(fname):
        print("File already exists, stopping")
        return sys.exit(-1)
    f = open(fname, 'w')
    f.write(terpfileout)
    f.close()

def extractmembers(src):
    """ Extract each member of the zip file to an appropriate directory.
    
    On the filesystem, processing some specific members.
    """
    topdir = ''
    for name in src.namelist():
        direlements = string.split(name, os.sep)
        if topdir == '':
            topdir = direlements[0]
        elif topdir != direlements[0]:
            print("Top directory in zip file is inconsistent, stopping")
            sys.exit(-1)
    if topdir == '':
        print("Top directory empty, stopping.")
        sys.exit(-1)
    else:
        print("Top directory is " + topdir)
    dstdir = tempfile.mkdtemp()
    zipdir = os.path.join(dstdir, topdir)
    # Make a temporary directory to put everything in
    createdst(zipdir)
    if DEBUG: print("JOINED " + os.path.join(topdir, '__terp__.py'))
    for name in src.namelist():
        if string.find(os.path.normpath(name), os.path.join(topdir, '__init__.py')) >= 0:
            print("Process __init__.py")
            processinitfile(src, name, dstdir)            
        elif string.find(os.path.normpath(name), os.path.join(topdir, '__terp__.py')) >= 0:
            print("Process __terp__.py")
            version = processterpfile(src, name, dstdir)            
        elif os.path.splitext(name)[1] == '.xml':
            print("Process xml file " + name)
            processxmlfile(src, name, dstdir)            
        else:
            src.extract(name, dstdir)
    return dstdir, topdir, version

def getinputfile(srcdir, srcfile):
    """ Return a src filename or ''. Start at srcpath.
    """
    filepath = tkFileDialog.askopenfilename(
                        initialdir=srcdir, 
                        initialfile=srcfile,
                        title="Find Open ERP Module .zip file to open", 
                        filetypes=(('Zip Files','*.zip'),),
                        )
    if filepath == None:
        print("Input file not specified, stopping")
        sys.exit(-1)
    return filepath

def saveoutputfile(srcdir1, srcdir2, config, dstfile):
    """ Save srcpath to dstpath.
    """
    dstdir = config.get('Location','lastoutputdir')
    srcdir = os.path.join(srcdir1, srcdir2)
    initialdstfile = os.path.join(dstdir, dstfile)
    filepath = tkFileDialog.asksaveasfilename(
                        initialdir=dstdir, 
                        initialfile=dstfile,
                        title="Create new Open ERP Module .zip file", 
                        filetypes=(('Zip Files','*.zip'),),
                        )
    if filepath == None:
        print("Output file not specified, stopping")
        sys.exit(-1)
    if os.path.exists(filepath):
        print("Proposed output file exists, stopping (even if you thought you could overwrite it).")
        sys.exit(-1)
    if not os.path.exists(srcdir):
        print("Source directory doesn't exist, stopping")
        sys.exit(-1)
    if not os.path.isdir(srcdir):
        print("Source isn't a directory, stopping")
        sys.exit(-1)
    config.set('Location','lastoutputdir', os.path.split(filepath)[0])
    # create a new zipfile for output
    f = openzipw(filepath)
    # for file in src, write it to zip
    for root, dirs, files in os.walk(srcdir1):
        for file in files:
            # extract archive name from file name
            if DEBUG: print "zzzz", root, "zzz", root[len(srcdir1)+1:], "zz", file, "z" 
            f.write(os.path.join(root, file), os.path.join(root[len(srcdir1)+1:], file))
    # close the zipfile cleanly
    f.close()
    # delete unzipped version in its temporary location
    shutil.rmtree(srcdir1, True)

# =====================================================================

# Check Python version and exit if it doesn't meet requirements
if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 6):
    print("Python version is less than 2.6, which is needed to run, stopping.")
    print sys.version
    sys.exit(-1)

# Get config file if it exists, set defaults otherwise
homedir = os.getenv("USERPROFILE") or os.getenv("HOME")
defconf = os.path.join(homedir, DEFCONF)

config = ConfigParser.RawConfigParser()
if os.path.exists(defconf):
    config.readfp(open(defconf))
    lastinputdir = config.get('Location','lastinputdir')
    lastinputfile = config.get('Location','lastinputfile')
    lastoutputdir = config.get('Location','lastoutputdir')
else:
    config.add_section('Location')
    lastinputdir = SRCDIR
    lastinputfile = HOMEFILE
    lastoutputdir = SRCDIR

# Get a location for the input file and save the configuration 
# you choose for use next time. 
filename = getinputfile(lastinputdir, lastinputfile)
if filename == '':
    print("No file specified, stopping")
    sys.exit(-1)
else:
    lastinputdir, lastinputfile = os.path.split(filename)
    config.set('Location', 'lastinputdir', lastinputdir)
    config.set('Location', 'lastinputfile', lastinputfile)
    
# Open the zip file for reading. Print the contents of the zip you find.
f = openzipr(filename)
f.printdir()

# Get some parameters then close the source zip fule
maindir, topdir, version = extractmembers(f)
closezip(f)

# Create a default for the output filename using the version you find inside
defaultoutputfilename = topdir + "-" + version + ".zip"

# Set the output directory in case there's nothing in lastoutputdir yet
config.set('Location', 'lastoutputdir', lastoutputdir)

# Save the output file in a zip file
saveoutputfile(maindir, topdir, config, defaultoutputfilename)

configfile = open(defconf, 'wb')
config.write(configfile)
