#!/usr/local/bin/python2.6
#
#

import sys
import time

from profeta.variable  import *
from profeta.attitude  import *
from profeta.inference  import *
from profeta.action  import *
from profeta.lib  import *
from profeta.main import *


## ---- BELIEFS REPRESENTING THE KNOWLEDGE
class obj(Belief):
    pass

class upon(Belief):
    pass

class owned(Belief):
    pass


## ---- REACTORS REPRESENTING USER COMMANDS
class say(Reactor):
    pass

class pick(Reactor):
    pass

class put(Reactor):
    pass

class kb(Reactor):
    pass

class table(Reactor):
    pass

class my(Reactor):
    pass

class tower(Reactor):
    pass

## ---- GOALS
class find_highest(Goal):
    pass

class climb_down(Goal):
    pass

## ---- ACTIONS
class show_kb(Action):
    def execute(self):
        print PROFETA.kb()


class show_table(Action):
    def execute(self):
        print map(lambda x: x[0][0], PROFETA.kb().beliefs_like(obj("_")))


class show_my(Action):
    def execute(self):
        print map(lambda x: x[0][0], PROFETA.kb().beliefs_like(owned("_")))


## ---- SENSORS
class prompt(Sensor):
    def sense(self):
        e = raw_input ("COMMAND: ")
        tokens = e.lower().split()
        # use '*' to expand tokens into a sequence of arguments
        return +say(*tokens)


if __name__ == "__main__":

    PROFETA.start()

    PROFETA.add_sensor(prompt())

    PROFETA.assert_belief(obj("cube"))
    PROFETA.assert_belief(obj("cylinder"))
    PROFETA.assert_belief(obj("prism"))

    PROFETA.assert_belief(upon("cube", "cylinder"))  # cube is upon the pyramid
    PROFETA.assert_belief(upon("prism", "cube"))     # prism is upon the cube

    # A very small natural language processor (NLP)
    +say("show", "me", "the", "table") >> [ show_table() ]
    +say("show", "the", "table") >> [ show_table() ]
    +say("show", "table") >> [ show_table() ]

    +say("show", "me", "the", "tower", "of", "X") >> [ +tower("X") ]
    +say("show", "me", "tower", "of", "X") >> [ +tower("X") ]
    +say("show", "the", "tower", "of", "X") >> [ +tower("X") ]
    +say("show", "tower", "of", "X") >> [ +tower("X") ]

    +say("show", "my", "objects") >> [ +my() ]

    +say("V", "X")        / (lambda : V in ['pick', 'get', 'catch' ]) >> [ +pick("X") ]
    +say("V", "the", "X") / (lambda : V in ['pick', 'get', 'catch' ]) >> [ +pick("X") ]
    +say("V", "a", "X")   / (lambda : V in ['pick', 'get', 'catch' ]) >> [ +pick("X") ]

    +say("V", "X")        / (lambda : V in ['put', 'release' ]) >> [ +put("X") ]
    +say("V", "the", "X") / (lambda : V in ['put', 'release' ]) >> [ +put("X") ]
    +say("V", "the", "X", "upon", "the", "Y") / (lambda : V in ['put', 'release' ]) >> [ +put("X", "Y") ]
    +say("V", "X", "upon", "the", "Y") / (lambda : V in ['put', 'release' ]) >> [ +put("X", "Y") ]
    +say("V", "the", "X", "upon", "Y") / (lambda : V in ['put', 'release' ]) >> [ +put("X", "Y") ]
    +say("V", "X", "upon", "Y") / (lambda : V in ['put', 'release' ]) >> [ +put("X", "Y") ]


    # ---------------------------------------------------------------------------
    # PLANS TO PERFORM OBJECT PICKING
    # ---------------------------------------------------------------------------
    # the object X is on the table but another object Y is upon it
    +pick("X") / (obj("X") & upon("Y", "X")) >> [ show("cannot pick"), 
                                                  show("X"),
                                                  show("since it is under the "),
                                                  show_line("Y") ]
    # the object X is on the table and upon object Y,
    # so pick the object and update the "upon" belief
    +pick("X") / (obj("X") & upon("X", "Y")) >> [ show("X"), show_line("picked"), 
                                                  -obj("X"), -upon("X", "Y"),
                                                  +owned("X") ]
    # the object X is on the table, pick the object
    +pick("X") / obj("X") >> [ show("X"), show_line("picked"), -obj("X"), +owned("X") ]
    # the object X is already owned
    +pick("X") / owned("X") >> [ show("you've still got"), show_line("X") ]
    # the object X is not on the table
    +pick("X") >> [ show("cannot pick"), show("X"), show_line("since it is not present") ]
    # ---------------------------------------------------------------------------

    # ---------------------------------------------------------------------------
    # PLANS TO PERFORM OBJECT RELEASE
    # ---------------------------------------------------------------------------
    +put("X") / owned("X") >> [ show("X"), show_line("is now on the table"), 
                                -owned("X"), +obj("X") ]
    +put("X") / obj("X") >> [ show("X"), show_line("is already on the table") ]
    +put("X") >> [ show("you don't hold"), show_line("X") ]

    # I cannot perform put if the object Y has another object Z on its top
    +put("X", "Y") / (owned("X") & obj("Y") & upon("Z", "Y") ) >> [ show("Y"),
                                                                    show("has"),
                                                                    show("Z"),
                                                                    show_line("on its top") ]
    +put("X", "Y") / (owned("X") & obj("Y")) >> [ -owned("X"),
                                                  +obj("X"),
                                                  +upon("X", "Y"),
                                                  show_line("done") ]

    +kb() >> [ show_kb() ]
    +table() >> [  ]
    +my() >> [ show_my() ]

    +tower("X") / obj("X") >> [ find_highest("X") ]
    +tower("X") >> [ show("X"), show_line("is not on the table") ]

    find_highest("X") / upon("Z", "X") >> [ find_highest("Z") ]
    find_highest("X") >> [ show_line("#### TOP  ####"), show_line("X"), climb_down("X") ]

    climb_down("X") / upon("X", "Y") >> [ show_line("Y"), climb_down("Y") ]
    climb_down("_") >> [ show_line("#### BASE ####") ]

    #PROFETA.run_shell(globals())
    PROFETA.run()

