#!/usr/bin/python3

import readline
import clicmd
import shlex
import sys
import traceback

class Cmdline(object):
    def __init__(self):
        self.contexts = {}
        self.prompt = "> "
        self.context = None
        self.completions = []

        # Call the completer when tab is hit
        readline.set_completer(self.complete)
        readline.parse_and_bind('tab: complete')

        readline.set_completion_display_matches_hook(self.display_matches)

        # remove some word breaks
        delims = ' \t\n'
        readline.set_completer_delims(delims)

    def display_matches(self, sustitution, matches, longest_match_length):
        print()
        for m in matches:
            print("   %s"%m)
        print("%s%s"%(self.prompt, readline.get_line_buffer()),
              end = "", flush = True)
        readline.forced_update_display()

    def add_context(self, name, ctx):
        assert(not name in self.contexts), "duplicate context name %s"%(name)
        self.contexts[name] = ctx

    def set_context(self, name):
        self.context = self.contexts[name]

    def set_prompt(self, prompt):
        self.prompt = prompt

    def complete(self, text, state):
        response = None

        # state is not 0, the list of completions is already stored in
        # self.completions[]: just return the next one
        if state != 0:
            if state >= len(self.completions):
                return None
            return self.completions[state]

        # else, try to build the completion list
        try:
            line = readline.get_line_buffer() # full line
            end = readline.get_endidx() # cursor

            #print("<%s>"%origline[:end])
            in_buf = line[:end]

            tokens = shlex.split(in_buf, comments = False) # XXX check all calls to shlex
            # whitespace after the first token, it means we want to complete
            # the second token
            if len(tokens) == 0 or not in_buf.endswith(tokens[-1]):
                tokens.append("")
            completions = self.context.complete(tokens)

            # Build the completion list in the readline format (a list of
            # string). It may include the help if tab is pressed twice.
            self.completions = []
            completion_type = readline.get_completion_type()
            # completion_key = readline.get_completion_invoking_key() # XXX
            completion_key = 0
            if chr(completion_type) == "?":
                for c in completions:
                    help_str = c.cmd.get_help()
                    if c.token == "":
                        self.completions.append(c.cmd.to_expr() + ": " + help_str)
                    elif help_str == "":
                        self.completions.append(c.token)
                    else:
                        self.completions.append(c.token + ": " + help_str)
                # check if it matches the command, in this case display [return]
                # XXX does it work well?
                result = None
                for cmd in self.context:
                    result = cmd.match(tokens)
                    if result != None:
                        self.completions.append("[return]")
                        break
            else:
                for c in completions:
                    if c.token == "":
                        continue
                    if c.terminal == True:
                        self.completions.append(c.token + " ")
                    else:
                        self.completions.append(c.token)

            if len(self.completions) == 0:
                return None

            return self.completions[0]

        except:
            traceback.print_exc()

        return None

    def input_loop(self):
        line = ''
        while line != 'stop':
            try:
                line = input(self.prompt)
            except KeyboardInterrupt:
                print()
                continue
            except EOFError:
                print()
                return
            tokens = shlex.split(line, comments = True) # XXX
            if len(tokens) == 0:
                continue
            assert(self.context != None), "context not set, use set_context()"
            result = None
            for cmd in self.context:
                result = cmd.match(tokens)
                if result != None:
                    break
            if result == None:
                print("Invalid command or syntax error")
            else:
                assert(cmd.cb != None), \
                    "no callback function for %s"%(cmd)
                cmd.cb(self, result)


