#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI main module
'''

import os, os.path
import sys
import re
import subprocess
import shlex
import StringIO

import cccli
from cccli.command import Command, Alias
from cccli.printer import Printer, color
from cccli.exception import *

from sjrpc.client import SimpleRpcClient
from sjrpc.utils import RpcHandler, pure
from sjrpc.core.exceptions import *

class Cli(object):
    def __init__(self, settings):
        self.settings = settings
        self.prompt = "> "
        self.rpc = None
        self.alias = Alias()

    def start(self, line=""):
        '''Start a CLI'''
        # line stuff
        if line:
            sys.stdin = StringIO.StringIO(line)
        # set interactive mode
        self.interactive = sys.stderr.isatty() and sys.stdin.isatty()
        # start printer and load history
        self.printer = Printer(self.interactive,
                               self.settings.get("forceyes", False),
                               historyfile=self.settings.get("history", ""),
                               historysize=self.settings.get("hsize", None))
        # load alias
        self.alias.load(self.settings.get("alias", ""))
        # print debug
        self.printer.debug("Interactive: %s"%self.interactive)
        self.printer.debug("Alias: %s"%self.alias)
        self.printer.debug("Loaded history: %s"%len(self.printer.history))
        # connecting
        self._connect()
        # auth
        self._auth()
        # parsing
        self._parse()
        # save history
        self.printer.history.save(self.settings.get("history", ""))

    def _connect(self):
        '''Connect to a cloud control server'''
        self.printer.debug("Connecting...")
        try:
            self.rpc = SimpleRpcClient.from_addr(self.settings["server"],
                                             self.settings["port"],
                                             enable_ssl=True,
                                             default_handler=CliHandler(),
                                             on_disconnect="quit",
                                             timeout=self.settings["timeout"]
                                        )
            self.rpc.start(daemonize=True)
        except Exception as e:
            s = "Connection failure!" if not str(e) else "Connection failure: %s"%str(e)
            raise cliError(s)
        self.printer.debug("Connected.")

    def _auth(self):
        '''Handle server authentification'''
        self.printer.debug("Authenticating...")
        try:
            self.rpc.call("authentify", self.settings["login"], self.settings["pass"])
        except Exception as e:
            s = "Authentication failure!" if not str(e) else "Authentication failure: %s"%str(e)
            raise cliError(s)
        self.printer.debug("Authenticated.")

    def _parse(self):
        '''Parse a line'''
        if self.interactive:
            prompt = "\001%s\002%s>\001%s\002 "%(color["light"],self.settings["login"],color["reset"])
        else:
            prompt = ""
        while True:
            try:
                try:
                    argv = shlex.split(self.printer.getline(prompt), comments=True)
                except ValueError as e:
                    self.printer.error("Lexer: %s"%str(e))
                    continue
                if len(argv) == 0:
                    continue
                # alias subsitution
                if argv[0] in self.alias:
                    oldargv = argv[1:]
                    argv = shlex.split(self.alias[argv[0]])
                    argv.extend(oldargv)
                self._exec_command(argv)
            except KeyboardInterrupt:
                self.printer.out("")
                continue
            except EOFError:
                break
            except SystemExit:
                break
        if self.interactive:
            self.printer.out("tcho!")

    def _exec_command(self, argv):
        '''Execute command'''
        self.printer.debug("argv: %s"%argv)
        try:
            if (argv[0][0] == "!"):
                argv[0] = argv[0][1:]
                if not len(argv[0]):
                    return
                p = subprocess.Popen(argv, close_fds=True, shell=True)
                p.wait()
                ret = p.returncode
            elif (argv[0] == "?"):
                Command(["help"], self).call()
            else:
                Command(argv, self).call()
        except cmdBadArgument as e:
            if str(e):
                self.printer.error("Bad argument: %s."%str(e))
            else:
                self.printer.error("Bad argument.")
            usage = Command.usage(argv[0])
            if usage != "":
                self.printer.out("usage: %s."%usage)
        except cmdBadName:
            self.printer.error("No command: %s."%argv[0])
        except cmdWarning as e:
            self.printer.warn("%s: %s"%(argv[0], str(e)))
        except cmdError as e:
            self.printer.error("%s: %s"%(argv[0], str(e)))
        except Exception as e:
            if cccli.debug:
                raise
            self.printer.error(str(e))
            self.printer.warn("This is a not expected error, please report it!")

class CliHandler(RpcHandler):
    '''Handle RPC incoming request'''

    @pure
    def get_tags(self, tags=()):
        if "version" in tags:
            return { "version": cccli.version }

    @pure
    def quit(self, rpc=None):
        Printer().fatal("Disconnected from server!")