#!/usr/bin/python3
"""
This script converts a commentary or dictionary IMP source to a sword
module.  The internal IMP content is processed as plain text.  Some
files in the destination directories will be removed; acknowledge with
the '--force' option.

Module names should not exceed 15 characters in length.  In the case
of dictionary modules, the module name is detected from the last part
of the destination path.

Module conf file creation is not included.

Dependencies: python3, libsword-utils (specifically, imp2vs and
imp2ld).
"""

import tempfile, os, re, subprocess, sys, argparse

def parse_command():
    parser = argparse.ArgumentParser(description=globals()['__doc__'])
    group1 = parser.add_argument_group(title='other arguments')
    group1.add_argument('-f', '--force', required=True, \
                            action='store_true', \
                            help='Permit the removal of files in ' + \
                            'the destination directory')
    group2 = parser.add_mutually_exclusive_group(required=True)
    group2.add_argument('-c', '--commentary', action='store_true', \
                            help='Process a commentary source')
    group2.add_argument('-d', '--dictionary', action='store_true', \
                            help='Process a dictionary source ' + \
                            '(last part of destination path is used' + \
                            ' as module name)')
    parser.add_argument('source', type=argparse.FileType('r'), \
                            help='Source IMP file, or \'-\' for stdin')
    parser.add_argument('destination', type=dir_check, \
                            help='Destination folder for sword module;' \
                            + ' this would normally be like ' + \
                            '~/.sword/modules/comments/zcom/NAME/' + \
                            ' for commentaries or ' + \
                            '~/.sword/modules/lexdict/zld/NAME/' + \
                            ' for dictionaries')
    return(parser.parse_args())

def dir_check(string):
    """Check that string is a valid path for a directory."""
    path = os.path.abspath(string)
    if not os.path.isdir(path):
        msg = "%r is not a valid directory path" % string
        raise argparse.ArgumentTypeError(msg)
    return(path)

def create_temp_file():
    """Return path for a temporary file."""
    file = tempfile.mkstemp(suffix='.imp', prefix='swordmodgen.tmp.')
    return(file[1])

def delete_contents(path):
    """From directory at path, remove some files.

    Files having the following extensions are removed:
    czs, czv, czz, dat, idx, zdt, zdx
    """
    ext_del = ('.czs', '.czv', '.czz', '.dat', '.idx', '.zdt', '.zdx')
    for file in os.listdir(path):
        if file.endswith(ext_del):
            file_path = os.path.join(path, file)
            os.unlink(file_path)

def generate_osis_string(string):
    """From string, return simple OSIS single-line string."""
    string = re.sub(r'\(^\n*\|\n*$\n\)', '', string)
    string = re.sub(r'\n*$\n', '', string)
    if string == '':
        return('')
    string = re.sub(r'^', '<p>', string)
    string = re.sub(r'$', '</p>', string)
    string = re.sub(r'\n{2,}', '</p><p>', string)
    string = re.sub(r' *\n', ' ', string)
    return(string + '\n')

def generate_osis_imp(src, dest):
    """Convert IMP entries from plain text to simple OSIS.

    Keyword arguments:
    src -- file object of plain text IMP file
    dest -- file object of OSIS formatted IMP file

    Returns total number of entries processed, as integer.
    """
    count = 0
    md_lines = []
    key = re.compile('^\$\$\$.*$')
    blank = re.compile('^$')
    for line in src:
        if key.match(line):
            dest.write(generate_osis_string(''.join(md_lines)))
            md_lines = []
            dest.write(line)
            count += 1
        elif count == 0:
            pass
        else:
            md_lines.append(line)
    dest.write(generate_osis_string(''.join(md_lines)))
    return(count)

def generate_comm(src, dest):
    """Generate commentary from IMP source file.

    Keyword arguments:
    src = path to IMP src file
    dest = path to sword module directory

    """
    subprocess.call(['imp2vs', src, '-z', '-b', '3', '-o', dest], \
                        stdout=open('/dev/null', 'w'))

def generate_dict(src, dest):
    """Generate dictionary from IMP source file.

    The module name is determined from the last part of the
    destination path.

    Keyword arguments:
    src = path to IMP src file
    dest = path to sword module directory
    """
    os.chdir(dest)
    name = os.path.basename(os.path.abspath(dest))
    if name == '':
        name = generateddict
    subprocess.call(['imp2ld', src, name, 'z', '50'], \
                        stdout=open('/dev/null', 'w'))

if __name__ == '__main__':
    args = parse_command()
    if len(os.path.basename(os.path.abspath(args.destination))) > 15:
        sys.exit('Sword module name is longer than 15 characters ' + \
                     '(detected from last part of destination path)')
    imp_tmp = create_temp_file()
    with open(imp_tmp, 'w') as d:
        count = generate_osis_imp(args.source, d)
    args.source.close()
    delete_contents(args.destination)
    if args.commentary:
        generate_comm(imp_tmp, args.destination)
    else:
        generate_dict(imp_tmp, args.destination)
    os.remove(imp_tmp)
    print('Processed ' + str(count) + ' entries', file=sys.stderr)
