#!/usr/bin/env python3
# Copyright 2015-2016 Samsung Electronics Co., Ltd.
#
# 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 os
import sys
import functools
import logging
import traceback
from argparse import (ArgumentParser,
                      RawTextHelpFormatter)
from litmus import (__version__,
                    _path_for_locks_,
                    _duts_, _projects_,
                    _confdir_)
from litmus.core.util import init_logger


def subparser(func):
    """docstring for subparser"""
    @functools.wraps(func)
    def wrapper(parser):
        """docstring for wrapper"""
        splitted = func.__doc__.split('\n')
        name = func.__name__.split('_')[0]
        subparser = parser.add_parser(name, help=splitted[0],
                                      description='\n'.join(splitted[1:]),
                                      formatter_class=RawTextHelpFormatter)
        subparser.set_defaults(module='cmd_{0}'.format(name))
        return func(subparser)
    return wrapper


@subparser
def adhoc_parser(parser):
    """run litmus project which is not imported.

    This will run litmus project which is not imported help you to use CI for
    testing. If you use seperated worker node of CI for running test project,
    just copy project directory to worker node and use this command. If not,
    you have to import litmus project on worker node before run it.

    Examples:
       $ litmus adhoc <project_path>
    """
    parser.add_argument('project_path', type=str, help='project path')
    parser.add_argument('-p', '--param', type=str, nargs='*',
                        help='parameters for project')
    parser.add_argument('-d', '--workingdir', type=str,
                        help='working directory')
    return parser


@subparser
def mk_parser(parser):
    """make new litmus project.

    This will create a new directory for project and copy default templates.
    You can locate more files and test scripts under this project directory
    and run it with project name.

    Examples:
       $ litmus mk <project_name>
    """
    parser.add_argument('project', type=str, help='project name')
    parser.add_argument('-t', '--type', type=str, help='dut type')
    parser.add_argument('-d', '--description', type=str, help='description')
    return parser


@subparser
def rm_parser(parser):
    """remove litmus project.

    This will remove both of your litmus project and project directory from
    your host. WARNING: You can not recover your test project sources after
    run this command.

    Examples:
       $ litmus rm <project_name>
    """
    parser.add_argument('project', type=str, help='project name')
    return parser


@subparser
def run_parser(parser):
    """run litmus project.

    This will run your litmus project. Project entry point is userscript.py in
    your project directory. You can pass argument to project with -p option.
    You can also set working directory with -d option. If you don't set working
    directory then litmus creates a temporary directory under /tmp and run test
    on it, and remove it if test has done. Hence you have to use -d option to
    keep test result and workspace.

    Examples:
       $ litmus run <project_name>
    """
    parser.add_argument('project', type=str, help='project name')
    parser.add_argument('-p', '--param', type=str, nargs='*',
                        help='parameters for project')
    parser.add_argument('-d', '--workingdir', type=str,
                        help='working directory')
    return parser


@subparser
def ls_parser(parser):
    """list all litmus projects.

    This will list all litmus projects on your host. Default project list is
    located in ~/.litmus/projects.

    Examples:
       $ litmus ls
    """
    return parser


@subparser
def dev_parser(parser):
    """list all devices from topology configuration.

    This will list all available test devices in topology configuration.
    If you want to run test with real device, you have to set up topology
    configuration. Default topology file is located in ~/.litmus/topology.

    Topology file example for standalone_tm1 is:

    [TM1_001]
    dev_type = standalone_tm1
    serialno = 01234TEST

    Examples:
       $ litmus dev
    """
    return parser


@subparser
def gt_parser(parser):
    """generate topology configuration.

    This is a helper to generate topology configuration for non-standalone
    devices. non-standalone device means that device is controlled by external
    uart and power supply. xu3 and u3 device types are non-standard device.
    This will find proper uart port and power supply node from your system.
    Do not run this command if you use standalone type devices.

    Examples:
       $ litmus gt
    """
    return parser


@subparser
def cp_parser(parser):
    """copy litmus project.

    This will create new litmus project from existed one. New project has
    duplicated project directory from origin project and does not use default
    template for project.

    Examples:
       $ litmus cp <origin project name> <new project name>
    """
    parser.add_argument('orig', type=str, help='origin project name')
    parser.add_argument('new', type=str, help='new project name')
    parser.add_argument('-d', '--description', type=str, help='description')
    return parser


@subparser
def imp_parser(parser):
    """import litmus project.

    This command will import litmus project. Import means that add new project
    authored by others into project list on your host.

    Examples:
       $ litmus imp <project name>
    """
    parser.add_argument('project', type=str, help='project name')
    parser.add_argument('-d', '--description', type=str, help='description')
    parser.add_argument('-p', '--path', type=str, help='path')
    return parser


def init_lockdir():
    """docstring for init_lockdir"""
    if not os.path.exists(_path_for_locks_):
        os.mkdir(_path_for_locks_)
        try:
            os.chmod(_path_for_locks_, 0o775)
        except PermissionError:
            logging.debug('Can\'t change lock directory permission')


def init_confdir():
    """docstring for init_confdir"""
    if not os.path.exists(_confdir_):
        os.mkdir(_confdir_)
        try:
            os.chmod(_confdir_, 0o775)
        except PermissionError:
            logging.debug('Can\'t change config directory permission')

    if not os.path.exists(_duts_):
        open(_duts_, 'a').close()
        try:
            os.chmod(_duts_, 0o664)
        except PermissionError:
            logging.debug('Can\'t change topology file permission')

    if not os.path.exists(_projects_):
        open(_projects_, 'a').close()
        try:
            os.chmod(_projects_, 0o664)
        except PermissionError:
            logging.debug('Can\'t change projects file permission')


def main(argv=None):
    """docstring for main"""
    description = 'litmus : lightweight test manager'
    parser = ArgumentParser(description=description)

    parser.add_argument('-V', '--version',
                        action='version',
                        version='%(prog)s ' + __version__)
    parser.add_argument('-t', '--topology',
                        type=str,
                        help='topology file path')
    parser.add_argument('-p', '--projects',
                        type=str,
                        help='projects file path')

    parser.format_usage = parser.format_help
    subparsers = parser.add_subparsers(title='subcommands', dest='subcommands')
    subparsers.required = True

    for name, obj in sorted(globals().items()):
        if name.endswith('_parser') and callable(obj):
            obj(subparsers)

    args = parser.parse_args(argv[1:])

    if not args.projects:
        args.projects = _projects_
    else:
        args.projects = os.path.expanduser(args.projects)
    if not args.topology:
        args.topology = _duts_
    else:
        args.topology = os.path.expanduser(args.topology)

    module = __import__('litmus.cmds.{0}'.format(args.module),
                        fromlist=[args.module])
    return module.main(args)


if __name__ == '__main__':
    try:
        init_logger()
        init_lockdir()
        init_confdir()
        sys.exit(main(sys.argv))
    except KeyboardInterrupt:
        raise Exception('KeyboardInterrupt')
    except Exception:
        logging.debug(traceback.format_exc())
        sys.exit(1)
