""" event.py
 Copyright (C) 1998 Aloril
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

------------------------------------------------------------
see event doc string

ething(id,name="foo",type="thing.mind")
event("look",what=ething(id,name="foo",type="thing.mind"))
event("say",what=esay(?,?))

Some examples of messages:
"event('make',what=ething('_Nisuf3',name='Nisuf',type='farmer'),loc=(20, 50, 300))"
"event('destroy', what='_Nisuf3')"
"event('move', what='_Nisuf3', loc=(-50, 50, 290))"
"event('move', what='_axe10', loc='_Nisuf3')"
"event('fire', what='_fire', loc='_house2', target='_house2')"
"""

import string,re
from types import *
id_pat=re.compile(r"_(.*)_(\d+)")

class id_unknown(Exception): pass

class event_base:
    def str_value(self, value, name=""):
        t=type(value)
        if t==StringType:
            if value[0]=="_":
                m=id_pat.match(value)
                return ''+m.group(1)+""
            return value
        elif t==TupleType and len(value)==3:
            if name!="xyz": return ""+`value`+""
        elif t==InstanceType:
            if isinstance(value,event_base): return str(value)
            elif hasattr(value,"is_thing"): return self.str_value(value.id)
        elif name=="interlinguish":
            l=[]
            for v in value:
                l.append(str(v))
            return string.join(l,"\n")
        return `value`
    def strv(self, var):
        value=self.__dict__[var]
        if value:
            return self.l.append("<"+var+">"+\
                                 self.str_value(value,var)+\
                                 "")
        else: return ""
    def reprv(self, var):
        value=self.__dict__[var]
        if value: self.l.append(var+"="+`value`)
    def type_str_(self,var):
        value=self.__dict__[var]
        t=type(value)
        if t==StringType:
            if value[0]=='_': return 'id'
            else: return 'string'
        elif t in [IntType,FloatType]:
            return 'number'
        elif t in [TupleType,ListType]:
            if len(value)==3: return 'xyz'
            else: return 'list'
        elif t==InstanceType:
            if isinstance(value,ething): return 'ething(..)'
            elif isinstance(value,esay): return 'esay(..)'
            elif isinstance(value,event): return value.generate_type_string()
            elif hasattr(value,"is_thing"): return 'id'
            elif value.__class__.__name__=="world_time": return 'time'
            else: return 'other_instance:'+value.__class__.__name__
        elif t==NoneType:
            return ""
        else:
            return 'other:'+`t`
    def type_str(self,var):
        str=self.type_str_(var)
        if str:
            self.l.append(var+":"+str)
    def __str__(self):
        beg=self.str_beg()
        name=self.xml_name
        var_list=self.var_list
        if len(var_list)<=1: return beg+""
        self.l=[]
        for var in var_list[1:]:
            self.strv(var)
        return beg+string.join(self.l,"\n")+"\n"
    def __repr__(self):
        name=self.__class__.__name__
        var_list=self.var_list
        if len(var_list)==0: return name+"()"
        elif len(var_list)==1: return name+"("+`getattr(self,var_list[0])`+")"
        self.l=[]
        for var in var_list[1:]:
            self.reprv(var)
        return name+"("+`getattr(self,var_list[0])`+","+string.join(self.l,",")+")"
    def generate_type_string(self):
        name=self.__class__.__name__
        var_list=self.var_list
        if len(var_list)==0: return name+"()"
        elif len(var_list)==1: return name+"("+`getattr(self,var_list[0])`+")"
        self.l=[]
        for var in var_list[1:]:
            self.type_str(var)
        return name+"("+`getattr(self,var_list[0])`+","+string.join(self.l,",")+")"

