#!/usr/bin/env python3

import sys
import os
import argparse
from collections import OrderedDict
import json
import yaml
import csv
from codemeta.codemeta import resolve

try:
    LM_PREFIX = os.environ['LM_PREFIX']
except KeyError:
    print("You are not in LaMachine!",file=sys.stderr)
    sys.exit(2)

if yaml is not None:
    def represent_ordereddict(dumper, data):
        value = []

        for item_key, item_value in data.items():
            node_key = dumper.represent_data(item_key)
            node_value = dumper.represent_data(item_value)

            value.append((node_key, node_value))

        return yaml.nodes.MappingNode('tag:yaml.org,2002:map', value)

    yaml.add_representer(OrderedDict, represent_ordereddict)

def yaml_simplify(d):
    if isinstance(d, (dict, OrderedDict)):
        return dict( (k, yaml_simplify(v)) for k, v in d.items() if k[0] != '@' )
    elif isinstance(d, (list, tuple)):
        return list( yaml_simplify(v) for v in d )
    else:
        return d

def flatten(x, multilineprefix="", bold="", tt=""):
    if isinstance(x, str):
        return x.strip()
    if isinstance(x, (list, tuple)):
        l = [ flatten(y,multilineprefix,bold, tt) for y in x if y ]
        if all("\n" in y for y in l):
            return multilineprefix.join(l)
        else:
            return "; ".join(l)
    if isinstance(x, dict):
        if 'givenName' in x and 'familyName' in x: #names
            return x['givenName'] + " " + x['familyName']
        elif 'parentOrganization' in x: #organization
            return x.get('name','') + ", " + flatten(x['parentOrganization'])
        elif 'urlTemplate' in x: #entryPoints
            name = x.get('name','')
            if name: name = bold + name + bold + ', '
            desc = x.get('description','')
            if desc:  desc = ": " + desc
            return name + INTERFACETYPE.get(x.get('interfaceType','?'),'?') + "; " + tt + x.get('urlTemplate','').replace('file:///','') + tt +  desc + "\n"
        elif 'name' in x:
            return x['name'].strip()
        else:
            return ""


INTERFACETYPE = {
    'CLI': "Command-line tool",
    'GUI': "Graphical User Interface",
    'WUI': "Web-based User Interface",
    'WS': "Webservice",
    'REST': "RESTful webservice",
    '?': "interface unknown",
}



CSVCOLUMNS = ('identifier',
              'name',
              'author',
              'producer',
              'version',
              'description',
              'url',
              'codeRepository',
              'programmingLanguage',
              'issueTracker',
              'contIntegration',
              'license',
              'dateCreated',
              'entryPoints',
              'keywords',
              'funder',
              'developmentStatus')

LABELS = {
    'identifier': 'Identifier',
    'name': 'Name',
    'author': 'Author(s)',
    'producer': 'Producer(s)',
    'version': 'Version',
    'description':'Description',
    'url':'Website',
    'codeRepository':'Code Repository',
    'programmingLanguage': 'Programming Language(s)',
    'issueTracker': 'Issue Tracker',
    'contIntegration': 'Continuous Integration Tests',
    'license': 'Licence',
    'dateCreated':'Date created',
    'entryPoints':'Entry points',
    'keywords':'Keywords',
    'funder':'Funder(s)',
    'developmentStatus':'Development Status'
}

MARKDOWNFIELDS = ('author',
              'producer',
              'url',
              'codeRepository',
              'programmingLanguage',
              'issuetracker',
              'contIntegration',
              'license',
              'dateCreated',
              'keywords',
              'funder',
              'developmentStatus',
              'entryPoints')

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description="LaMachine list")
    #parser.add_argument('--yaml', help="Read metadata from standard input (YAML format)", action='store_true',required=False)
    parser.add_argument('-r', "--registry", type=str,help="Registry file", action='store',required=False, default=LM_PREFIX + "/var/lamachine-registry.json")
    parser.add_argument("--csv", type=str, help="Output to CSV file", action='store',required=False, default="")
    parser.add_argument("--md", help="Output to Markdown", action='store_true')
    parser.add_argument('-s', "--short", help="Short output", action='store_true',required=False)
    parser.add_argument('-v', "--versions", help="Outputs a customversions.yml file which you can use to bootstrap another LaMachine with the exact same versions", action='store_true',required=False)
    parser.add_argument("--json", help="Short output", action='store_true',required=False)
    parser.add_argument("--dependencies", help="Include all dependencies as well", action='store_true',required=False)
    parser.add_argument('software', nargs='*', help='Software packages to query (if not specified, will return all)')
    args = parser.parse_args()
    with open(args.registry,'r',encoding='utf-8') as f:
        registry = resolve(json.load(f))
    jsonout = {}
    yamlout = {}
    softwarelist = [ x.lower() for x in args.software ]
    if softwarelist and args.dependencies:
        for data in sorted(registry['@graph'], key=lambda x: x['identifier'].lower()):
            if data['identifier'].lower() in softwarelist:
                if 'softwareRequirements' in data:
                    for dep in data['softwareRequirements']:
                        if 'identifier' in dep and dep['identifier'].lower() not in softwarelist:
                            softwarelist.append(dep['identifier'].lower())

    if args.versions:
        print("customversion:")
    if args.csv:
        csvfile = open(args.csv, 'w', newline='')
        csvwriter = csv.DictWriter(csvfile, fieldnames=CSVCOLUMNS,quoting=csv.QUOTE_ALL)
        csvwriter.writerow( { k:LABELS[k] for k in CSVCOLUMNS } )
    for data in sorted(registry['@graph'], key=lambda x: x['identifier'].lower()):
        if not softwarelist or data['identifier'].lower() in softwarelist:
            if args.versions:
                if 'version' in data:
                    yamlout[data['identifier'].lower()] = data['version']
                    print("  " + data['identifier'].lower() + ': "' + data['version'] + '"')
                else:
                    print("WARNING: No version information for " + data['identifier'],file=sys.stderr)
            elif args.csv:
                csvwriter.writerow( { key: flatten(data.get(key,"")) for key in CSVCOLUMNS } )
            elif args.md:
                print("* **{}** ({})".format(data.get('name',data['identifier'].lower()), data.get('version','unknown version')) + ": " + data.get('description',''))
                if not args.short:
                    for field in MARKDOWNFIELDS:
                        if field in data:
                            nl = "\n       - " if field == "entryPoints" else ""
                            print("   * *" + LABELS[field] + "*: " + nl +  flatten(data[field], multilineprefix="      - ",bold="**", tt="``"))
            elif args.short:
                print('{0: <20}'.format(data['identifier'].lower()) + "\t" + '{0: <30}'.format(data.get('name','unknown')) + "\t" + data.get('version','unknown')+ "\t" + data.get('description',''))
            elif args.json:
                jsonout[data['identifier'].lower()] = data
            else:
                print(yaml.dump({data['identifier'].lower(): yaml_simplify(data)}, default_flow_style=False))

    if args.versions:
        with open("customversions.yml",'w',encoding='utf-8') as f:
            print(yaml.dump({"customversion": yamlout}, default_flow_style=False), file=f)
        print("(Also written to customversions.yml)",file=sys.stderr)
    elif args.json:
        print(json.dumps(jsonout, ensure_ascii=False,indent=4))

