"""
Functionality for providing info on the commands, and alias generation.
"""

import os
import settings
from commands import *
from utils import *

CALLABLE_CMD_PREFIX = 'c_cmd_'
ALIAS_GENERATOR_PREFIX = 'c_alias_'
FUNCTION_GENERATOR_PREFIX = 'c_func_'


def get_callable_commands():
    """
    Returns dict of all the callable commands, i.e. those which are called by the alias they create.
    """
    return _get_commands(CALLABLE_CMD_PREFIX)


def get_alias_generators():
    return _get_commands(ALIAS_GENERATOR_PREFIX)
    

def get_function_generators():
    return _get_commands(FUNCTION_GENERATOR_PREFIX)


def _get_commands(prefix):
    commands = {}
    cutby = len(prefix)
    for function_name, function in GLOBAL_NAMES.items():
        alias = None
        help_str = None
        if function_name.startswith(prefix):
            docstring_lines = [l.lstrip() for l in function.__doc__.split(os.linesep)]
            for line in docstring_lines:
                if line.startswith('alias:'):
                    alias = _extract_docstring_value(line)
                elif line.startswith('help:'):
                    help_str = _extract_docstring_value(line)
            if alias and help_str:
                handle = function_name[cutby:]
                commands[handle] = {'function':function, 'alias': alias, 'help': help_str}
    return commands


def _extract_docstring_value(line):
    return line.split(':', 1)[1].strip()


def c_cmd_rebuild_aliases(args):
    """
    alias: manage.rebuild_aliases
    help: Rebuilds the alias file.
    """
    callable_commands = get_callable_commands()
    alias_generators = get_alias_generators()
    function_generators = get_function_generators()
    ab = AliasFileBuilder()
    ab.build(callable_commands, alias_generators, function_generators)


class AliasFileBuilder:

    def __init__(self):
        self.unique_aliases = OnceList('Alias')
        self.lines = []
        self.help_entries = {}

    def build(self, callable_cmds, alias_generators, function_generators):
        self._insert_intial_lines()
        self._add_callable_cmds(callable_cmds)
        self._add_alias_generators(alias_generators)
        self._add_function_generators(function_generators)
        self._add_help_alias()
        self._write_aliases()

    def _insert_intial_lines(self):
        """
        Builds the initial lines for the alias file, setting variable and creating a couple of aliases.
        """
        self.unique_aliases.add('manage.reload_aliases')
        self.unique_aliases.add('manage.realias')
        self.help_entries['manage.reload_aliases'] = 'Rebuilds and reloads aliases in current session.'
        self.help_entries['manage.realias'] = 'Reloads aliases in current session.'
        self.lines.extend([
            "# This file is generated by main.py in the project 'c'.",
            "# Changed made here will be overwritten.",
            " ",
            "SCRIPT='{}'".format(settings.MAIN_FILE),
            " ",
            "alias c.manage.reload_aliases='source {}'".format(settings.ALIAS_FILE),
            "alias c.manage.realias='c.manage.rebuild_aliases && c.manage.reload_aliases'".format(settings.ALIAS_FILE),
        ])

    def _add_callable_cmds(self, callable_cmds):
        """
        Builds the aliases for callable commands.
        """
        self.lines.append(' ')
        self.lines.append('# callable commands:')
        for name, command in callable_cmds.items():
            self._expand_callable_aliases(name, command)

    def _expand_callable_aliases(self, fn_name, command):
        """
        Returns list of lines for the command.
        Handles one of three situations

            1. The alias is simple (e.g. 'hello') in which case a single alias is added
            2. The alias is composite (e.g. 'hello.[projects]') in which case an alias is added
               for every entry in [projects]
            3. The command generates aliases, in which case the function itself is called.

        """
        alias = command['alias']
        self.help_entries[alias] = command['help']
        if '[' in alias:
            first_bracket = alias.find('[')
            second_bracket = alias.find(']')
            text_before = alias[0:first_bracket]
            text_after = alias[second_bracket + 1:]
            listable = alias[first_bracket + 1:second_bracket]
            enum_function = globals()['list_' + listable]
            for item in enum_function():
                sub_alias = text_before + item + text_after
                self.unique_aliases.add(sub_alias)
                self.lines.append("alias c.{sub_alias}='python3 $SCRIPT {fn_name} {item} $*'".format(**locals()))
        else:
            self.unique_aliases.add(alias)
            self.lines.append("alias c.{alias}='python3 $SCRIPT {fn_name} $*'".format(**locals()))

    def _add_alias_generators(self, alias_generators):
        """
        Builds the aliases generated by alias generator.
        """
        self.lines.append(' ')
        self.lines.append('# c generated aliases:')
        for command in alias_generators.values():
            self._expand_alias_generators(command)

    def _expand_alias_generators(self, command):
        alias = command['alias']
        self.help_entries[alias] = command['help']
        for sub_alias, execute in command['function']():
            self.unique_aliases.add(sub_alias)
            self.lines.append("alias c.{sub_alias}='{execute}'".format(**locals()))

    def _add_function_generators(self, function_generators):
        """
        Builds the aliases generated by alias generator.
        """
        self.lines.append(' ')
        self.lines.append('# c generated functions:')
        for command in function_generators.values():
            self._expand_function_generators(command)

    def _expand_function_generators(self, command):
        alias = command['alias']
        self.help_entries[alias] = command['help']
        for function_name, function_lines in command['function']():
            self.unique_aliases.add(function_name)
            lines = ['function ' + function_name + ' { ']
            lines.extend(['  ' + l for l in function_lines])
            lines.append('}')
            self.lines.extend(lines)

    def _add_raw_generators(self, function_generators):
        pass
        """
        alias = command['alias']
        self.help_entries[alias] = command['help']
        for function_name, lines in command['function']():
            self.unique_aliases.add(sub_alias)
            self.lines.extend(command['function']())
        """

    def _add_help_alias(self):
        self.unique_aliases.add('help')
        self.help_entries['help'] = 'Prints this page.'
        self.lines.extend([
            ' ',
            'function c.help {',
            '  echo " "',
            '  echo "  Commands:"',
            '  echo " "',
            ])
        longest_alias = max(len(k) for k in self.help_entries)
        for name in sorted(self.help_entries):
            pad = (longest_alias - len(name)) * ' '
            alias = '  echo "    c.{name} {pad} {help_str}"'.format(name=name, pad=pad, help_str=self.help_entries[name])
            self.lines.append(alias)
        self.lines.extend(['  echo " "', '}', ' '])

    def _write_aliases(self):
        with open(settings.ALIAS_FILE, 'w') as f:
            for line in self.lines:
                f.write(line + '\n')


GLOBAL_NAMES = globals()