#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Update manifest file for chewy repository
#
#

import argparse
import os
import portage.output
import re
import sys
import urllib.parse

import chewy
import chewy.meta

log = portage.output.EOutput()

_IGNORE_PATH = [
    re.compile('\\.(git|svn|hg)')
  , re.compile('README.md')
  , re.compile('manifest')
  ]
_X_CHEWY_REPOBASE = '# X-Chewy-' + chewy.meta.REPOBASE + ': '
_MANIFEST_FILENAME = 'manifest'
_MANIFEST_HEADER = '''#
# ATTENTION DO NOT EDIT! This file was generated by `update-manifest.py`!
#
'''


def getMetaInfo(filename):
    f = open(filename, 'rt')
    content = f.read()
    return chewy.Module(content)


def mustBeIgnored(entry):
    for expr in _IGNORE_PATH:
        if expr.match(entry):
            return True
    return False


def scanDir(repobase_path, rel_path):
    this_level_result = []
    log.einfo('Scanning directory ' + rel_path + ' for files...')
    current_abs_path = os.path.join(repobase_path, rel_path)
    for entry in os.listdir(current_abs_path):
        current = os.path.join(rel_path, entry)
        if mustBeIgnored(entry):
            continue
        if os.path.isdir(current):
            this_level_result += scanDir(repobase_path, current)
        elif os.path.isfile(current):
            try:
                mod = getMetaInfo(current)
                # Make sure module path is match
                mod_abs_path = os.path.abspath(os.path.join(repobase_path, mod.path))
                cur_abs_path = os.path.abspath(os.path.join(repobase_path, current))
                if mod_abs_path != cur_abs_path:
                    log.eerror('Module path invalid: `{}`'.format(mod.path))
                    log.ewarn('Skip it!')
                    continue
                # Make sure module has a description
                if not mod.description:
                    log.eerror('Module must contain a description meta tag: `{}`'.format(current))
                    log.ewarn('Skip it!')
                    continue
                # TODO Make sure all listed files exists
                this_level_result.append(mod)
                log.einfo('Found {} {}'.format(mod.path, mod.version))
            except chewy.NoMetaError:
                continue
            except chewy.ModuleError as e:
                log.ewarn('Module {} loading failure: {}'.format(current, e))
                log.ewarn('Skip it!')
                continue
    return this_level_result


def try_to_get_repobase_from_manifest(repo_dir):
    manifest_filename = os.path.join(repo_dir, _MANIFEST_FILENAME)
    log.einfo('Try to get RepoBase from {}'.format(manifest_filename))
    if os.path.isfile(manifest_filename):
        f = open(manifest_filename, 'rt')
        content = f.read()
        return chewy.Manifest(content).repobase
    # TODO More elaborate error description needed
    raise chewy.ManifestError('Manifest file not found or invalid: {}'.format(manifest_filename))


def main():
    parser = argparse.ArgumentParser(description='Update a Chewy manifest for repository.')
    parser.add_argument(
        'repo_url'
      , metavar='REPO-URL'
      , help='Repository URL. Default taken from manifest file if exists.'
      , nargs='?'
      )
    parser.add_argument(
        '-r'
      , '--repo-dir'
      , metavar='PATH'
      , default=os.getcwd()
      , help='Work copy of Chewy repository to scan. Default is a current directory.'
      )
    args = parser.parse_args()

    # Get repository base URL
    if args.repo_url is None:
        try:
            repo_url = try_to_get_repobase_from_manifest(args.repo_dir)
        except chewy.ManifestError as e:
            log.eerror(e.args[0])
            sys.exit(1)
    else:
        # TODO Validate URL
        repo_url = args.repo_url

    log.einfo('Set repo base directory to ' + repo_url)
    # Walk through the current directory down and find all files
    modules = scanDir(os.curdir, os.curdir)
    if len(modules):
        manifest = open(_MANIFEST_FILENAME, 'wt')
        manifest.write(_X_CHEWY_REPOBASE + repo_url + '\n' + _MANIFEST_HEADER)
        for mod in modules:
            if mod.repobase != repo_url:
                log.ewarn(
                    'Module {} belongs to other Chewy repository: {}'.format(mod.path, mod.repobase)
                  )
                log.ewarn('Skip it!')
                continue
            addons_str = ''
            if mod.addons:
                addons_str = ' ' + ' '.join(mod.addons)
            description = urllib.parse.quote_plus(mod.description)
            manifest.write(mod.path + ' ' + str(mod.version) + ' ' + description + addons_str + '\n')
    else:
        log.ewarn('No CMake modules found. Nothing to do...')


if __name__ == '__main__':
    main()
