def isAttributeSpecifier(x):
    return type(x)==tuple

def attributeSpecifier(x):
    return x[0]

def makeAttributeSpecifier(x):
    return (x,)

class Feature(object):
	"""
	Feature:	

	Attributes:
	attribute:	 --
	value:	 --
	"""
	def __init__(self,attribute=None,value=None):
		self.__attribute=attribute
		self.__value=value
                # don't worry about descriptions that are featureless
                if isinstance(value,Description) and value.features==[]:
                    self.__value = value.base

	# accessors for Feature
	def get_attribute(self):
		return self.__attribute
	def set_attribute(self,attribute):
		self.__attribute=attribute
	attribute=property(get_attribute,set_attribute)

	def get_value(self):
		return self.__value
	def set_value(self,value):
		self.__value=value
	value=property(get_value,set_value)

        def __repr__(self):
            return '<Feature: ' + repr(self.attribute) + ':' + repr(self.value) + '>'

class Description(object):
	"""
	Description:	

	Attributes:
	base:	 --
	features:	 --
	"""
	def __init__(self,base=None,features=list()):
            # print "Initializing with ", base, "; features=",features
            self.__base=base
            self.__features=list(features) # hmm...

	# accessors for Description
	def get_base(self):
            return self.__base
	def set_base(self,base):
            self.__base=base
	base=property(get_base,set_base)

	def get_features(self):
            return self.__features
	def set_features(self,features):
            self.__features=features
	features=property(get_features,set_features)

        def all_abstractions(self):
            return all_abstractions(self.base)

        def __repr__(self):
            return '<Description: '  + repr(self.base) + ' ' + repr(self.features) + '>'

class Prediction(object):
	"""
	Prediction:	

	Attributes:
	base:	 --
	pattern:	 --
	start:	 --
	next:	 --
	description:	 --
	"""
	def __init__(self,base=None,pattern=None,start=None,next=None,features=list()):
            # print "Init. prediction: ", id(self), '; base=', base, "; pat=", pattern,"; start=",start,"; next=",next,";features=",features
            self.__base=base
            self.__pattern=pattern
            self.__start=start
            self.__next=next
            self.__features=list(features) # hmm...

	# accessors for Prediction
	def get_base(self):
            return self.__base
	def set_base(self,base):
            self.__base=base
	base=property(get_base,set_base)

	def get_pattern(self):
		return self.__pattern
	def set_pattern(self,pattern):
		self.__pattern=pattern
	pattern=property(get_pattern,set_pattern)

	def get_start(self):
		return self.__start
	def set_start(self,start):
		self.__start=start
	start=property(get_start,set_start)

	def get_next(self):
		return self.__next
	def set_next(self,next):
		self.__next=next
	next=property(get_next,set_next)

	def get_features(self):
		return self.__features
	def set_features(self,features):
		self.__features=features
	features=property(get_features,set_features)

        def target(self):
            spec = self.pattern[0]
            if isAttributeSpecifier(spec):
                base = self.base
                attribute = attributeSpecifier(spec)
                value = attribute_value(base,attribute)
                if (attribute==None):
                    error("Not an attribute")
                else:
                    return value
            else:
                return spec

