/// @file behavior.cc
/// @author Joshua Petitt <petitj01@tartarus.uwa.edu.au>
/// @author Stefan Schmitt <sschmitt@ee.uwa.edu.au>
/// @version 1.0
/// @date 2003
/// 
/// Implements behavior.hh.
/// 

#include "behavior/behavior.hh"
#include "state/state.hh"

namespace EyeMind {

// Explicit instantiation.
template Array<SignalLink, IN_SIGNAL_LIST_SIZE>;

// Explicit instantiation.
// Not needed since IN_SIGNAL_LIST_SIZE == OUT_SIGNAL_LIST_SIZE
//template Array<SignalLink, OUT_SIGNAL_LIST_SIZE>;

void SignalLink::Null()
{
	in = NULL; out = NULL;
	signal = 0; next_signal = 0;
	excite_on_update = false;
}


SignalLink::SignalLink()
{
	Null();
	generic = true;
	buffered = false;
	excite_on_update = false;
	Ks=1;
	Bs=0;
	decay=2;
}


SignalLink::~SignalLink()
{
}


void SignalLink::Signal(int s)
{
	next_signal = s; updated = true;
	if (!buffered) Update();
}

void SignalLink::Reset()
{
	next_signal = 0;
	signal = 0;
	updated = false;
}

void SignalLink::Decay()
{
	if(decay>0)
	{
		signal -= signal/decay + 1;	// exponential decay
		if (signal<0) signal = 0;
	}
}

bool SignalLink::Update()
{
	signal += Ks*next_signal; // linear growth
	updated = false;

	if ((out != NULL) && excite_on_update)
	{
		out->Excite(signal);
	}

	return true;
}




Behavior::Behavior()
{
	threshold = 0;
	root = false;
	active = true;
	excite_lock = false;
	timebase = 0;
	feed_signal = 0;
}


Behavior::~Behavior()
{
}


void Behavior::Timebase(float t)
{
	OSPanicIf(timebase != 0, "Timebase already set");
	timebase = t;
}


void Behavior::Excite(int signal)
{
	if (!active) 
	{
 		DEBUG_PRINT("Not active\n");
		return;
	}

	if (excite_lock) return;

	excite_lock=true;
	if(SumInputSignals() > threshold)
	{
		feed_signal = Execute();
		FeedAttached();
		DecayInputSignals();
	}
	excite_lock=false;
}


void Behavior::Feed(SignalLink& l)
{
	l.Signal(feed_signal);
}


void Behavior::FeedAttached()
{
	SignalLink* i = outlinks.Begin();
	while (i!=NULL)
	{
		Feed(*i);
		i = ++outlinks;
	};
}


int Behavior::SumInputSignals()
{
	int sum = 0;

	SignalLink* i = inlinks.Begin();
	while (i!=NULL)
	{
		sum+=i->Signal();
		i = ++inlinks;
	}

	return sum;
}

SignalLink* Behavior::FindMaxInputSignal()
{
	SignalLink* temp = NULL;
	int max = 0;

	SignalLink* i = inlinks.Begin();
	while (i != NULL)
	{
		if(i->Signal() > max)
		{
			temp = i;
			max = i->Signal();
		}
		i = ++inlinks;
	}

	if(temp->Signal())
		return temp;
	else
		return NULL;
}


SignalLink* Behavior::FindMaxNonGenericInputSignal()
{
	SignalLink* temp = NULL;
	int max = 0;

	SignalLink* i = inlinks.Begin();
	while (i != NULL)
	{
		if(!(i->Generic()) && (i->Signal()>max))
		{
			temp = i;
			max = i->Signal();
		}
		i = ++inlinks;
	}

	if(max)
		return temp;
	else
		return NULL;
}


unsigned Behavior::SignalRange(SignalLink& l)
{
	int signal = l.Signal();

	if ((signal >= (int)EXCITE_LOW_MIN) && (signal <= (int)EXCITE_LOW_MAX))
		return EXCITE_RANGE_LOW;

	if ((signal >= (int)EXCITE_MED_MIN) && (signal <= (int)EXCITE_MED_MAX))
		return EXCITE_RANGE_MED;

	if ((signal >= (int)EXCITE_HI_MIN) && (signal <= (int)EXCITE_HI_MAX))
		return EXCITE_RANGE_HI;

	return EXCITE_RANGE_NONE;
}


bool Behavior::InSignalRange(SignalLink& l, unsigned range)
{
	bool b = false;
	int signal = l.Signal();

	switch (range)
	{
	case EXCITE_RANGE_LOW:
		if ((signal >= (int)EXCITE_LOW_MIN) && (signal <= (int)EXCITE_LOW_MAX))
			b = true;
		break;
	case EXCITE_RANGE_MED:
		if ((signal >= (int)EXCITE_MED_MIN) && (signal <= (int)EXCITE_MED_MAX))
			b = true;
		break;

	case EXCITE_RANGE_HI :
		if ((signal >= (int)EXCITE_HI_MIN) && (signal <= (int)EXCITE_HI_MAX))
			b = true;
		break;
	}

	return b;
}

void Behavior::ResetInputSignals()
{
	SignalLink *l = inlinks.Begin();

	while(l!=NULL)
	{
		l->Reset();
		l = ++inlinks;
	}
}


void Behavior::DecayInputSignals()
{
	SignalLink *l = inlinks.Begin();

	while(l!=NULL)
	{
		l->Decay();
		l = ++inlinks;
	}
}


void Behavior::ResetOutputSignals()
{
	SignalLink *l = outlinks.Begin();

	while(l!=NULL)
	{
		l->Reset();
		l = ++outlinks;
	}
}


Behavior& operator>>(SignalLink& l, Behavior& o)
{
	// This operator function is only to be used for generic SignalLinks,
	// you will have to write your own function for attaching non-generic
	// SignalLinks to Behaviors. This new function will only take the
	// one type of non-generic SignalLink as parameter that you declared.
	// If you try to attach a different type of non-generic SignalLink,
	// its type is autmatically downcasted to a generic SignalLink, and
	// this function is called instead. The Generic() property of the
	// SignalLink is still false, of course.
	// So this operator function makes the following assumptions:
	//  1. Non-generic SignalLinks are only used with their specific
	//  Behaviors
	//  2. If you try something else anyway, it is seen as a mistake,
	//  rather than treating the non-generic SignalLink as a generic one
	OSPanicIf(!l.Generic(),"Non-generic SignalLink attached as generic");
	return SignalLinkOutputToBehavior(l,o);
}


Behavior& operator<<(SignalLink& l, Behavior& i)
{
	// see comment at operator>>()
	OSPanicIf(!l.Generic(),"Non-generic SignalLink attached as generic");
	return SignalLinkInputToBehavior(l,i);
}


SignalLink& operator>>(Behavior& n, SignalLink& o)
{
	// see comment at operator>>()
	OSPanicIf(!o.Generic(),"Non-generic SignalLink attached as generic");
	return BehaviorOutputToSignalLink(n,o);
}


SignalLink& operator<<(Behavior& n, SignalLink& i)
{
	// see comment at operator>>()
	OSPanicIf(!i.Generic(),"Non-generic SignalLink attached as generic");
	return BehaviorInputToSignalLink(n,i);
}


SignalLink* operator|(Behavior& s, Behavior& t)
{
	return DisconnectBehaviors(s,t);
}



Behavior& SignalLinkOutputToBehavior(SignalLink& l, Behavior& o)
{
	l.out = &o; // set output of SignalLink
	o.inlinks + l;  // add to inlinks of Behavior
	
	return o; // make chaining possible (like stream)
}


Behavior& SignalLinkInputToBehavior(SignalLink& l, Behavior& i)
{
	l.in = &i; // set input of SignalLink
	i.outlinks + l; // add to outlinks of Behavior

	l.excite_on_update = true;

	return i; // make chaining possible (like stream)
}


SignalLink& BehaviorOutputToSignalLink(Behavior& n, SignalLink& o)
{
	n.outlinks + o; // add to outlinks of Behavior 
	o.in = &n; // set input of SignalLink

	o.excite_on_update = true;

	return o; // make chaining possible (like stream)
}


SignalLink& BehaviorInputToSignalLink(Behavior& n, SignalLink& i)
{
	n.inlinks + i; // add to inlinks of Behavior
	i.out = &n; // set output of SignalLink

	return i; // make chaining possible (like stream)
}


SignalLink* DisconnectBehaviors(Behavior& s, Behavior& t)
{
	SignalLink* l;
	
	// iterate through source output SignalLinks
	s.outlinks.Begin();
	while ((l = s.outlinks++))
		if (l->out == &t) break; // check for link to target
		
	if (l) // link to target found in source output SignalLinks
	{
		l->Null();
		t.inlinks-*l;
		return s.outlinks-*l;
	}

	return NULL;
}


}; // namespace EyeMind

