#!/usr/bin/python -tt
from __future__ import with_statement
import datetime
import argparse
import sys
import os

#output "constants"
LOG_FATAL  = 0
LOG_ERROR  = 1
LOG_WARN   = 2
LOG_ALWAYS = 2.5
LOG_NORMAL = 3
LOG_INFO   = 4
LOG_DEBUG  = 5

msgPrefixes = [
    "FATAL: ",
    "ERROR: ",
    "WARNING: ",
    "",
    "INFO: ",
    "DEBUG: "
]

#local variables
null = open(os.devnull, 'r+')
parser = argparse.ArgumentParser(description = 'Basis of my python scripts.')
arguments = None

def dieCleanly(level = None):
    """Exit the application with proper cleanup."""

    #TODO: perform application cleanup

    if level == None:
        level = LOG_ERROR

    #exit with appropriate value
    if level == LOG_FATAL:
        sys.exit(1)
    sys.exit(0)


def message(level, msg):
    global arguments
    """Print a message to a certain debug level"""
    retval = None

    if level <= arguments.logLevel or level == LOG_ALWAYS:
        out = sys.stdout

        # if logfile is open print to it instead
        if arguments.logFile == "-":
            out = sys.log
        elif level <= LOG_WARN:
            out = sys.stderr

        retval = msgPrefixes[int(level + 0.5)] + msg
        out.write(retval)
        retval = len(retval)

    if level <= LOG_FATAL:
        dieCleanly(level)

    return retval

def printUsage(msg = None):
    global parser
    if msg != None:
        message(LOG_FATAL, msg + parser.get_usage())
    message(LOG_ALWAYS, usage)
    dieCleanly(LOG_ALWAYS)

def parseArguments():
    global parser, arguments
    parser.add_argument('-l', '--log-file',
                        metavar = 'FILE', dest = 'logFile',
                        help = 'Specify a log to receive all messages.')
    parser.add_argument('-q', '--quiet',
                        action = 'count',
                        help = 'Decrease verbosity.')
    parser.add_argument('-v', '--verbose',
                        action = 'count',
                        help = 'Increase verbosity.')
    parser.add_argument('-a', '--append-to-spec',
                        metavar = 'SPECFILE',
                        help = 'Replace the changlog in the specified spec file with our content.')
    parser.add_argument('-d', '--debian-release',
                        metavar = 'DEBREL',
                        help = 'Output a debian changelog with this version used in the first entry.')
    parser.add_argument('-b', '--build',
                        metavar = 'BLDNUM',
                        help = 'Specify a build number to override the most recent entry.')
    parser.add_argument('--arg-file',
                        metavar = 'ARGFILE',
                        help = 'Specify a file that lists arguments to parse one set per line.')
    parser.add_argument('-o', '--output',
                        metavar = 'OUTPUT',
                        help = 'Where to put the output.')
    arguments = parser.parse_args()
    arguments.logLevel = LOG_NORMAL;
    if arguments.verbose:
        arguments.logLevel += arguments.verbose
    if arguments.quiet:
        arguments.logLevel -= arguments.quiet
        if arguments.logLevel <= LOG_FATAL:
            arguments.logLevel = LOG_FATAL
    if arguments.logFile == '-':
        arguments.logFile = None

parseArguments()
# open the log file if specified
if arguments.logFile != None:
    sys.log = open(arguments.logFile, "a", 1)
    arguments.logFile = "-"

