#!/usr/libexec/platform-python

import configparser
import subprocess
import argparse
import logging
import pipes
import sys
import os
import re

log = logging.getLogger('__main__')
log.setLevel(logging.INFO)
log.addHandler(logging.StreamHandler(sys.stdout))

parser = argparse.ArgumentParser(description='Preprocess the given spec file '
    'and either print the result to stdout or to a file. If the source spec '
    'contains any macros that produce files, by default these will be put '
    'into CWD. NOTE: you need to trust the spec files you are preprocessing.')

parser.add_argument('spec_file', action='store',
                    help='Path to a spec file template.')
parser.add_argument('--output', '-o',  action='store', metavar='PATH',
                    help='Where to put the preprocessed text. If not given, '
                    'the text is printed to stdout. If the given path is a directory, '
                    'basename of the <spec_file> is used to complete it (.rpkg extension '
                    'will be stripped if present).')
parser.add_argument('--check-context', '-c', action='store_true',
                    help='Run preprocessing only if it is allowed by rpkg '
                    'configuration (i.e. rpkg.preprocess_spec == True), '
                    'otherwise directly output the original text. '
                    'The configuration is read first from rpkg.conf file '
                    'in the git top-level directory for the spec file '
                    'and then from rpkg.conf file placed directly next to '
                    'to the spec file (i.e. in the same directory). No other '
                    'rpkg.conf in the system is taken into account.')
parser.add_argument('--no-outdir', action='store_true',
                    help='Disable any file generation by macros in the spec.')
parser.add_argument('--outdir', default=os.getcwd(), action='store',
                    help='Where to put generated files if any, by default CWD.')
parser.add_argument('--verbose', action='store_true',
                    help='Print some debug messages. --output|-o needs '
                    'to be specified as well for the messages to appear.')

args = parser.parse_args()


def setup_log():
    if args.verbose and args.output:
        log.setLevel(logging.DEBUG)


def cmd_repr(cmd):
    quoted_items = []
    for i in range(len(cmd)):
        quoted_items.append(pipes.quote(cmd[i]))
    return ' '.join(quoted_items)


def run(cmd, shell=False, env=None, cwd=None, capture=True, capture_stderr=False, throw=True):
    if capture:
        stdout = subprocess.PIPE
    else:
        stdout = None

    if capture_stderr:
        stderr = subprocess.PIPE
    else:
        stderr = None

    log.debug('Running: {0}'.format(
        cmd_repr(cmd) if not shell else cmd))

    process = subprocess.Popen(
        cmd, stdout=stdout, stderr=stderr, shell=shell, env=env, cwd=cwd)

    (out, err) = process.communicate()

    err = err.decode('utf-8').strip('\n') if err else ''

    if process.returncode != 0 and throw:
        sys.exit(err)

    out = out.decode('utf-8').strip('\n') if out else ''

    return {'out': out,
            'err': err,
            'rc': process.returncode}


def check_context(spec_path):
    configs_to_read = []

    # first check repo config
    cmd_result = run("git rev-parse --show-toplevel",
                     shell=True, capture_stderr=True, throw=False,
                     cwd=os.path.dirname(spec_path))

    if cmd_result['rc'] == 0:
        repo_config = os.path.join(cmd_result['out'], 'rpkg.conf')
        if os.path.isfile(repo_config):
            log.debug('repo config found.')
            configs_to_read.append(repo_config)

    # now check spec config
    spec_config = os.path.join(os.path.dirname(spec_path), 'rpkg.conf')
    if os.path.isfile(spec_config):
        log.debug('spec config found.')
        configs_to_read.append(spec_config)

    if not configs_to_read:
        # no rpkg.conf discovered
        return False

    # check if preprocessing is enabled by the configuration
    parser = configparser.ConfigParser(
        interpolation=configparser.ExtendedInterpolation())
    parser.read(configs_to_read)

    return parser.getboolean('rpkg', 'preprocess_spec', fallback=False)


def preproc_cmd_compose(spec_path):
    env_params = '-e INPUT_PATH="{0}"'.format(spec_path)
    if not args.no_outdir:
        env_params += ' -e OUTDIR="{0}"'.format(args.outdir)
    return 'cat "{0}" | preproc -s /usr/lib/rpkg.macros.d/all.bash {1}'.format(spec_path, env_params)


def write(where, data):
    if sys.version_info < (3, 0):
        where.write(data.encode('utf-8'))
    else:
        where.write(data)


def main():
    spec_abs_path = os.path.abspath(args.spec_file)

    if args.check_context and not check_context(spec_abs_path):
        cmd = 'cat "{0}"'.format(spec_abs_path)
    else:
        cmd = preproc_cmd_compose(spec_abs_path)

    try:
        result_text = run(cmd, shell=True)['out']
    except:
        sys.exit("Error during spec file preprocessing.")

    if not args.output:
        write(sys.stdout, result_text+'\n')
        return

    if os.path.isdir(args.output):
        output_filename = re.sub(r'\.rpkg$', '', os.path.basename(spec_abs_path))
        output_path = os.path.join(args.output, output_filename)
    else:
        output_path = args.output

    output_file = open(output_path, "w")
    write(output_file, result_text+'\n')
    output_file.close()


if __name__ == '__main__':
    setup_log()
    main()
