import time  # time system tools
import copy  # object copy tools

from Message  import *
from Universe import *

################################################
#  Object Class
#
class Object:
   "Class Object, top class for most objects"

################################################
#  State Related Methods
#
# Init State Methods
#
   def initDefault(self):
      pass
#
# jump state method
#
   def jump( self, newState = '_default' ):
       if self.initState.has_key(newState):  # if newState is defined
          self._state = newState             #   change state, and
          self.initState[self._state]()      #   call proper init function
#
#  Let's see an example on how to use states:
#    
#   * Touch methods
#
#   def initPatrol(self): 
#      print 'Init Patrol State'
#   def _touch(self, s):
#      print 'I WAS TOUCHED BY ' + s
#   def attack(self, s):
#      print 'CHARGE TARGET ' + s
#
#   * Main touch method
#
#   def touch(self, ob):
#      if self.touchState.has_key(self._state):   # if _state has defined method
#         self.touchState[self._state](ob)
#      else:
#         self.touchState['_default'](ob)
#
#   * Touch method table (to insert at __init__)
#
#   self.initState['patrol'] = self.initPatrol
#   self.touchState = { 'patrol' : self.attack,
#                       '_default' : self._touch }
#
#   * To use:
#
#   x.touch('dragon')
#   x.jump('patrol')
#   x.touch('dragon')
#
#  State Related Methods
################################################

################################################
#  Tag Related Methods
#
   def tagInsert(self, tag):
      if self.tagList.count(tag) == 0: # if it doesn't exists,
         self.tagList.append(tag)      #  insert it!
#
   def tagRemove(self, tag):
      if self.tagList.count(tag) > 0:  # if exists,
         self.tagList.remove(tag)      #  remove it!
#
# Returns true (i.e., one) if object has at least one tag
#
   def tagHas(self, tag):
      return self.tagList.count(tag)
#
# Returns tag list
#
   def tagCollection(self):
      return self.tagList
#
# Returns all objects from objList with same tags as object
#
   def tagSimilar(self, objList):
      newList = []
      for obj in objList: 
         missedTag = 0
         for tag in obj.tagList:
            if self.tagList.count(tag) == 0:
               missedTag = 1
               break
         if missedTag == 0 and obj.tagList != []:
            newList.append(obj)
      return newList
#
# Returns all objects from objList with at least one tag as object
#
   def tagRelated(self, objList):
      newList = []
      for obj in objList: 
         hasTag = 0
         for tag in obj.tagList:
            if self.tagList.count(tag) > 0:
               hasTag = 1
               break
         if hasTag == 1:
            newList.append(obj)
      return newList
#
#  Returns all tags that object has, that are related to 
#   at least one other object from objList
#
   def tagListAg(self, objList):
      newList = []
      for obj in objList:
         for tag in obj.tagList:
            if self.tagList.count(tag) > 0 and \
               newList.count(tag) == 0:
               newList.append(tag)         
      return newList
#
#  Tag Related Methods
################################################

################################################
#  Event Related Methods
#
# Insert event at the end of list
#
   def newEvent(self, evtName, *args):    # Keep incoming event for processing
      if self.evtTable.has_key(evtName):  #  if event exists in evtTable,
         evtTuple = (evtName, args)       #    make the event structure, and
         self.evtQueue.append(evtTuple)   #    insert it at the queue end
#
# Returns true if queue is empty
#
   def hasEvents(self):
      return self.evtQueue != []
#
# Process next event
#
   def forceEvent(self):                  # Force next event to be processed
      evt = self.evtQueue[0]              #  get next event, and
      self.evtQueue.remove(evt)           #  remove it from queue
          # execute method with specific sequence
      apply( self.evtTable[evt[0]][0], evt[1] )
#
#  Events Related Methods
################################################

################################################
#  Messages Related Methods
#
#  Send event message to specific object (if key != 0, then it is encrypted)
#
   def msgSend(self, objId, msg, key=0):
      M = Message(msg, self.id, objId)  # create message
      if key != 0:
         M.encrypt(key)
      try:   
         _U.getObj(objId).newEvent('evtMessage', M)
      except: 
         pass
#
#  Send event message to specific list group
#
   def msgSendGroup(self, objList, msg, key=0):
      M = Message(msg, self.id, objList)  # create message
      if key != 0:
         M.encrypt(key)
      try:   
         for objId in objList:
            _U.getObj(objId).newEvent('evtMessage', M)
      except: 
         pass
#
#  Send event message to all objects
#
   def msgBroadcast(self, msg, key=0):
      M = Message(msg, self.id, 0)  # create message
      if key != 0:
         M.encrypt(key)
      try:   
         for obj in _U.objList:
            obj[1].newEvent('evtMessage', M)
      except: 
         pass
#
#  Process message event. Only does anything if ok is 1
#
   def msgReceive(self, M):
      myM = copy.copy(M)          # make a copy of message, due to possible decryptation
      obj = _U.getObj(M.senderId) # get sender object
      ok = 0
      if not obj.id in self.filterList:   # check filters
         ok = 1
         for tag in self.filterTagList:
            if obj.tagHas(tag):
               ok = 0
               break
      if ok == 1:                            
         if myM.isEncrypt == 1:           # if it is encrypted,
            myM.decrypt(self._msgKey)     #   try to decrypt it
         print self._name + ': Got new msg: \'' + myM.msg + '\' from object ' + `obj.id`
#
#  Refuse messages from object 'obj'
#
   def msgFilter(self, objId):
      self.filterList.append(objId)
#
#  Refuse messages from objects with 'tag'
#
   def msgFilterTag(self, tag):
      self.filterTagList.append(tag)
#
#  Messages Related Methods
################################################

   def __str__(self):
     return 'Obj \'' + self._name + '\' <ID:' + `self.id` + ', created ' + time.ctime(self._time) + '>'

   def __init__(self, name='N/A'):
         ### insert object into Universe List
      _U.addObj(self)           # tell Universe that there is a new object
      self.id    = _U.id        # get new id for object
      self._name = name         # object optional name
      self._time = time.time()  # insert birth time
         ### State data structures
      self.initState = { '_default' : self.initDefault 
                       }        # state method table
      self._state = '_default'  # actual state attribute
         ### Tag data structures
      self.tagList = []         # object list tag
         ### Event data structures
         # evtTable associates an event name with a tuple (function, n-args)
      self.evtTable = {'evtMessage'   : (self.msgReceive, 1)   # msg handler
                      }
      self.evtQueue = []        # keeps actual not treated events
         ### Messages data structures
      self._msgKey = 0          # keeps actual decode key, for encrypted msgs
      self.filterList = []      # refuses msgs from list id objects
      self.filterTagList = []   # refuses msgs from object with some list tag
#
#  Object Class
################################################

_U = Universe()

x = Object('x')
y = Object('y')
z = Object('z')

print '*'*80
print _U

#x.msgSend(y.id,'hello Y')
#y.msgBroadcast('bye all :-(')
x.msgSendGroup([y.id,z.id],'hello mates',4)

y._msgKey=4

#x.tagInsert('cursed')
#y.msgFilterTag('cursed')
#x.tagRemove('cursed')


while x.hasEvents():   
   x.forceEvent()
while y.hasEvents():   
   y.forceEvent()
while z.hasEvents():   
   z.forceEvent()
