#!/usr/bin/env python

import sys
import optparse
import os
import shutil
import xml2rfc
import lxml.etree


def display_version(self, opt, value, parser):
    print('.'.join(map(str, xml2rfc.VERSION)))
    sys.exit()


def clear_cache(self, opt, value, parser):
    xml2rfc.parser.XmlRfcParser('').delete_cache()
    sys.exit()


def main():
    # Populate options
    optionparser = optparse.OptionParser(usage='%prog SOURCE [options] '
                                        'FORMATS...\nExample: %prog '
                                        'draft.xml -f Draft-1.0 --text --html')
    optionparser.add_option('-v', '--verbose', action='store_true',
                            dest='verbose', help='print extra information')
    optionparser.add_option('-q', '--quiet', action='store_true',
                            dest='quiet', help='dont print anything')
    optionparser.add_option('-w', '--warn-error', action='store_true',
                            dest='warn_error', help='treat warnings as '
                            'python exceptions.')
    optionparser.add_option('-c', '--cache', dest='cache', 
                            help='specify an alternate cache directory to write'
                            ' to')
    optionparser.add_option('-d', '--dtd', dest='dtd',
                            help='specify an alternate dtd file')
    optionparser.add_option('-b', '--basename', dest='basename',
                            help='specify the base name for output files')
    optionparser.add_option('-f', '--filename', dest='filename',
                            help='specify an explicit output filename (only '
                            'valid with a single format enabled)')
    optionparser.add_option('', '--clear-cache', action='callback',
                            help='purge the cache and exit',
                            callback=clear_cache)
    optionparser.add_option('', '--version', action='callback',
                            help='display the version number and exit',
                            callback=display_version)

    formatgroup = optparse.OptionGroup(optionparser, 'Formats', 'At least one'
                                       ' but as many as all of the following'
                                       ' output formats must be specified.'
                                       ' The destination filename will be based'
                                       ' on the input filename, unless an'
                                       ' argument was given to --basename.')
    formatgroup.add_option('', '--raw', dest='raw', action='store_true',
                           help='outputs to a text file, unpaginated')
    formatgroup.add_option('', '--text', dest='text', action='store_true',
                           help='outputs to a text file with proper page '
                           'breaks')
    formatgroup.add_option('', '--nroff', dest='nroff', action='store_true',
                           help='outputs to an nroff file')
    formatgroup.add_option('', '--html', dest='html', action='store_true',
                           help='outputs to an html file')
    formatgroup.add_option('', '--exp', dest='exp', action='store_true',
                           help='outputs to an XML file with all references'
                           ' expanded.')

    optionparser.add_option_group(formatgroup)

    # Parse and validate arguments
    (options, args) = optionparser.parse_args()
    if len(args) < 1:
        optionparser.print_help()
        sys.exit(2)
    source = args[0]
    if not os.path.exists(source):
        sys.exit('No such file: ' + source)
    num_formats = len(filter(bool, [options.raw, options.text, options.nroff,
                                    options.html, options.exp]))
    if num_formats > 1 and options.filename:
        sys.exit('Cannot give an explicit filename with more than one format, '
                 'use --basename instead.')
    if num_formats < 1:
        # Default to paginated text output
        options.text = True
    if options.cache:
        if not os.path.exists(options.cache):
            try:
                os.makedirs(options.cache)
                if options.verbose:
                    xml2rfc.log.write('Created cache directory at', 
                                      options.cache)
            except OSError, e:
                print 'Unable to make cache directory:', options.cache
                print e
                sys.exit(1)
        else:
            if not os.access(options.cache, os.W_OK):
                print 'Cache directory is not writable:', options.cache
                sys.exit(1)

    # Setup warnings module
    xml2rfc.log.warn_error = options.warn_error and True or False
    xml2rfc.log.quiet = options.quiet and True or False

    # Parse the document into an xmlrfc tree instance
    parser = xml2rfc.XmlRfcParser(source, verbose=options.verbose,
                                  quiet=options.quiet,
                                  cache_path=options.cache)
    try:
        xmlrfc = parser.parse()
    except (lxml.etree.XMLSyntaxError, xml2rfc.parser.XmlReferenceError), error:
        linestr = error.position[0] > 0 and 'Line ' + str(error.position[0]) + ': ' \
                  or ''
        xml2rfc.log.error('Unable to parse the XML document:', args[0],
                          '\n  ' + linestr + error.msg)
        sys.exit(1)
        
    # Validate the document
    ok, errors = xmlrfc.validate(dtd_path=options.dtd)
    if not ok:
        error_str = 'Unable to validate the XML document: ' + args[0]
        for error in errors:
            error_str += '\n  Line ' + str(error.line) + ': ' + error.message
        xml2rfc.log.error(error_str)
        sys.exit(1)

    # Execute any writers specified
    if options.basename:
        basename = options.basename
    else:
        # Create basename based on input
        basename = os.path.basename(source).rsplit('.', 1)[0]

    if options.exp:
        # Expanded XML writer needs a separate tree instance with
        # all comments and PI's preserved.  We can assume there are no
        # parse errors at this point since we didnt call sys.exit() during
        # parsing.
        new_xmlrfc = parser.parse(remove_comments=False)
        expwriter = xml2rfc.ExpandedXmlWriter(new_xmlrfc,
                                              quiet=options.quiet,
                                              verbose=options.verbose)
        filename = options.filename
        if not filename:
            filename = basename + '-expanded.xml'
        expwriter.write(filename)
    if options.html:
        htmlwriter = xml2rfc.HtmlRfcWriter(xmlrfc,
                                           quiet=options.quiet,
                                           verbose=options.verbose)
        filename = options.filename
        if not filename:
            filename = basename + '.html'
        htmlwriter.write(filename)
    if options.raw:
        rawwriter = xml2rfc.RawTextRfcWriter(xmlrfc,
                                             quiet=options.quiet,
                                             verbose=options.verbose)
        filename = options.filename
        if not filename:
            filename = basename + '-raw.txt'
        rawwriter.write(filename)
    if options.text:
        pagedwriter = xml2rfc.PaginatedTextRfcWriter(xmlrfc,
                                                     quiet=options.quiet,
                                                     verbose=options.verbose)
        filename = options.filename
        if not filename:
            filename = basename + '.txt'
        pagedwriter.write(filename)
    if options.nroff:
        nroffwriter = xml2rfc.NroffRfcWriter(xmlrfc,
                                             quiet=options.quiet,
                                             verbose=options.verbose)
        filename = options.filename
        if not filename:
            filename = basename + '.nroff'
        nroffwriter.write(filename)

if __name__ == '__main__':
    main()
