
"""
mbed SDK
Copyright (c) 2011-2018 ARM Limited

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

import sys
import json
import argparse
from collections import defaultdict

# Make sure that any global generic setup is run
from . import lstools_base
from mbed_os_tools.detect.main import (
    create,
    mbed_os_support,
    mbed_lstools_os_info,
    mock_platform
)

import logging
logger = logging.getLogger("mbedls.main")
logger.addHandler(logging.NullHandler())
del logging

def get_version():
    """! Get mbed-ls Python module version string """
    import pkg_resources  # part of setuptools
    return pkg_resources.require("mbed-ls")[0].version

def print_version(mbeds, args):
    print(get_version())

def print_mbeds(mbeds, args, simple):
    devices = mbeds.list_mbeds(unique_names=True, read_details_txt=True)
    if devices:
        from prettytable import PrettyTable, HEADER
        columns = ['platform_name', 'platform_name_unique', 'mount_point',
                    'serial_port', 'target_id', 'daplink_version']
        pt = PrettyTable(columns, junction_char="|", hrules=HEADER)
        pt.align = 'l'
        for d in devices:
            pt.add_row([d.get(col, None) or 'unknown' for col in columns])
        print(pt.get_string(border=not simple, header=not simple,
                            padding_width=1, sortby='platform_name_unique'))

def print_table(mbeds, args):
    return print_mbeds(mbeds, args, False)

def print_simple(mbeds, args):
    return print_mbeds(mbeds, args, True)

def list_platforms(mbeds, args):
    print(mbeds.list_manufacture_ids())

def mbeds_as_json(mbeds, args):
    print(json.dumps(mbeds.list_mbeds(unique_names=True,
                                      read_details_txt=True),
                     indent=4, sort_keys=True))

def json_by_target_id(mbeds, args):
    print(json.dumps({m['target_id']: m for m
                      in mbeds.list_mbeds(unique_names=True,
                                          read_details_txt=True)},
                     indent=4, sort_keys=True))

def json_platforms(mbeds, args):
    platforms = set()
    for d in mbeds.list_mbeds():
        platforms |= set([d['platform_name']])
    print(json.dumps(list(platforms), indent=4, sort_keys=True))

def json_platforms_ext(mbeds, args):
    platforms = defaultdict(lambda: 0)
    for d in mbeds.list_mbeds():
        platforms[d['platform_name']] += 1
    print(json.dumps(platforms, indent=4, sort_keys=True))

def parse_cli(to_parse):
    """! Parse the command line

    @return Retrun a namespace that contains:
     * command - python function to run
     * skip_retarget - bool indicting to skip retargeting
     * list_unmounted - list boards that are not mounted
     * debug - turn on debug logging
    """
    parser = argparse.ArgumentParser()
    parser.set_defaults(command=print_table)

    commands = parser.add_argument_group('sub commands')\
                     .add_mutually_exclusive_group()
    commands.add_argument(
        '-s', '--simple', dest='command', action='store_const',
        const=print_simple,
        help='list attached targets without column headers and borders')
    commands.add_argument(
        '-j', '--json', dest='command', action='store_const',
        const=mbeds_as_json,
        help='list attached targets with detailed information in JSON format')
    commands.add_argument(
        '-J', '--json-by-target-id', dest='command', action='store_const',
        const=json_by_target_id,
        help='map attached targets from their target ID to their detailed '
        'information in JSON format')
    commands.add_argument(
        '-p', '--json-platforms', dest='command', action='store_const',
        const=json_platforms,
        help='list attached platform names in JSON format.')
    commands.add_argument(
        '-P', '--json-platforms-ext', dest='command', action='store_const',
        const=json_platforms_ext,
        help='map attached platform names to the number of attached boards in '
        'JSON format')
    commands.add_argument(
        '-l', '--list', dest='command', action='store_const',
        const=list_platforms,
        help='list all target IDs and their corresponding platform names '
        'understood by mbed-ls')
    commands.add_argument(
        '--version', dest='command', action='store_const', const=print_version,
        help='print package version and exit')
    commands.add_argument(
        '-m', '--mock', metavar='ID:NAME',
        help='substitute or create a target ID to platform name mapping used'
        'when invoking mbedls in the current directory')

    parser.add_argument(
        '--skip-retarget', dest='skip_retarget', default=False,
        action="store_true",
        help='skip parsing and interpretation of the re-target file,'
        ' `./mbedls.json`')
    parser.add_argument(
        '-u', '--list-unmounted', dest='list_unmounted', default=False,
        action='store_true',
        help='list mbeds, regardless of whether they are mounted or not')
    parser.add_argument(
        '-d', '--debug', dest='debug', default=False, action="store_true",
        help='outputs extra debug information useful when creating issues!')

    args = parser.parse_args(to_parse)
    if args.mock:
        args.command = mock_platform
    return args

def start_logging():
    try:
        import colorlog
        colorlog.basicConfig(
            format='%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s')
    except ImportError:
        import logging
        logging.basicConfig()
        del logging

def mbedls_main():
    """! Function used to drive CLI (command line interface) application
    @return Function exits with success code
    """
    start_logging()

    args = parse_cli(sys.argv[1:])

    import logging
    root_logger = logging.getLogger("mbedls")
    if args.debug:
        root_logger.setLevel(logging.DEBUG)
    else:
        root_logger.setLevel(logging.INFO)
    del logging
    logger.debug("mbed-ls ver. %s", get_version())
    logger.debug("host: %s",  str(mbed_lstools_os_info()))

    mbeds = create(skip_retarget=args.skip_retarget,
                   list_unmounted=args.list_unmounted,
                   force_mock=args.command is mock_platform)

    if mbeds is None:
        logger.critical('This platform is not supported! Pull requests welcome at github.com/ARMmbed/mbed-ls')
        sys.exit(1)

    ret_code = args.command(mbeds, args)
    if not ret_code:
        ret_code = 0

    logger.debug("Return code: %d", ret_code)

    sys.exit(ret_code)
