#!/usr/bin/env python


import optparse
import os
import os.path
import subprocess
import sys
import traceback


def vprint(verb, msg):
    if verb:
        print >>sys.stderr, msg


def call_process(args):
    p = subprocess.Popen(args, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE)
    output = p.communicate()
    output = (output[0].strip(), output[1].strip())
    return_code = p.returncode
    return output[0], output[1], return_code


def compile_docs(src_dir, dest_dir, cmd, ext, verb=False):
    vprint(verb, 'Compiling from {0} to {1}'.format(src_dir, dest_dir))
    if os.path.exists(dest_dir):
        if not os.path.isdir(dest_dir):
            raise RuntimeError('File exists at destination directory '
                '{0}'.format(dest_dir))
    else:
        os.makedirs(dest_dir)
    suffix = '.py'
    for f in os.listdir(src_dir):
        src = os.path.join(src_dir, f)
        if not os.path.isfile(src):
            continue
        dest = os.path.join(dest_dir, os.path.splitext(f)[0] + ext)
        if os.path.exists(dest) and not os.path.isfile(dest):
            # check_timestamps will have printed a warning earlier
            continue
        vprint(verb, 'Compiling {0} to {1}'.format(src, dest))
        try:
            stdout, stderr, ret = call_process([cmd + suffix, src, dest])
        except OSError, e:
            if e.errno == 13:
                # The .py version of the command was not found; try without it
                suffix = ''
                # Repeat the previous command
                stdout, stderr, ret = call_process([cmd + suffix, src, dest])
            else:
                raise
        if ret != 0:
            raise RuntimeError('Failed to compile {0} to {1}:\n{2}'.format(src,
                dest, stderr))


def compile_tex(src_dir, dest_dir, verb=False):
    vprint(verb, 'Compiling from {0} to {1}'.format(src_dir, dest_dir))
    if os.path.exists(dest_dir):
        if not os.path.isdir(dest_dir):
            raise RuntimeError('File exists at destination directory '
                '{0}'.format(dest_dir))
    else:
        os.makedirs(dest_dir)
    tex_files = [os.path.join(src_dir, f) for f in os.listdir(src_dir) \
            if os.path.splitext(f)[1] == '.tex']
    for f in tex_files:
        dest = os.path.join(dest_dir, os.path.splitext(f)[0] + '.pdf')
        vprint(verb, 'Compiling {0} to {1}'.format(f, dest))
        stdout, stderr, ret = call_process(['rubber', '-d', '--into',
            dest_dir, f])


def lang_dir(lang='en'):
    if lang == 'en':
        return ''
    elif lang == 'ja':
        return 'ja'


def common_dir():
    return 'common'


def rest_dir():
    return 'rest'


def man_dir():
    return 'man'


def html_dir():
    return 'html'


def tex_dir():
    return 'tex'


def pdf_dir():
    return 'pdf'


def generate_man(lang='en', verb=False):
    src = os.path.join(rest_dir(), lang_dir(lang))
    dest = os.path.join(man_dir(), lang_dir(lang), 'man1')
    vprint(verb, 'Generating man pages for language {0}'.format(lang))
    compile_docs(src, dest, 'rst2man', '.1', verb=verb)


def generate_html(lang='en', verb=False):
    src = os.path.join(rest_dir(), lang_dir(lang))
    dest = os.path.join(html_dir(), lang_dir(lang))
    vprint(verb, 'Generating HTML pages for language {0}'.format(lang))
    compile_docs(src, dest, 'rst2html', '.html', verb=verb)


def generate_pdf(lang='en', verb=False):
    src = os.path.join(rest_dir(), lang_dir(lang))
    tex = os.path.join(tex_dir(), lang_dir(lang))
    dest = os.path.join(pdf_dir(), lang_dir(lang))
    vprint(verb, 'Generating PDF documents for language {0}'.format(lang))
    compile_docs(src, tex, 'rst2latex', '.tex', verb=verb)
    compile_tex(tex, dest, verb=verb)


def generate_docs(doctype='man', lang='en', verb=False):
    if doctype == 'man':
        generate_man(lang=lang, verb=verb)
    elif doctype == 'html':
        generate_html(lang=lang, verb=verb)
    elif doctype == 'pdf':
        generate_pdf(lang=lang, verb=verb)
    else:
        raise ValueError('Unknown documentation type: {0}'.format(doctype))


def check_timestamps_in_dir(lang, src_dir, check_dir, verb=False):
    vprint(verb, 'Checking timestamps for directory {0}'.format(check_dir))
    for f in os.listdir(src_dir):
        src = os.path.join(src_dir, f)
        if not os.path.isfile(src):
            continue
        check = os.path.join(check_dir, f)
        if not os.path.isfile(check):
            print >>sys.stderr, 'File {0} missing for language {1}.'.format(f,
                    lang)
            continue
        src_time = os.path.getmtime(src)
        check_time = os.path.getmtime(check)
        if src_time > check_time:
            print >>sys.stderr, ('File {0} for language {1} is out of '
                'date.'.format(f, lang))


def check_timestamps(lang='en', verb=False):
    vprint(verb, 'Checking timestamps for language {0}'.format(lang))
    src_dir = os.path.join(rest_dir(), lang_dir('en'))
    check_dir = os.path.join(rest_dir(), lang_dir(lang))
    check_timestamps_in_dir(lang, src_dir, check_dir)
    common_src_dir = os.path.join(common_dir(), lang_dir('en'))
    common_check_dir = os.path.join(common_dir(), lang_dir(lang))
    check_timestamps_in_dir(lang, common_src_dir, common_check_dir)


def main(argv):
    usage = ('Usage:: %prog [options] [type]\n'
        'Generate documentation from the reStructuredText source files.\n'
        'All languages will be generated if none are specified.')
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('-e', dest='en', action='store_true', default=False,
            help='Generate the English documentation.')
    parser.add_option('-j', dest='ja', action='store_true', default=False,
            help='Generate the Japanese documentation.')
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
            default=False, help='Output verbose information.')

    try:
        options, args = parser.parse_args()
    except optparse.OptionError, e:
        print >>sys.stderr, 'OptionError:', e
        return 1
    if not args:
        args = ['man', 'html', 'pdf']

    if not options.en and not options.ja:
        langs = ['en', 'ja']
    else:
        langs = []
        if options.en:
            langs.append('en')
        if options.ja:
            langs.append('ja')
    print 'Languages: {0}'.format(langs)

    try:
        for l in langs:
            check_timestamps(l, verb=options.verbose)
            for doctype in args:
                generate_docs(doctype=doctype, lang=l, verb=options.verbose)
    except Exception, e:
        if options.verbose:
            traceback.print_exc()
        print >>sys.stderr, '{0}: {1}'.format(os.path.basename(sys.argv[0]), e)
        return 1
    return 0


if __name__ == '__main__':
    main(sys.argv)

