#!/usr/bin/env python

from __future__ import print_function

import sys
import os

# If this script is renamed to 'xml2rfc.py' on a Windows system, the import
# of the realy xml2rfc module will break.  In order to handle this, we remove
# the directory of the script file from the python system path:
script_dir = os.path.dirname(os.path.realpath(__file__))
if script_dir in sys.path:
    sys.path.remove(script_dir)

import optparse
import os
import xml2rfc
import lxml.etree
import datetime

major, minor = sys.version_info[:2]
if not major == 2 and minor >= 6:
    print ("")
    print ("The xml2rfc script requires python 2, with a version of 2.6 or higher.")
    print ("Can't proceed, quitting.")
    exit()

def display_version(self, opt, value, parser):
    print(xml2rfc.__version__)
    sys.exit()


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


def main():
    # Populate options
    formatter = optparse.IndentedHelpFormatter(max_help_position=40)
    optionparser = optparse.OptionParser(usage='xml2rfc SOURCE [options] '
                                        'FORMATS...\nExample: xml2rfc '
                                        'draft.xml -f Draft-1.0 --text --html',
                                        formatter=formatter)
    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 '
    #                        'errors and halt execution')
    optionparser.add_option('-n', '--no-dtd', dest='no_dtd', action='store_true',
                            help='disable DTD validation step')
    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', metavar='NAME',
                            help='specify the base name for output files')
    optionparser.add_option('-o', '--out', '-f', '--filename', dest='filename', metavar='FILE',
                            help='specify an explicit output filename (only '
                            'valid with a single format enabled)')
    optionparser.add_option('', '--date', dest='datestring', metavar='DATE',
                            default=datetime.datetime.today().strftime("%Y-%m-%d"),
                            help='run as if todays date is DATE (format: yyyy-mm-dd)')
    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([ o for o in [options.raw, options.text, options.nroff, options.html, options.exp] if o])
    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 as e:
                print('Unable to make cache directory: %s ' % options.cache)
                print(e)
                sys.exit(1)
        else:
            if not os.access(options.cache, os.W_OK):
                print('Cache directory is not writable: %s' % options.cache)
                sys.exit(1)
    options.date = datetime.datetime.strptime(options.datestring, "%Y-%m-%d").date()


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

    # Parse the document into an xmlrfc tree instance
    parser = xml2rfc.XmlRfcParser(source, verbose=options.verbose,
                                  quiet=options.quiet,
                                  cache_path=options.cache,
                                  templates_path=globals().get('_TEMPLATESPATH', None))
    try:
        xmlrfc = parser.parse()
    except xml2rfc.parser.XmlRfcError as e:
        xml2rfc.log.exception('Unable to parse the XML document: ' + args[0], e)
        sys.exit(1)
    except lxml.etree.XMLSyntaxError as e:
        # Give the lxml.etree.XmlSyntaxError exception a line attribute which
        # matches lxml.etree._LogEntry, so we can use the same logging function
        xml2rfc.log.exception('Unable to parse the XML document: ' + args[0], e.error_log)
        sys.exit(1)
        
    # Validate the document unless disabled
    if not options.no_dtd:
        ok, errors = xmlrfc.validate(dtd_path=options.dtd)
        if not ok:
            xml2rfc.log.exception('Unable to validate the XML document: ' + args[0], errors)
            sys.exit(1)

    # Execute any writers specified
    try:
        source_path, source_base = os.path.split(source)
        source_name, source_ext  = os.path.splitext(source_base)
        if options.basename:
            if os.path.isdir(options.basename):
                basename = os.path.join(options.basename, source_name)
            else:
                basename = options.basename
        else:
            # Create basename based on input
            basename = os.path.join(source_path, source_name)

        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, quiet=True)
            expwriter = xml2rfc.ExpandedXmlWriter(new_xmlrfc,
                                                  quiet=options.quiet,
                                                  verbose=options.verbose,
                                                  date=options.date)
            filename = options.filename
            if not filename:
                filename = basename + '.exp.xml'
            expwriter.write(filename)
        if options.html:
            htmlwriter = xml2rfc.HtmlRfcWriter(xmlrfc,
                                               quiet=options.quiet,
                                               verbose=options.verbose,
                                               date=options.date,
                                               templates_dir=globals().get('_TEMPLATESPATH', None))
            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,
                                                 date=options.date)
            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,
                                                         date=options.date)
            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,
                                                 date=options.date)
            filename = options.filename
            if not filename:
                filename = basename + '.nroff'
            nroffwriter.write(filename)
    except xml2rfc.RfcWriterError as e:
        xml2rfc.log.error('Unable to convert the document: ' + args[0],  
                          '\n  ' + e.msg)

if __name__ == '__main__':
    main()
