from __future__ import print_function
from __future__ import unicode_literals

import os
import subprocess
import re
import sys

DEFAULT_ENGINE_DIR='/opt/opengnsys/engine'

def get_engine_dir():
    '''
    Returns tHe engine dir, where the scripts reside. This is either set by
    the user with the OG_ENGINE_DIR environmental var or uses the
    DEFAULT_ENGINE_DIR.
    '''
    engine_dir = os.environ.get('OG_ENGINE_DIR')
    if not engine_dir:
        engine_dir = DEFAULT_ENGINE_DIR

    engine_dir = os.path.abspath(engine_dir)

    if not os.path.isdir(engine_dir):
        raise EngineDirError(engine_dir)

    return engine_dir

class EngineDirError(Exception):
    def __init__(self, engine_dir):
        super(EngineDirError, self).__init__(
                "Invalid OG_ENGINE_DIR: %s" % engine_dir)

def get_module_names():
    '''
    Returns the list of module names.

    The scripts are grouped inside modules, and there's a subdir for each module
    inside the engine dir.
    '''
    engine_dir = get_engine_dir()
    files = os.listdir(engine_dir)

    # ignore files and directories starting with .
    return [filename for filename in files if filename[0] != '.' and
            os.path.isdir(os.path.join(engine_dir, filename))]


def get_scripts():
    '''
    Returns a dict where each key is a module name and the value is
    other dict with the scripts. In this dict the key is the basename
    and the value is the extension. eg:

    'py' = scripts['utils']['list-modules']
    '''
    engine_dir = get_engine_dir()
    modules = get_module_names()

    scripts = {}

    for module in modules:
        scripts[module] = {}
        directory = os.path.join(engine_dir, module)
        files = os.listdir(directory)
        for filename in files:
            # ignore no-files and hidden files
            if not os.path.isfile(os.path.join(directory, filename)) or\
                    filename[0] == '.':
                    # os.access(os.path.join(directory, filename), os.X_OK):
                continue

            script, extension = os.path.splitext(filename)
            scripts[module][script] = extension

    return scripts


def show_help(*args):
    '''
    Called by run_command() when no command is given, showing information about
    how to use ogr.
    '''
    print('''usage: %(cmd)s COMMAND [ARGS]

See %(cmd)s list to show the list of available commands.
See %(cmd)s help COMMAND for more information on a specific command.
''' % dict(cmd=args[0]))

def what_module(script_name):
    '''
    Returns the module where script_name is.
    '''
    # Removing extension
    script_without_ext = os.path.splitext(script_name)[0]

    for module, script_list in get_scripts().items():
        if script_without_ext in script_list:
            return module

    raise ScriptNotFound(script_name)

def where_is(script_name):
    '''
    Returns the path where is the script given.
    The script_name may have extension or not.
    '''
    scripts = get_scripts()
    module = what_module(script_name)
    script_without_ext = os.path.splitext(script_name)[0]
    extension = scripts[module][script_without_ext]
    script_name = script_without_ext + extension
    script_path = os.path.join(get_engine_dir(), module, script_name)

    if not os.access(script_path, os.X_OK):
        # If it's not executable
        raise ScriptNotExecutable(script_path)

    return script_path


class ScriptNotFound(Exception):
    def __init__(self, script_name):
        super(ScriptNotFound,self).__init__(
                "Script not found: %s" % script_name)

class ScriptNotExecutable(Exception):
    def __init__(self, script_name):
        super(ScriptNotExecutable,self).__init__(
                "Script is not executable: %s" % script_name)

def show_progress(progress, description):
    '''Hook to show progress in the output'''
    print('[%s]: %s' % (progress, description))

def _get_doxygen_comment(script_name):
    '''Returns a string with all doxygen documentation found in the script.
    If documentation is not found raise a NoDocumentationFound'''
    script_path = where_is(script_name)
    doxygen_expression = re.compile(r"/\*\*((?:[^/]|/(?!\*\*))*)\*/")
    result = None
    with open(script_path) as f:
        result = doxygen_expression.search(f.read())

    if not result: 
        raise DocumentationNotFound(script_path)

    # Remove '#' in the string
    return result.group(1).replace('#', '')


class DocumentationNotFound(Exception):
    def __init__(self, script_path):
        super(DocumentationNotFound,self).__init__(
                "No documentation found in: %s" % script_path)


