#!/usr/bin/python
# Read an NSIS installer script containing special commands GwyExpandFiles
# and GwyExpandDeleteAll and expand them.  The first is like File except that
# it is expanded to a literal list of files prepended with corresponding
# SetOutPath.  The second must come after the first and outputs commands to
# delete exactly the files and directories created by GwyExpandFiles and
# nothing else.
import sys, re, os, ntpath, glob

nargs = len(sys.argv)
if nargs > 2 or nargs == 2 and sys.argv[1] in ('-h', '--help'):
    print 'expandnsi [setup] <gwyddion.nsit >gwyddion.nsi'
    sys.exit(0)

if nargs == 2:
    setupfile = sys.argv[1]
else:
    setupfile = 'setup'

if not os.access(setupfile, os.R_OK):
    sys.stderr.write('Cannot find setup file %s.\n' % setupfile)
    sys.exit(1)

for line in file(setupfile):
    m = re.match(r'^(?P<name>\w+)=(?P<value>.*?)\s*$', line)
    if m:
        locals()[m.group('name')] = os.path.expandvars(m.group('value'))

for name in ('target_prefix',):
    if name not in locals():
        sys.stderr.write('Cannot find %s in %s.\n' % (name, setupfile))
        sys.exit(1)

if target_prefix.find('mingw32') >= 0:
    mingw='32'
elif target_prefix.find('mingw64') >= 0:
    mingw='64'
else:
    sys.stderr.write('Cannot guess 32/64bit from %s.\n' % target_prefix)
    sys.exit(1)

indent = r'^(?P<indent>\s*)'
delimited = r'(?P<delim>["\'])(?P<value>.*)(?P=delim)'
define_re = re.compile(indent + r'!define\s+(?P<name>\w+)\s+' + delimited + r'\s*$').match
expand_files_re = re.compile(indent + r'GwyExpandFiles\s+' + delimited + r'\s*$').match
expand_delete_all = re.compile(indent + r'GwyExpandDeleteAll\s*$').match
replace_arch = re.compile(r'%ARCH%').sub

defines = {}
content = []
files = []
directories = set()
current_path = None

def expand(string):
    def expand_one(match):
        name = match.group(1)
        if name in defines:
            return defines[name]
        sys.stderr.write('Line %u: Undefined macro ${%s} was expanded to empty string.\n' % (lineno, name))
        return ''

    return re.sub(r'\$\{(\w+)\}', expand_one, string)

for lineno, line in enumerate(sys.stdin):
    line = replace_arch('win' + mingw, line)
    m = expand_files_re(line)
    if m:
        fileglob = expand(m.group('value'))
        indent = m.group('indent')
        unix_fileglob = fileglob.replace('\\', '/')
        unix_filespec = os.path.join(target_prefix, unix_fileglob)
        if unix_fileglob.find('*') or unix_fileglob.find('?') or unix_fileglob.find('['):
            filelist = glob.glob(unix_filespec)
            if not filelist:
                sys.stderr.write('Line %u: File glob "%s" does not match anything.\n' % (lineno, fileglob))
        else:
            filelist = (unix_filespec,)

        for unix_filename in filelist:
            filename = unix_filename.replace('/', '\\')[len(target_prefix)+1:]
            files.append(filename)
            path = ntpath.dirname(filename)
            if path != current_path:
                sys.stdout.write('\r\n%sSetOutPath "$INSTDIR\\%s"\r\n' % (indent, path))
                current_path = path
                while path:
                    directories.add(path)
                    path = ntpath.dirname(path)
            sys.stdout.write('%sFile "%s"\r\n' % (indent, filename))
        continue

    m = expand_delete_all(line)
    if m:
        indent = m.group('indent')
        for filename in reversed(sorted(files)):
            sys.stdout.write('%sDelete "$INSTDIR\\%s"\r\n' % (indent, filename))
        paths = reversed(sorted(directories))
        for path in paths:
            sys.stdout.write('%sRMDir "$INSTDIR\\%s"\r\n' % (indent, path))
        continue

    sys.stdout.write(line)

    m = define_re(line)
    if m:
        name, value = m.group('name'), expand(m.group('value'))
        defines[name] = value

