#!/usr/bin/env python3
#
# Copyright (c) 2019 Jon Turney
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

#
# Fix hints for (probably source) packages consisting of just 'skip:'
#
# (cygport stopped generating these since 0.23.0)
#

import argparse
import os
import re
import shutil
import sys

from . import common_constants
from . import hint
from .version import SetupVersion


#
# write a hint file
#
def hint_file_write(fn, hints):
    with open(fn, 'w') as f:
        for k, v in hints.items():
            print("%s: %s" % (k, v), file=f)


#
# pick most popular item from a list
#
def pick(l):
    if not l:
        return None

    h = {}

    for i in l:
        h[i] = h.get(i, 0) + 1

    for i in sorted(h, key=h.get, reverse=True):
        print('%4d: %s' % (h[i], i))

    i = sorted(h, key=h.get, reverse=True)[0]
    # unanimous ?
    if h[i] == len(l):
        return i

    return None


#
# try to invent plausible information by looking at subpackages
#
def invent_from_subpackages(path, vr):
    sdesc_candidates = []
    category_candidates = []

    for (dirpath, _subdirs, files) in os.walk(path):
        # debuginfo packages never have a good information
        if 'debuginfo' in dirpath:
            continue

        # consider sub-package hints
        for f in files:
            if re.match('^.*-' + re.escape(vr) + '.hint$', f):
                hints = hint.hint_file_parse(os.path.join(dirpath, f), hint.pvr)
                if 'sdesc' in hints:
                    # ... which doesn't contain 'Obsolete' etc.
                    if 'Obsolete' in hints['sdesc'] or '_obsolete' in hints['category']:
                        continue

                    # remove anything inside parentheses, or a single word after
                    # a hyphen, at the end of quoted sdesc
                    sdesc = hints['sdesc']
                    sdesc = re.sub(r'"(.*)"', r'\1', sdesc)
                    sdesc = re.sub(r'(\(.*?\))$', '', sdesc)
                    sdesc = re.sub(r' - \w*$', '', sdesc)
                    sdesc = sdesc.strip()
                    sdesc = '"' + sdesc + '"'

                    sdesc_candidates.append(sdesc)

                    # ignore 'Doc' category (on the basis that this usually a
                    # documentation subpackage)
                    if hints['category'] != 'Doc':
                        category_candidates.append(hints['category'])

    # Ignore a single tool/utility subpackage
    for t in ['utility', 'utilities', 'tool']:
        occurences = [c for c in sdesc_candidates if re.search(r'\b' + t + '\b', c.lower())]
        if len(occurences) == 1:
            sdesc_candidates.remove(occurences[0])

    # pick 'Libs' if that's a possibility
    category = pick(category_candidates)
    if not category and 'Libs' in category_candidates:
        category = 'Libs'

    return (pick(sdesc_candidates), category)


#
# try to invent plausible information by looking at more recent versions
#
def invent_from_other_versions(path, later_vrs):
    sdesc_candidates = []
    category_candidates = []

    pn = path.split(os.path.sep)[-1]
    for vr in later_vrs:
        f = pn + '-' + vr + '.hint'
        hints = hint.hint_file_parse(os.path.join(path, f), hint.pvr)

        if 'sdesc' in hints:
            sdesc_candidates.append(hints['sdesc'])
        if 'category' in hints:
            category_candidates.append(hints['category'])

    return (pick(sdesc_candidates), pick(category_candidates))


#
#
#
def fix_one_hint(dirpath, hintfile, vr, later_vrs):
    hints = hint.hint_file_parse(os.path.join(dirpath, hintfile), hint.pvr)

    hints.pop('parse-errors', None)
    hints.pop('parse-warnings', None)

    if ('skip' not in hints) or (len(hints) > 1):
        return (0, 0)

    # if hint only contains skip:, try to come up with plausible sdesc and category
    (sdesc, category) = invent_from_subpackages(dirpath, vr)
    if not (sdesc and category):
        (sdesc, category) = invent_from_other_versions(dirpath, later_vrs)
    if not (sdesc and category):
        print('couldn\'t invent hints for %s' % (hintfile))
        return (1, 0)

    hints['sdesc'] = sdesc
    hints['category'] = category

    if 'source' not in hints['sdesc']:
        sdesc = re.sub(r'"(.*)"', r'\1', hints['sdesc'])
        sdesc += ' (source)'
        hints['sdesc'] = '"' + sdesc + '"'

    print('writing invented hints for %s' % (hintfile))
    shutil.copy2(os.path.join(dirpath, hintfile), os.path.join(dirpath, hintfile + '.bak'))
    hint_file_write(os.path.join(dirpath, hintfile), hints)

    return (1, 1)


def fix_hints(rel_area, mode):
    skip_only = 0
    invented = 0

    for (dirpath, _subdirs, files) in os.walk(rel_area):
        vrs = {}
        for f in files:
            match = re.match(r'^.*?-(\d.*).hint$', f)
            if match:
                vrs[match.group(1)] = f

        if vrs:
            if mode == 'newest':
                v = sorted(vrs, key=lambda v: SetupVersion(v), reverse=True)[0]
                f = vrs[v]
                vrs = {v: f}

            sorted_vrs = sorted(vrs, key=lambda v: SetupVersion(v))
            while sorted_vrs:
                vr = sorted_vrs.pop(0)
                (s, i) = fix_one_hint(dirpath, vrs[vr], vr, sorted_vrs)
                skip_only += s
                invented += i

    print('%d skip only hints, invented %d hints' % (skip_only, invented))


#
#
#


def main():
    relarea_default = common_constants.FTP

    parser = argparse.ArgumentParser(description='skip-only hint fixer')
    parser.add_argument('--releasearea', action='store', metavar='DIR', help="release directory (default: " + relarea_default + ")", default=relarea_default, dest='rel_area')
    parser.add_argument('--mode', action='store', metavar='all|newest', choices=['all', 'newest'], help="fix all hints, or (default) only for newest version", default='newest')
    (args) = parser.parse_args()

    return fix_hints(args.rel_area, args.mode)


#
#
#

if __name__ == "__main__":
    sys.exit(main())