def get_help(script_name):
    '''Receives a script and it analizes its documentation. Return a dict
    whose labels are doxygen labels and values are the next strings.
    Special case in version, it's a list of dictionary with the name,
    authors and date of each version.
    '''
    doxygen_str = _get_doxygen_comment(script_name)
    doxygen_args_re = re.compile(r"@(?P<command>\w+)\s+([^@]+)")
    spaces_re = re.compile(r"\s+")
    doxygen_list = doxygen_args_re.findall(doxygen_str)
    help_dict = dict()
    for label, value in doxygen_list:
        # Normalize the spaces
        value = spaces_re.sub(' ', value.strip())
        if label == 'version':
            if not label in help_dict:
                help_dict[label] = list()
            version = { 'name' : value}
            help_dict[label].append(version)

        elif label == 'author':
            if 'version' in help_dict:
                last_version = help_dict['version'][-1]
                if label not in last_version:
                    last_version[label] = list()

                last_version[label].append(value)
            else:
                #print("Author label found in documentation without previous\
 #version label in %s. Omiting." % script_name, file=sys.stderr)
                pass

        elif label == 'date':
            if 'version' in help_dict:
                help_dict['version'][-1][label] = value
            else:
                #print("Date label found in documentation without previous\
 #version label in %s. Omiting." % script_name, file=sys.stderr)
                pass

        else:
            if not label in help_dict:
                help_dict[label] = list()

            help_dict[label].append(value)
    '''Ale para que lo mires
    '''
    first_line = open(where_is(script_name))
    help_dict["lang"]=spaces_re.split(first_line.readline())[1]

    return help_dict
    
    

class Command:
    progress_expresion = re.compile(r"^\[(\d+)\] (.+)")

    def __init__(self, command):
        '''Create object to be runned'''
        if not command:
            show_help(command)
            return -1

        self.request = None
        if type(command) == type(list()):
            self.request = command
        elif type(command) == type(tuple()):
            self.request = list(command)
        else:
            raise InvalidArgument('command', 'Must be tuple or list')

        if len(command) == 0:
            raise InvalidArgument('command', 'Must not be empty')

        self.script_name = self.request.pop(0)
        script_path = where_is(self.script_name)
        self.request.insert(0, script_path)


        self.output = ''
        self.errput = ''
        self.return_value = None
        self.pid = 0
        self.proc = None

    def run(self, progress_cb=None):
        '''Run the process. This a block function.'''
        self.proc = subprocess.Popen(self.request, shell=False,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                bufsize=1, universal_newlines=True)

        self.pid = self.proc.pid
        while True:
            buf = self.proc.stderr.readline()
            if buf == '':
                break
            match = Command.progress_expresion.match(buf)
            if match:
                if progress_cb:
                    progress_cb(match.group(1), match.group(2))
            else:
                self.errput += buf

        self.return_value = self.proc.wait()
        self.output, unused = self.proc.communicate()

class InvalidArgument(Exception):
    def __init__(self, argument, description):
        super(InvalidArgument, self).__init__(
                "Invalid argument %s: %s" % (argument, description))


# def run_command(*args):
#     if not args:
#         show_help(args)
#         return -1
# 
#     script_name = args.pop(0)
#     engine_dir = get_engine_dir()
# 
#     for module, script_list in get_scripts():
#         if script_name in script_list:
#             script = os.path.join(engine_dir, module, script_name)
#             args.insert(0,script)
#             proc = subprocess.Popen(self.request, shell=True, stdout=PIPE, stderr=PIPE, bufsize=1)
#             output = None
#             errput = None
#             regexp = re.compile("^\[(\d+)\] (.+)")
#             while True:
#                 print "reading"
#                 output = proc.stdout.readline()
#                 if proc.poll() != None:
#                     break
#                 print "reading!!", output
#                 match = regexp.match(output)
#                 if match:
#                     # Detected an update, sent it to the server
#                     progress = match.group(1)
#                     message = match.group(2)
#                     self.send_progress_update(progress, message)
#             if proc.returncode:
#                 raise Exception("Return code: %d" % proc.returncode)
#             self.timer.cancel()
#             self.send_update("FINISHED", output)
#         except Exception, e:
#             self.timer.cancel()
#             self.send_update("ERROR", e.__str__())
#
