#!/usr/bin/env python
import code
import optparse
import os
import sys
import StringIO
import traceback

class EPython(object):
    def __init__(self, globals_, locals_):
        self.lines   = []
        self.globals = globals_
        self.locals  = locals_

    def reset(self):
        self.lines = []

    def prompt(self):
        if self.lines:
            return "PROMPT:..>"
        else:
            return "PROMPT:>>>"

    def handle(self, line):
        if line.startswith('ENTER:'):
            self.push(line[6:-1])
        elif line.startswith('COMPLETE:'):
            self.complete(line[9:-1])
        else:
            print "ERROR:invalid input"

    def complete(self, s):
        candidates = []
        names      = s.split('.')
        obj        = None
        i          = 0
        name       = names[0]
        if len(names) > 1:
            while i < len(names):
                name = names[i]
                if obj is None:
                    if name in self.locals:
                        obj = self.locals[name]
                    elif name in self.globals:
                        obj = self.globals[name]
                    else:
                        break
                else:
                    if hasattr(obj, name):
                        obj = getattr(obj, name)
                    else:
                        break
                i += 1

        if i == len(names) - 1:
            base = '.'.join(names[:-1])
            if base:
                base += '.'
            if obj is not None:
                newnames = dir(obj)
            else:
                newnames = set()
                newnames.update(dir(__builtins__))
                newnames.update(self.locals)
                newnames.update(self.globals)
            candidates = [base + x for x in newnames if x.startswith(name)]
        print "COMPLETIONS:%s" % ('|'.join(candidates))

    def push(self, s):
        self.lines.append(s)
        s2 = '\n'.join(self.lines)
        try:
            code_obj = code.compile_command(s2)
            if code_obj is None:
                return
            try:
                exec code_obj in self.globals, self.locals
            except Exception, e:
                print str(e) + "\n" + traceback.format_exc()
        except (SyntaxError, OverflowError, ValueError), e:
            print str(e) + "\n" + traceback.format_exc()
        self.reset()

    def main(self):
        while True:
            print '\n' + self.prompt()
            sys.stdout.flush()
            line = sys.stdin.readline()
            if not line:
                break
            self.handle(line)

if __name__ == "__main__":
    stanzas = []
    def add_eval(option, opt, value, parser):
        stanzas.append(('eval', value))
    def add_path(option, opt, value, parser):
        stanzas.append(('path', value))
    parser = optparse.OptionParser()
    parser.add_option('-e', '--eval', type='string', action='callback', callback=add_eval)
    parser.add_option('-r', '--run', type='string', action='callback', callback=add_path)
    parser.add_option('-p', '--pipe', action='store_true')
    parser.parse_args()
    del parser, add_path, add_eval

    __EP = EPython(globals_=globals(), locals_=locals())
    sys.path.insert(0, os.getcwd())
    for (type_, value) in stanzas:
        #break
        if type_ == 'eval':
            exec value in __EP.globals, __EP.locals
            __EP.push(value)
        elif type_ == 'path':
            f = open(value, 'r')
            exec f in __EP.globals, __EP.locals
            f.close()
        else:
            raise Exception, 'how can this happen?'
    del stanzas

    __EP.reset()
    __EP.main()