#       m   
#      u    __main__.py - Sun Aug 12 17:46 CEST 2012
#  SQLite   interactive shell
#    d      part of sqmediumlite
#   e       copyright (C): nobody
#  m        

"""
Interactive shell around the sqmediumlite network interface.
Invoked by the main method of the network front-end module.
"""

import sys, os, time
import sqmedium as sqlite
from sqmedium._shellbase import Shell as baseShell, _fmt_sql_value

class Shell(baseShell):
    # modified methods
    def __init__ (self, **kwargs):
        # plug in some required module-level methods
        sqlite.SQLError = sqlite.OperationalError
        sqlite.apswversion = self.apswversion
        sqlite.sqlitelibversion = self.sqlitelibversion
        sqlite.complete = self.complete_statement
        sqlite.format_sql_value = _fmt_sql_value
        baseShell.__init__ (self, **kwargs)

    def _ensure_db(self):
        "The database isn't opened until first use.  This function ensures it is now open."
        # leave out connect flags and unset isolation_level
        if not self._db:
            if self.dbfilename is None:
                self.dbfilename=""
            self._db=sqlite.Connection(self.dbfilename, isolation_level=None)
        return self._db
    db=property(_ensure_db, baseShell._set_db, None, "The current :class:`Connection`")

    def complete_sql(self, line, token, beg, end):
        """Provide some completions for SQL

        :param line: The current complete input line
        :param token: The word readline is looking for matches
        :param beg: Integer offset of token in line
        :param end: Integer end of token in line
        :return: A list of completions, or an empty list if none
        """
        # handle ServerNotUp
        try:
            return baseShell.complete_sql(self, line, token, beg, end)
        except sqlite.ServerNotUp:
            return [] # no connection, no completion

    def process_complete_line(self, command):
        """Given some text will call the appropriate method to process
        it (eg :meth:`process_sql` or :meth:`process_command`)"""
        # arrange reconnect if network connection is interrupted
        try:
            return baseShell.process_complete_line(self, command)
        except sqlite.ServerNotUp as e:
            self._db = None
            raise

    def process_sql(self, sql, bindings=None, internal=False, summary=None):
        """Processes SQL text consisting of one or more statements

        :param sql: SQL to execute

        :param bindings: bindings for the *sql*

        :param internal: If True then this is an internal execution
          (eg the .tables or .database command).  When executing
          internal sql timings are not shown nor is the SQL echoed.
        """
        # Pysqlite back-end does like None as bindings 
        if bindings is None:
            bindings = ()
        return  baseShell.process_sql(self, sql, bindings, internal, summary)

    def handle_interrupt(self):
        """Deal with keyboard interrupt (typically Control-C).  It
        will :meth:`~Connection.interrupt` the database and print"^C" if interactive."""
        try:
            baseShell.handle_interrupt(self)
        except:
            # reset colour if interrupted while connecting
            self.write(self.stderr, self.colour.error_)
            raise

    # new commands
    def command_sleep (self, args):
        """sleep MS: sleep so many miliseconds"""
        if len (args) != 1: raise self.Error("sleep takes 1 parameter")
        time.sleep (.001 * int (args [0]))
    def command_start (self, args):
        """start: start back-end for sqmediumlite """
        if args: raise self.Error("start takes no parameters")
        sqlite.start ()
    def command_stop (self, args):
        """stop: stop back-end for sqmediumlite """
        if args: raise self.Error("stop takes no parameters")
        sqlite.stop ()
    def command_status (self, args):
        """status: print status of sqmediumlite back-end"""
        if args: raise self.Error("status takes no parameters")
        try:
            for i in self.db._do ("modstatus").items():
                self.write(self.stderr, "%s: %s\n" % i)
        except sqlite.ServerNotUp:
            self.write(self.stderr, "sqmediumlite back-end process does not exist\n")
    def command_restart (self, args):
        """restart: restart back-end for sqmediumlite """
        if args: raise self.Error("restart takes no parameters")
        if sqlite.status ():
            sqlite.stop ()
        sqlite.start ()
    def command_service (self, args):
        """service install|start|stop|remove: service for sqmediumlite back-end """
        sqlite.service (*args) # incl. parameter check
    def command_dir (self, args):
        """dir [PATH]: list database files """
        if len (args) > 1: raise self.Error("sleep takes at most 1 parameter")
        for i in self.db._do ("moddo", "dir", *args):
            self.write(self.stderr, "%s\n" % i)

    # new internal methods
    def sqlitelibversion (self):
        try:
            return self.db.sqlite_version
        except sqlite.ServerNotUp:
            return "not available"
    def apswversion (self):
        try:
            versions = self.db.apsw_version, self.db.backend_version
        except sqlite.ServerNotUp:
            versions = "version not available", sqlite.version
        return "%s, sqmediumlite %s" % versions
    def complete_statement (self, sql):
        try:
            return self.db.complete_statement (sql)
        except sqlite.ServerNotUp:
            return True # fail asap

def main():
    # make handle_exception see parameter errors
    """
    Call this to run the interactive shell.  It automatically passes
    in sys.argv[1:] and exits Python when done.

    """
    try:
        ### _,_,cmds=s.process_args(sys.argv[1:])
        ### if len(cmds)==0:
        s=Shell(args=sys.argv[1:])
        if len (sys.argv) <= 2 or sys.argv [-1].startswith ('-'):
            s.cmdloop() # if no sql or dot parameters
    except:
        v=sys.exc_info()[1]
        if getattr(v, "_handle_exception_saw_this", False):
            pass
        else:
            # Where did this exception come from?
            import traceback; traceback.print_exc()
        sys.exit(1)

if __name__ == "__main__":
    main ()