#! /usr/bin/env python2
# -*- coding: utf-8 -*-
# PYTHON_ARGCOMPLETE_OK

# This file is part of argtoolbox.
#
# argtoolbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# argtoolbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LinShare user cli.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2014 Frédéric MARTIN
#
# Contributors list:
#
#  Frédéric MARTIN frederic.martin.fma@gmail.com
#

import sys
import os
import shlex
import importlib
import logging
import argparse
import argtoolbox
from argtoolbox import BasicProgram
from argtoolbox import DefaultCommand
from argtoolbox import query_yes_no
from subprocess import call


class ConfigGenerationCommand(object):
    """This command read a existing program and generate the associated
    configuration file"""

    def __call__(self, args):
        glog.debug("args : " + str(args))
        if args.debug:
            glog.setLevel(logging.DEBUG)
        config = self.getconfig(args)
        self.generate(args, config)

    # pylint: disable=no-self-use
    def getconfig(self, args):
        """Looking for a Config Object into the input program."""
        glog.info("Looking for Config object into input program ...")
        glog.debug("input program name : '" + str(args.program) + "'")
        program = args.program
        local_dir = os.path.dirname(program)
        glog.debug("local_dir : '" + str(local_dir) + "'")
        sys.path.append(local_dir)

        module_name = program.split('/')[-1].split('.')[0]
        glog.debug("module name found : " + str(module_name))

        level = logging.getLogger().getEffectiveLevel()
        module = importlib.import_module(module_name)
        glog.setLevel(level)

        result = None
        for i in dir(module):
            temp_class = getattr(module, i)
            if isinstance(temp_class, argtoolbox.Config):
                glog.debug("variable name found : " + str(i))
                glog.debug("variable type found : " + str(type(temp_class)))
                result = temp_class
                break
            # pylint: disable=line-too-long
            elif isinstance(temp_class, argtoolbox.BasicProgram) or isinstance(temp_class, argtoolbox.DefaultProgram):
                glog.debug("variable name found : " + str(i))
                glog.debug("variable type found : " + str(type(temp_class)))
                result = temp_class.config
                break
        if result:
            glog.info("Config object found : " + result.prog_name)
        return result

    # pylint: disable=no-self-use
    def generate(self, args, config):
        """Generate the default configuration file from the config object"""
        glog.info("Trying to generate the default configuration file ...")
        if not config.use_config_file:
            glog.info(
                "The current program does not support a configuration file.")
            return True
        configfile = os.path.expanduser('~/.' + config.prog_name + '.cfg')
        glog.info("The default configuration file name is : " + configfile)
        if args.output:
            configfile = args.output
            if configfile[:-4] != ".conf":
                configfile += ".conf"
            glog.warn("Using '" + configfile + "' as configuration file name.")

        if not args.force_yes:
            if os.path.exists(configfile):
                glog.warn(
                    "The current file already exists : " + str(configfile))
                if not argtoolbox.query_yes_no("Overwrite ?", "no"):
                    glog.error("Aborted.")
                    return False
        config.write_default_config_file(configfile, args.nocomments)
        glog.info("Done.")


# pylint:disable=no-init
# pylint:disable=too-few-public-methods
class RegisterCommand(DefaultCommand):
    """The command is design to register global argcomplete support"""

    def __call__(self, args):
        DefaultCommand.__call__(self, args)
        if args.global_register:
            sys.exit(call("activate-global-python-argcomplete"))
        elif args.user_register:
            cmd = "activate-global-python-argcomplete --user"
            if call(shlex.split(cmd)) != 0:
                sys.exit(1)
            bashrc = os.path.expanduser("~/.bashrc")
            fde = open(bashrc, 'a')
            fde.write("source ~/.bash_completion.d/python-argcomplete.sh\n")
            fde.flush()
            fde.close()
            self.log.info("The command 'source \
~/.bash_completion.d/python-argcomplete.sh' was added to your bashrc")
        else:
            bashrc = os.path.expanduser("~/.bashrc")
            self.log.info("register-python-argcomplete %(script)s" % {
                'script': args.script})
            fde = open(bashrc, 'a')
            fde.write('eval "$(register-python-argcomplete %(script)s)"\n' % {
                'script': args.script})
            fde.flush()
            fde.close()


# pylint:disable=no-init
class ListRegisteredCommand(DefaultCommand):
    """The command is design to register global argcomplete support"""

    def __call__(self, args):
        DefaultCommand.__call__(self, args)
        bashrc = os.path.expanduser("~/.bashrc")
        cmd = "grep register-python-argcomplete " + bashrc
        call(shlex.split(cmd))


