#!/usr/bin/python3
#
# yelp-new
# Copyright (C) 2010-2020 Shaun McCance <shaunm@gnome.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import configparser
import datetime
import os
import subprocess
import sys


DATADIR = '/usr/share/yelp-tools'

class YelpNew:
    arguments = [
        ('help', '-h', None, 'Show this help and exit'),
        ('stub', None, None, 'Create a stub file with .stub appended'),
        ('tmpl', None, None, 'Copy an installed template to a local template'),
        ('version', '-v', 'VERS', 'Specify the version number to substitute')
    ]

    def __init__(self):
        self.options = {}
        self.fileargs = []
        self.parse_args(sys.argv[1:])
        self.config = configparser.ConfigParser()
        try:
            self.config.read('.yelp-tools.cfg')
        except:
            self.config = None


    def parse_args(self, args):
        while len(args) > 0:
            argdef = None
            if args[0].startswith('--'):
                for arg_ in self.arguments:
                    if args[0] == '--' + arg_[0]:
                        argdef = arg_
                        break
                if argdef is None:
                    self.print_usage()
                    return 1
            elif args[0].startswith('-'):
                for arg_ in self.arguments:
                    if args[0] == arg_[1]:
                        argdef = arg_
                        break
                if argdef is None:
                    self.print_usage()
                    return 1
            if argdef is not None:
                takesarg = (argdef[2] is not None)
                if takesarg:
                    if len(args) < 2:
                        self.print_usage()
                        return 1
                    self.options.setdefault(argdef[0], [])
                    self.options[argdef[0]].append(args[1])
                    args = args[2:]
                else:
                    self.options[argdef[0]] = True
                    args = args[1:]
            else:
                self.fileargs.append(args[0])
                args = args[1:]


    def get_option_bool(self, arg):
        if arg in self.options:
            return self.options[arg] == True
        if self.config is not None:
            val = self.config.get('new', arg, fallback=None)
            if val is not None:
                return (val == 'true')
            val = self.config.get('default', arg, fallback=None)
            if val is not None:
                return (val == 'true')
        return False


    def get_option_str(self, arg):
        if arg in self.options:
            if isinstance(self.options[arg], list):
                return self.options[arg][-1]
        if self.config is not None:
            val = self.config.get('new', arg, fallback=None)
            if val is not None:
                return val
            val = self.config.get('default', arg, fallback=None)
            if val is not None:
                return val
        return None


    def get_replacements(self, pageid):
        repl = {'ID' : pageid}
        if len(self.fileargs) > 2:
            repl['TITLE'] = ' '.join(self.fileargs[2:])
        else:
            repl['TITLE'] = 'TITLE'
        today = datetime.datetime.now()
        repl['DATE'] = today.strftime('%Y-%m-%d')
        repl['YEAR'] = today.strftime('%Y')

        username = None
        useremail = None
        isgit = False
        isbzr = False
        cwd = os.getcwd()
        while cwd:
            if os.path.exists(os.path.join(cwd, '.git')):
                isgit = True
                break
            if os.path.exists(os.path.join(cwd, '.bzr')):
                isbzr = True
                break
            newcwd = os.path.dirname(cwd)
            if newcwd == cwd:
                break
            cwd = newcwd
        if isbzr:
            try:
                who = subprocess.run(['bzr', 'whoami'], check=True,
                                     capture_output=True, encoding='utf8')
                username, useremail = who.stdout.split('<')
                username = username.strip()
                useremail = useremail.split('>')[0].strip()
            except:
                username = None
                useremail = None
        if username is None:
            try:
                who = subprocess.run(['git', 'config', 'user.name'], check=True,
                                     capture_output=True, encoding='utf8')
                username = who.stdout.strip()
                who = subprocess.run(['git', 'config', 'user.email'], check=True,
                                     capture_output=True, encoding='utf8')
                useremail = who.stdout.strip()
            except:
                username = None
                useremail = None
        repl['NAME'] = username or 'YOUR NAME'
        repl['EMAIL'] = useremail or 'YOUR EMAIL ADDRESS'
        repl['VERSION'] = self.get_option_str('version') or 'VERSION.NUMBER'
        return repl


    def main(self):
        if len(self.fileargs) < 2:
            self.print_usage()
            return 1

        tmpl = self.fileargs[0]
        if '.' not in tmpl:
            tmpl = tmpl + '.page'
            ext = '.page'
        elif tmpl.endswith('.page'):
            ext = '.page'
        elif tmpl.endswith('.duck'):
            ext = '.duck'
        if self.get_option_bool('stub'):
            ext = ext + '.stub'
        tmplfile = os.path.join(os.getcwd(), tmpl + '.tmpl')
        if not os.path.exists(tmplfile):
            tmplfile = os.path.join(DATADIR, 'templates', tmpl)
            if not os.path.exists(tmplfile):
                print('No template found named ' + tmpl, file=sys.stderr)
                sys.exit(1)
        pageid = self.fileargs[1]
        istmpl = self.get_option_bool('tmpl')
        if istmpl:
            ext = ext + '.tmpl'
            repl = {}
        else:
            repl = self.get_replacements(pageid)
        def _writeout(outfile, infilename, depth=0):
            if depth > 10:
                # We could do this smarter by keeping a stack of infilenames, but why?
                print('Recursion limit reached for template includes', file=sys.stderr)
                sys.exit(1)
            for line in open(infilename):
                if (not istmpl) and line.startswith('<?yelp-tmpl-desc'):
                    continue
                if (not istmpl) and line.startswith('[-] yelp-tmpl-desc'):
                    continue
                while line is not None and '{{' in line:
                    before, after = line.split('{{', maxsplit=1)
                    if '}}' in after:
                        var, after = after.split('}}', maxsplit=1)
                        outfile.write(before)
                        isinclude = var.startswith('INCLUDE ')
                        if isinclude:
                            newfile = os.path.join(os.path.dirname(infilename), var[8:].strip())
                            _writeout(outfile, newfile, depth=depth+1)
                        elif istmpl:
                            outfile.write('{{' + var + '}}')
                        else:
                            outfile.write(repl.get(var, '{{' + var + '}}'))
                        if isinclude and after == '\n':
                            line = None
                        else:
                            line = after
                    else:
                        outfile.write(line)
                        line = None
                if line is not None:
                    outfile.write(line)

        if os.path.exists(pageid + ext):
            print('Output file ' + pageid + ext + ' already exists', file=sys.stderr)
            sys.exit(1)
        with open(pageid + ext, 'w') as outfile:
            _writeout(outfile, tmplfile)


    def print_usage(self):
        print('Usage: yelp-new [OPTIONS] <TEMPLATE> <ID> [TITLE]\n')
        print('Create a new file from an installed or local template file,\n' +
              'or create a new local template. TEMPLATE must be the name of\n' +
              'an installed or local template. ID is a page ID (and base\n' +
              'filename) for the new page. The optional TITLE argument\n'
              'provides the page title\n')
        print('Options:')
        maxarglen = 2
        args = []
        for arg in self.arguments:
            argkey = '--' + arg[0]
            if arg[1] is not None:
                argkey = arg[1] + ', ' + argkey
            if arg[2] is not None:
                argkey = argkey + ' ' + arg[2]
            args.append((argkey, arg[3]))
        for arg in args:
            maxarglen = max(maxarglen, len(arg[0]) + 1)
        for arg in args:
            print('  ' + (arg[0]).ljust(maxarglen) + '  ' + arg[1])
        localpages = []
        localducks = []
        installedpages = []
        installedducks = []
        descs = {}
        maxlen = 0
        def _getdesc(fpath):
            for line in open(fpath):
                if line.startswith('<?yelp-tmpl-desc '):
                    s = line[16:].strip()
                    if s.endswith('?>'):
                        s = s[:-2]
                    return s
                if line.startswith('[-] yelp-tmpl-desc'):
                    return line[18:].strip()
            return ''
        for fname in os.listdir(os.getcwd()):
            if fname.endswith('.page.tmpl'):
                fname = fname[:-5]
                maxlen = max(maxlen, len(fname))
                localpages.append(fname)
            elif fname.endswith('.duck.tmpl'):
                fname = fname[:-5]
                maxlen = max(maxlen, len(fname))
                localducks.append(fname)
            else:
                continue
            descs[fname] = _getdesc(os.path.join(os.getcwd(), fname + '.tmpl'))
        for fname in os.listdir(os.path.join(DATADIR, 'templates')):
            if fname.endswith('.page'):
                if fname in localpages:
                    continue
                maxlen = max(maxlen, len(fname))
                installedpages.append(fname)
            elif fname.endswith('.duck'):
                if fname in localducks:
                    continue
                maxlen = max(maxlen, len(fname))
                installedducks.append(fname)
            else:
                continue
            descs[fname] = _getdesc(os.path.join(DATADIR, 'templates', fname))
        if len(localpages) > 0:
            print('\nLocal Mallard Templates:')
            for page in localpages:
                print('  ' + page.ljust(maxlen) + '  ' + descs.get(page, ''))
        if len(localducks) > 0:
            print('\nLocal Ducktype Templates:')
            for duck in localducks:
                print('  ' + duck.ljust(maxlen) + '  ' + descs.get(duck, ''))
        if len(installedpages) > 0:
            print('\nInstalled Mallard Templates:')
            for page in installedpages:
                print('  ' + page.ljust(maxlen) + '  ' + descs.get(page, ''))
        if len(installedducks) > 0:
            print('\nInstalled Ducktype Templates:')
            for duck in installedducks:
                print('  ' + duck.ljust(maxlen) + '  ' + descs.get(duck, ''))


if __name__ == '__main__':
    try:
        sys.exit(YelpNew().main())
    except KeyboardInterrupt:
        sys.exit(1)