class DMAP(object):
	"""
	DMAP:	

	Attributes:
	anytimePredictions:	 --
	dynamicPredictions:	 --
	position:	 --
	callBacks:	 --
	seen:	 --
	complete:	 --
	"""
        def __init__(self):
            self.__anytimePredictions={}
            self.__dynamicPredictions={}
            self.__position=0
            self.__callBacks={}
            self.__seen=list()
            self.__complete=list()

	# accessors for DMAP
	def get_anytimePredictions(self):
            return self.__anytimePredictions
	def set_anytimePredictions(self,anytimePredictions):
            self.__anytimePredictions=anytimePredictions
        anytimePredictions=property(get_anytimePredictions,set_anytimePredictions)
            
	def get_dynamicPredictions(self):
            return self.__dynamicPredictions
	def set_dynamicPredictions(self,dynamicPredictions):
            self.__dynamicPredictions=dynamicPredictions
        dynamicPredictions=property(get_dynamicPredictions,set_dynamicPredictions)

	def get_position(self):
            return self.__position
	def set_position(self,position):
            self.__position=position
        position=property(get_position,set_position)

	def get_callBacks(self):
            return self.__callBacks
	def set_callBacks(self,callBacks):
            self.__callBacks=callBacks
        callBacks=property(get_callBacks,set_callBacks)

        def defineCallBack(self,class,procedure):
            cbs = self.callBacks.get(class,[])
            cbs.remove(procedure)
            self.callBacks[class] = cbs + procedure

	def get_seen(self):
            return self.__seen
	def set_seen(self,seen):
            self.__seen=seen
        seen=property(get_seen,set_seen)

	def get_complete(self):
            return self.__complete
	def set_complete(self,complete):
            self.__complete=complete
        complete=property(get_complete,set_complete)

        def clear(self,anytime=0,callbacks=0):
            self.position=0
            self.seen=list()
            self.complete=list()
            self.dynamicPredictions={}
            if (anytime==1):
                self.anytimePredictions={}
            if (callbacks==1):
                self.callBacks={}

        def parse(self,sentence):
            for word in sentence:
                self.position = self.position + 1
                self.reference(word,self.position,self.position)

        def reference(self,item,start,end):
            print "Referencing" ,item, "from", start, "to", end
            # print 'All abstractions of ',item,' are ',all_abstractions(item)
            for abstraction in all_abstractions(item):
                for prediction in self.anytimePredictions.get(abstraction,list()):
                    self.advance(prediction,item,start,end)
                for prediction in self.dynamicPredictions.get(abstraction,list()):
                    self.advance(prediction,item,start,end)
                for callback in self.callBacks.get(abstraction,list()):
                    callback(item,start,end)

        def advance(self,prediction,item,start,end):
            # print 'advancing', prediction.pattern,' on ',item,' from start=',start
            if (prediction.next==None) or (prediction.next==start):
                # intialize prediction values
                base = prediction.base
                pattern = prediction.pattern[1:]
                start = start
                if (prediction.start!=None):
                    start = prediction.start
                # print "extending...", id(prediction), 'with features=',prediction.features
                features=self.extend(prediction,item)
                # print "features are now ..." , features
                # reference or create new
                if (pattern==[]):
                    self.reference(self.find(base,features),start,end)
                else:
                    self.indexDynamic(Prediction(base,pattern,start,(self.position+1),features))

        def find(self,base,features):
            # print "The features are:", features
            return Description(base,features)
            # return base
        
        def extend(self,prediction,item):
            specialization=prediction.pattern[0]
            # features=prediction.features
            # print 'pattern:',prediction.pattern, " features",prediction.features,'spec:',specialization,' isattr:', isAttributeSpecifer(specialization)
            if isAttributeSpecifer(specialization):
                itemis = item
                if isinstance(itemis,Description):
                    itemis = itemis.base
                if isa(prediction.target(),itemis):
                    return features
                else:
                    #print "Appending ", item
                    fea = Feature(attributeSpecifier(specialization),item)
                    #print "Appending ", fea
                    prediction.features.append(fea)
                    #print "Features are now: ",prediction.features
                    return prediction.features
            else:
                return prediction.features
                                     
        def associate(self,base,pattern):
            if base==pattern[0]:
                print "Can't associate ", base, "with itself."
            else:
                prediction = Prediction(base=base,pattern=pattern)
                self.indexAnytime(prediction)
        
        def indexAnytime(self,prediction):
            target = prediction.target()
            predictions = self.anytimePredictions.get(target,list())
            predictions.append(prediction)
            self.anytimePredictions[target] = predictions

        def indexDynamic(self,prediction):
            target = prediction.target()
            predictions = self.dynamicPredictions.get(target,list())
            predictions.append(prediction)
            self.dynamicPredictions[target] = predictions