class event(event_base):
    #__dict__ doesn't define order
    """this is main method of communication between things
       EVENT ATTRIBUTES
       types: id string: it starts with underscore, then name, then unique integer
              Example: "_Nisuf_5"
                        12222234
                        
              1: All strings that start with underscore are id strings
              2: For debugging
              3: Separator
              4: Unique integer id

       not all these are needed (command and what are always needed)
       command: type of event: examples: "move", "make", "destroy", "say", "fire"
           type: string
       source: sender of event (world sets this if not set)
           type: id string
       target: receiver of event (you should set this for any thing
               that changes things, for example delete and change events)
           type: id string (in future also others, like: circle(loc,radius)
       desc: description of event
           type: string
       what: to what thing event relates or operates
           type: id string type string or ething or esay or 
                 string (attribute name) or (inside server actual thing)
       what_desc: description of above
           type: string
       loc: location of event
           type: id string or tuple (x,y,z)
                 will likely change to: (id string, (x,y,z)) format
       loc_desc: description of above
           type: string
       amount: how big fire, how strong visual signal, 
               how much to change object
           type: type of attribute that is changed
       time: time of event (world sets this if not set)
           type: mktime compatible tuple: (612, 1, 6, 8, 0, 0.0, 0, 6, -1)


       DIFFERENT COMMANDS:
       command "make":
         what="thing to make" as string or ething
         optional desc=for example "birth"
         optional loc=place for thing to appear (otherwise it appears in what
                      issued event)
         issuers:
           mind: event("make",what="axe", what_desc="ordinary axe")
           body: event("make", desc="birth", what=ething(...))
           house: event("make",what=fire)

       command "destroy":
         what=id string of thing to destroy
       
       command "change":
         what=what to change (is string or attribute name)
         amount=amount of change

         Here 'what' could be attribute name instead of id of thing.
         If target doesn't have that attribute it's initialized to
         given amount. Otherwise it's added to given amount. If 'what'
         is an id, then status attribute is changed.

         Target then would have id of target or in future some area or
         maybe some Python code or if not set then everything is changed.

       command "look":
         what=like change
         target=like change
         Will result in sight event, where 'what' is asked thing:
           if asked thing was attribute, then 'what' is its value
           if asked thing was id (or not defined), 
              then return all attributes as ething
           source=target in look event

       command "move":
         what=what to move
         loc=location to move into

       command "cut":
         CHEAT! (will likely change)
         what=what thing is used in cutting

       command "sight":
         what=event (for example move, destroy or make) or
              ething

       command "say":
         what=esay

       command "sound" (not used yet):
         what=event

       command "fire":
         what=fire
         loc=place of fire (for observers)
         target=target of fire
         amount=how big it is

       command "extinguish":
         what=fire to extinguish
         target=same as what

       command "goal":
         what=resulting event list from decision, example:
             [event('move',source='_farmer',target='_farmer',what='_farmer',
             loc=(12.5431347914, 95.4016955914, 265.44628788))]
         what_desc=decision derivation string, example: 
             goal('find place for home and build it').
             goal('find place for home and make it').
             goal('find place for home not too near or far from others').
             find_place()

       command "illegal":
         what=event
         what_desc=description about what's wrong: source_xyz, target_xyz

       command "debug":
         what=on or off

       command "NYI" (Not Yet Implemented):
         desc=description about what should have been implemented

       command "imaginary":
         desc=description about what events happened ;-)

       command "empty" (NOP, not really used)


       metaevents for engine.py:

       command "step" (run one tick in world)
    """
    var_list=["command", "source", "target", "desc", "what", "what_desc", 
              "loc", "loc_desc", "amount", "time"]
    is_event=1
    xml_name="event"
    def __init__(self, command, source=None, target=None, desc=None,
                 what=None, what_desc=None,
                 loc=None, loc_desc=None, amount=None, time=None):
        self.command=command
        self.source=source
        self.target=target
        self.desc=desc
        self.what=what
        self.what_desc=what_desc
        self.loc=loc
        self.loc_desc=loc_desc
        self.amount=amount
        self.time=time
    def str_beg(self):
        return "<"+self.xml_name+' command="'+self.command+'">'
    def copy(self):
        e=event(self.command,
                self.source,
                self.target,
                self.desc,
                self.what,
                self.what_desc,
                self.loc,
                self.loc_desc,
                self.amount,
                self.time)
        try:
            if hasattr(e.what,"is_event"):
                e.what=e.what.copy()
        except AttributeError:
            pass
        return e
    def replace_thing_with_id(self):
        if hasattr(self.source,"is_thing"): self.source=self.source.id
        if hasattr(self.target,"is_thing"): self.target=self.target.id
        if hasattr(self.what,"is_thing"): self.what=self.what.id
        elif hasattr(self.what,"is_event") or isinstance(self.what,esay):
            self.what=self.what.replace_thing_with_id()
        if hasattr(self.loc,"is_thing"): self.loc=self.loc.id
        return self
    def replace_id_with_thing(self,find_thing):
        try:
            if type(self.source)==StringType and self.source[0]=='_':
                self.source=find_thing(self.source)
            if type(self.target)==StringType and self.target[0]=='_':
                self.target=find_thing(self.target)
            if type(self.what)==StringType and self.what[0]=='_':
                self.what=find_thing(self.what)
            elif hasattr(self.what,"is_event") or isinstance(self.what,esay):
                self.what=self.what.replace_id_with_thing(find_thing)
            elif isinstance(self.what,ething):
                t=self.what
                if hasattr(t,"copy") and \
                   type(t.copy)==StringType and \
                   t.copy[0]=='_':
                    t.kw['copy']=t.copy=find_thing(t.copy)
            if type(self.loc)==StringType and self.loc[0]=='_':
                self.loc=find_thing(self.loc)
            return self
        except id_unknown, id:
            raise id_unknown, (id[0], self)

