#! /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
#

from __future__ import unicode_literals

import sys
import os
import shlex
import shutil
import uuid
import tempfile
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


# if you want to debug argcomplete completion,
# you just need to export _ARC_DEBUG=True

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.program)
        self.generate(args, config)

    # pylint: disable=no-self-use
    def getconfig(self, program):
        """Looking for a Config Object into the input program."""
        glog.info("Looking for Config object into input program ...")
        glog.debug(u"input program name : '" + program + u"'")

        if not os.path.exists(program):
            glog.error("the current input program does not exist : " + program)
            raise IOError('No such file')

        dest = os.path.join(tempfile.gettempdir(), unicode(uuid.uuid4()).replace("-", "") + ".py")
        shutil.copyfile(program, dest)

        program = dest

        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
            elif 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
            elif isinstance(temp_class, argtoolbox.BasicProgram):
                glog.debug("variable name found : " + str(i))
                glog.debug("variable type found : " + str(type(temp_class)))
                temp_class.add_config_options()
                result = temp_class.config
                break
        if result:
            glog.info("Config object found : " + result.prog_name)
        os.remove(program)
        program += "c"
        if os.path.exists(program):
            os.remove(program)

        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('program', action="store")
        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()