entries = []
def readChangeLog():
    global entries
    
    with open(os.path.join(sys.path[0], 'ChangeLog')) as input:
        entry = {}
        inMessage = False
        for line in input:
            if not line:
                continue

            if 'message' in entry:
                if line == 'END\n':
                    entries.append(entry)
                    entry = {}
                else:
                    entry['message'] += line
            else:
                line = line.strip()
                if line:
                    parts = list(map(lambda a : a.strip(), line.split(':', 1)))
                    if len(parts) == 2:
                        if parts[0].lower() == 'version':
                            entry['version'] = parts[1].strip()
                        elif parts[0].lower() == 'author':
                            entry['author'] = parts[1].strip()
                        elif parts[0].lower() == 'when':
                            #Tue Mar 07 2006
                            #Tue, 21 Aug 2012 20:01:44 -0600
                            #
                            entry['whenIsShort'] = False
                            try:
                                entry['when'] = datetime.datetime.strptime(parts[1], '%a %b %d %Y')
                                entry['whenIsShort'] = True
                            except:
                                try:
                                    entry['when'] = datetime.datetime.strptime(parts[1].rsplit(None, 1)[0],
                                                                               '%a, %d %b %Y %H:%M:%S')
                                except:
                                    raise Exception('Failed to parse date string: %s' % parts[1])
                            entry['whenText'] = parts[1]
                        elif parts[0].lower() == 'debian':
                            debs = list(map(lambda a : a.strip(), parts[1].split(',')))
                            if len(debs) != 2:
                                raise Exception('Debian entry should look like: rel, bld: %s' % parts[1])
                            entry['debianRel'] = debs[0]
                            entry['debianBld'] = debs[1]
                        elif parts[0].lower() == 'fedora':
                            entry['fedoraBld'] = parts[1]
                        elif parts[0].lower() == 'message':
                            entry['message'] = ''
                        else:
                            raise Exception('Unknown field: %s' % parts[0])
                    elif line.startswith('END'):
                        break
                    else:
                        raise Exception('Unparsed line: %s' % line)

def execute(args):
    output = sys.stdout
    if args.output:
        output = open(args.output, 'w')

    if args.append_to_spec and args.debian_release:
        message(LOG_FATAL, 'Either write a spec file or a debian changelog.\n')
    elif args.append_to_spec:
        # pass the spec through until we reach the changlog
        with open(args.append_to_spec) as input:
            for line in input:
                output.write(line)
                if line.strip().lower() == '%changelog':
                    break

        # fedora format
        first = True
        for entry in entries:
            if first:
                if args.build:
                    entry = dict(entry)
                    entry['fedoraBld'] = args.build
                first = False

            entry['whenShort'] = entry['when'].strftime('%a %b %d %Y')
            lines = []
            for line in entry['message'].splitlines():
                if not line.startswith('  * '):
                    raise Exception('Unrecognized message format: %s' % line)
                else:
                    lines.append(line.strip()[2:])
            text = ('- ' + ', '.join(lines)).replace('\t', ' ')

            # wrap the text
            wrapped = []
            wrapAt = 72
            while text:
                if wrapAt >= len(text):
                    wrapped.append(text.strip())
                    text = ''
                else:
                    at = wrapAt
                    while text[at] != ' ':
                        at -= 1
                        if at == 10:
                            raise Exception('Cannot wrap text: %s' % text)
                    wrapped.append(text[:at].strip())
                    text = '  ' + text[at:]
            entry['oneWrapped'] = '\n  '.join(wrapped)

            output.write("""* %(whenShort)s %(author)s %(version)s-%(fedoraBld)s
%(oneWrapped)s

""" % entry)
    elif args.debian_release:
        # debian format
        first = True
        for entry in entries:
            if first:
                # override some fields of the first entry
                if args.debian_release:
                    entry = dict(entry)
                    entry['debianRel'] = args.debian_release
                if args.build:
                    entry = dict(entry)
                    entry['debianBld'] = args.build
                first = False

            if entry['whenIsShort']:
                entry['longWhen'] = entry['when'].strftime('%a, %d %b %Y %H:%M:%S -0500')
            else:
                entry['longWhen'] = entry['whenText']
            output.write("""iguanair (%(version)s-%(debianRel)s) %(debianRel)s; urgency=low

%(message)s
 -- %(author)s  %(longWhen)s

""" % entry)
    else:
        message(LOG_FATAL, 'Either write a spec file or a debian changelog: %s\n' % arguments)

    if output != sys.stdout:
        output.close()

readChangeLog()
if arguments.arg_file:
    with open(arguments.arg_file) as input:
        for line in map(lambda a : a.strip(), input):
            execute(parser.parse_args(line.split()))
    sys.exit()
else:
    execute(arguments)