class ething(event_base):
    """
       first is id or when thing doesn't yet exist, then use name as id
       type: what kind of thing is going to be created (example: "farmer")
       other attributes are attributes for thing (like: sex="female")

       Examples:
       ething("Nisuf",type="farmer",sex="male")
       ething("_Nisuf3",name="Nisuf",type="thing.vbody.body.farmer",sex="male")
    """
    var_list=["id", "name", "type"]
    xml_name="thing"
    def __init__(self, id, **kw):
        if not kw.has_key("name"):
            kw["name"]=id
        self.var_list=["id"]+kw.keys()
        self.id=id
        self.__dict__.update(kw)
        self.kw=kw
    def str_beg(self):
        return "<"+self.xml_name+">"+self.str_value(self.id)+"\n"

class esay(event_base):
    """
       verb: string
       subject: string or id string
       object: string or id string
       adjective: string
       type: string
       string: string (this is for PC to PC communication)

       examples: 
       esay('',verb='own',subject='_Nisuf',object='_house')
       esay('',verb='know',subject='axe',object='smithy')
       esay('',verb='know',subject='smithy',object=(20, 10, 305))
       esay('',verb='learn',subject='chop trees',object=
            "cut_something(self,'chop trees', 'winter', 'forest','axe')")
    """
    var_list=["string","interlinguish",
              "verb","subject","object","adjective","type"]
    xml_name="say"
    def __init__(self, string="", interlinguish=[],
                 verb="", subject="", object="", adjective="", type=""):
        self.string=string
        self.interlinguish=interlinguish
        self.verb=verb
        self.subject=subject
        self.object=object
        self.adjective=adjective
        self.type=type
    def str_beg(self):
        return "<"+self.xml_name+">"+self.string
    def replace_thing_with_id(self):
        if hasattr(self.subject,"is_thing"): self.subject=self.subject.id
        if hasattr(self.object,"is_thing"): self.object=self.object.id
        return self
    def replace_id_with_thing(self,find_thing):
        sub=self.subject
        if type(sub)==StringType and len(sub) and sub[0]=='_':
            self.subject=find_thing(sub)
        obj=self.object
        if type(obj)==StringType and len(obj) and obj[0]=='_':
            self.object=find_thing(obj)
        return self

def get_dict_func(self, dict, func_str, func_undefined):
    try:
        return dict[func_str]
    except KeyError:
        try:
            func=dict[func_str]=getattr(self,func_str)
        except AttributeError:
            func=dict[func_str]=func_undefined
        return func

def get_event_func(self,e):
    return get_dict_func(self,self.event_dict,
                         e.command+"_event",self.undefined_event)

def events_replace_thing_with_id(self, input):
    input2=map(event.copy,input)
    return map(event.replace_thing_with_id,input2)
def events_replace_id_with_thing(self, input):
    return map(event.replace_id_with_thing,
               input,[self.find_thing_by_id]*len(input))

def xml_event_list(el,time=None):
    l=map(str,el)
    if time:
        return ''+string.join(l,"\n")+""
    return ""+string.join(l,"\n")+""

    Source: geocities.com/siliconvalley/station/4279/src

               ( geocities.com/siliconvalley/station/4279)                   ( geocities.com/siliconvalley/station)                   ( geocities.com/siliconvalley)