#
# module holmes.py
#
# main module; implements interactive prompt, etc.
# see holmes/holmes.py and holmes.doc for more info;
##############################################################




import match
from kbase import *
import forward
import backward
for i in '57': exec('import backwrd'+i)
import forward2




def holmes():
    print '-Holmes3 inference engine-'
    kbase = Kbase().init()                  # keeps one kbase
    backchain = backward.backward           # default back chainer
    backwrd1  = backward
    forchain  = forward.forward             # default fwd chainer
    forward1  = forward

    while 1:
        x = raw_input('holmes> ')

        if x[0:2] == '+-':                  # start forward chaining
            facts = internal(x[2:])         # +- man plato, woman wanda
            forchain(kbase, facts)
        
        elif x[0:2] == '?-':                # start backward chaining
            goal = internal(x[2:])          # ?- mortal ?x
            backchain(kbase, goal)

        elif x[0:2] == '++':                # forward chain, with 'filter'
            facts = internal(x[2:])         
            facts, filter = facts[0:len(facts)-1], facts[len(facts)-1]
            forchain(kbase, facts, filter)

        elif x[0:2] == '??':                # backward, with print mode
            goals = internal(x[2:])
            size = len(goals)
            goal = goals[0:size-2]
            backchain(kbase, goal, (goals[size-2][0], goals[size-1][0]))

        elif x != '' and x[0] == '?' and x[1] in '157':
            backchain = eval('backwrd'+x[1]).backward

        elif x != '' and x[0] == '+' and x[1] in '12':
            forchain = eval('forward'+x[1]).forward

        elif x[0:2] == '@=':                # load a kbase file: @- file
            kbase = Kbase().init(x[2:])
        
        elif x[0:2] == '+=':                # add a rule interactively
            kbase.add_rule(internal_rule(x[2:]))

        elif x[0:2] == '-=':
            kbase.remove_rule(x[2:])        # remove a rule by id interactive
        
        elif x[0:2] == '=@':                # save current kbase to a file
            kbase.save_rules(x[2:])

        elif x[0:2] == '@@':                # browse the kbase: !- if, mortal
            kbase.browse_pattern(x[2:])     # 1 or 2 optional args
        
        elif x[0:2] == '!-':                # run a statement: !- kbase = k1
            exec(strip(x[2:]))              # will also print an expr value
     
        elif x == 'help':
            list_commands()                 # command help

        elif x == 'stop':                   # exit command line
            break
        
        elif x != '':                       # ignore blank lines
            print 'what?'




def list_commands():
    print 'I understand these command forms:'
    print
    for x in \
        ['+- fact, fact..    --forward chain',             \
         '?- goal, goal..    --backward chain',            \
         '++ fact,.., filter --forward, with filter',      \
         '?? goal,.., x,y    --backward, with print mode', \
         '?n   (n=1..7)      --use back chainer variant',  \
         '+n   (n=1..2)      --use forward chain variant', \
         ' ',                                              \
         '@= file            --load a kbase file',         \
         '+= rule x if y..   --add a rule to kbase',       \
         '-= id              --delete kbase rule',         \
         '=@ file            --save kbase to file',        \
         '@@ part, value     --browse kbase',              \
         ' ',                                              \
         '!- statement       --run Python stmt or expr',   \
         'help               --get this listing',          \
         'stop               --exit holmes',               \
         ' '                                               \
        ]: print x                                   




holmes()     # run shell immediately when module is imported
             # note: code below this not yet available in holmes shell





#############################################################
# here, we define 2 ways to redirect input/output, 
# from files and strings;   neither of these is 
# necessary on most platforms;  just use:
#   python holmes.py < file1 > file2
#############################################################




import sys



def redirect(files):
    global save_in, save_out
    if files:
        import sys
        input = open(files[0], 'r')                 # a file object/instance
        save_in   = sys.stdin
        sys.stdin = input                           # preopened file obj
        if len(files) == 2:
            output = open(files[1], 'w')
            save_out   = sys.stdout
            sys.stdout = output



def restore(files):
    global save_in, save_out
    if files:
        sys.stdin.close()
        sys.stdin = save_in                         # restore terminal i/o
        if len(files) == 2:
            sys.stdout.close()
            sys.stdout = save_out



def test(*files):                   # 0 args = interactive
    redirect(files)                 # 1 arg  = stdin name
    holmes()                        # 2 args = stdin, stdout
    restore(files)




#
# using strings for i/o, via classes
#



class Input:
    def init(self, string):
        self.data = string; return self
    
    def read(self, *size):
        if not size:
            res, self.data = self.data, ''
        else:
            res, self.data = self.data[:size], self.data[size:]
        return res
    
    def readline(self):
        if '\n' in self.data:
            where = index(self.data, '\n')
            res, self.data = self.data[:where+1], self.data[where+1:]
        else:
            res, self.data = self.data, ''
        return res



class Output:
    def init(self):
        self.data = ''; return self
    def flush(self):
        return None
    def write(self, str):
        self.data = self.data + str




def test_strings(input):
    source = Input().init(input)
    target = Output().init()

    prior_in, prior_out   = sys.stdin, sys.stdout       # save file objects
    sys.stdin, sys.stdout = source, target              # use class objects
    
    holmes()

    sys.stdin, sys.stdout = prior_in, prior_out
    return target.data                                  # return output str
    