class GenerateCommand(DefaultCommand):
    """The command is design to generate a sample program"""

    def __call__(self, args):
        DefaultCommand.__call__(self, args)

        filename = args.prog_name.lower() + ".py"
        prog_name = args.prog_name.lower()
        command_name = args.command_name.lower()

        if self.copy(filename, args.force_yes):
            self.rename_command(command_name, filename)
            self.rename_prog(prog_name, filename)
            self.description(args.description, prog_name, filename)
            print "program generation complete : " + str(filename)

    # pylint: disable=no-self-use
    def description(self, description, prog_name, filename):
        """Replace ${description} variable in file content"""
        if not description:
            description = "Description of program " + prog_name
        cmd = "sed -i -e 's/#{description}/%(data)s/g' %(file)s" % {
            'data': description,
            'file': filename}
        call(shlex.split(cmd))

    # pylint: disable=no-self-use
    def rename_command(self, command_name, filename):
        """Replace ${command_name} variable in file content"""
        cmd = "sed -i -e 's/#{command_name}/%(data)s/g' %(file)s" % {
            'data': command_name,
            'file': filename}
        call(shlex.split(cmd))
        cmd = "sed -i -e 's/#{command_name_class}/%(data)s/g' %(file)s" % {
            'data': command_name.title(),
            'file': filename}
        call(shlex.split(cmd))

    def rename_prog(self, prog_name, filename):
        """Replace ${prog_name} variable in file content"""
        cmd = "sed -i -e 's/#{prog_name}/%(data)s/g' %(file)s" % {
            'data': prog_name,
            'file': filename}
        call(shlex.split(cmd))
        prog_name.title()
        cmd = "sed -i -e 's/#{prog_name_class}/%(data)s/g' %(file)s" % {
            'data': prog_name.title(),
            'file': filename}
        call(shlex.split(cmd))

    def copy(self, filename, force_yes):
        """Copy the default template to the new file name."""
        if not force_yes:
            if os.path.exists(filename):
                self.log.warn(
                    "current file already exists : " + str(filename))
                if not query_yes_no("overwrite ?", "no"):
                    self.log.error("aborted.")
                    return False
        cmd = "/bin/cp -f %(template)s %(filename)s" % {
            "template": os.path.join(
                os.path.dirname(argtoolbox.__file__),
                'templates',
                'default.tml'),
            "filename": filename}
        if call(shlex.split(cmd)) == 0:
            return True


class MyProgram(BasicProgram):
    """Main program"""

    def add_config_options(self):
        BasicProgram.add_config_options(self)

    def add_commands(self):
        BasicProgram.add_commands(self)
        self.parser.formatter_class = argparse.RawTextHelpFormatter
        subparsers = self.parser.add_subparsers()

        # Command register
        parser_tmp = subparsers.add_parser(
            'register',
            formatter_class = argparse.RawTextHelpFormatter,
            help="""This command is used to enable auto completion for program
based on argparse module using argcomplete module.

If you are using bash (version <= 4.2), you have to register
autocompletion support for every scripts, one by one.

If you are using bash (version >=4.3) you can use automatic
registering support (see global or user options)

""")


        register_group = parser_tmp.add_argument_group('register')
        group = register_group.add_mutually_exclusive_group(required=True)
        group.add_argument(
            '--script',
            action="store",
            help="""This option will modify your .bashrc to register the
bash_completion module for the current SCRIPT.\n
""")
        group.add_argument(
            '--global',
            action="store_true",
            default=False,
            dest="global_register",
            help="""This option will register the bash_completion script for
argcomplete into /etc/bash_completion_d/.
Completion will be automatically enable for every scripts
which support it.
Make sure you have sufficient privileges.
\n/!\\ Need Bash >= 4.3 /!\\ \n
""")
        group.add_argument(
            '--user',
            action="store_true",
            default=False,
            dest="user_register",
            help="""This option will register the bash_completion script for
argcomplete into ~/.bash_completion_d/ and modify your
bashrc to include it
Completion will be automatically enable for every scripts
which support it.
\n/!\\ Need Bash >= 4.3 /!\\ \n
            """)
        parser_tmp.set_defaults(__func__=RegisterCommand(self.config))


        # Command list registered commands
        parser_tmp = subparsers.add_parser(
            'list-registered',
            help="List already registered scripts")
        parser_tmp.set_defaults(__func__=ListRegisteredCommand(self.config))


        # Command new
        parser_tmp = subparsers.add_parser(
            'new',
            help="Create a new program based on default template")
        parser_tmp.add_argument("--description")
        parser_tmp.add_argument("--progname", dest="prog_name",
                                default="sample")
        parser_tmp.add_argument("--commandname", dest="command_name",
                                default="sample")
        parser_tmp.add_argument(
            '-f',
            dest="force_yes",
            action="store_true",
            help="overwrite the current output file even it still exists.")
        parser_tmp.set_defaults(__func__=GenerateCommand(self.config))


        # Command generate
        parser_tmp = subparsers.add_parser(
            'generate',
            help="""Scan an existing program and generate the associated
             configuration file""")
        parser_tmp.add_argument('--output', action="store")
        parser_tmp.add_argument('--input', action="store", dest="program",
                                required=True)
        parser_tmp.add_argument(
            '-n',
            dest="nocomments",
            action="store_false",
            help="config file generation without commments.")
        parser_tmp.add_argument(
            '-f',
            dest="force_yes",
            action="store_true",
            help="overwrite the current output file even it still exists.")
        parser_tmp.set_defaults(__func__=ConfigGenerationCommand())


# -----------------------------------------------------------------------------
# MAIN
# -----------------------------------------------------------------------------
# logger
# pylint:disable=invalid-name
glog = logging.getLogger()
glog.setLevel(logging.INFO)
# logger handlers
glog.addHandler(argtoolbox.streamHandler)


if __name__ == "__main__":
    PROG = MyProgram(
        "argtoolbox_utils",
        use_config_file=False,
        desc="""This tool help you generate config file for
         program based on argtoolbox package, create new basic
        program, ... make your dev life easier !""")
    PROG()
