"""
Flexx has a command line interface to perform some simple tasks.
Invoke it via ``python -m flexx``. Additional command line arguments
can be provided to configure Flexx, see
:func:`configuring flexx <flexx.config>`.

.. code-block:: none

"""

import sys

ALIASES = {'-h': 'help', '--help': 'help',
           '--version': 'version',
          }

class CLI:
    """ Command line interface class. Commands are simply defined as methods.
    """

    def __init__(self, args=None):
        if args is None:
            return

        command = args[0] if args else 'help'
        command = ALIASES.get(command, command)

        if command not in self.get_command_names():
            raise RuntimeError('Invalid command %r' % command)

        func = getattr(self, 'cmd_' + command)
        func(*args[1:])

    def get_command_names(self):
        commands = [d[4:] for d in dir(self) if d.startswith('cmd_')]
        commands.sort()
        return commands

    def get_global_help(self):
        lines = []
        lines.append('Flexx command line interface')
        lines.append('  python -m flexx <command> [args]')
        lines.append('')
        for command in self.get_command_names():
            doc = getattr(self, 'cmd_' + command).__doc__
            if doc:
                summary = doc.strip().splitlines()[0]
                lines.append('%s %s' % (command.ljust(15), summary))
        return '\n'.join(lines)

    def cmd_help(self, command=None):
        """ show information on how to use this command.
        """

        if command:
            if command not in self.get_command_names():
                raise RuntimeError('Invalid command %r' % command)
            doc = getattr(self, 'cmd_' + command).__doc__
            if doc:
                lines = doc.strip().splitlines()
                doc = '\n'.join([lines[0]] + [line[8:] for line in lines[1:]])
                print('%s - %s' % (command, doc))
            else:
                print('%s - no docs' % command)
        else:
            print(self.get_global_help())

    def cmd_version(self):
        """ print the version number
        """
        import sys
        try:
            import flexx
        except ImportError:
            sys.path.insert(0, '.')
            import flexx
        print(flexx.__version__)

    def cmd_info(self, port=None):
        """ show info on flexx server process corresponding to given port,
        e.g. flexx info 8080
        The kind of info that is provided is not standardized/documented yet.
        """
        if port is None:
            return self.cmd_help('info')
        port = int(port)
        try:
            print(http_fetch('http://localhost:%i/flexx/cmd/info' % port))
        except FetchError:
            print('There appears to be no local server at port %i' % port)

    def cmd_stop(self, port=None):
        """ stop the flexx server process corresponding to the given port.
        """
        if port is None:
            return self.cmd_help('stop')
        port = int(port)
        try:
            print(http_fetch('http://localhost:%i/flexx/cmd/stop' % port))
            print('stopped server at %i' % port)
        except FetchError:
            print('There appears to be no local server at port %i' % port)

    def cmd_log(self, port=None, level='info'):
        """ Start listening to log messages from a server process - STUB
        flexx log port level
        """
        if port is None:
            return self.cmd_help('log')
        print('not yet implemented')
        #print(http_fetch('http://localhost:%i/flexx/cmd/log' % int(port)))


class FetchError(Exception):
    pass

def http_fetch(url):
    """ Perform an HTTP request.
    """
    from tornado.httpclient import HTTPClient
    http_client = HTTPClient()
    try:
        response = http_client.fetch(url)
    except Exception as err:
        raise FetchError('http fetch failed: %s' % str(err))
    finally:
        http_client.close()
    return response.body.decode()


# Prepare docss
_cli_docs = CLI().get_global_help().splitlines()
__doc__ += '\n'.join(['    ' + line for line in _cli_docs])


def main():
    # Main entry point (see setup.py)
    CLI(sys.argv[1:])


if __name__ == '__main__':
    main()
