#include "Agent.h"
#include "Patch.h"
#include "Grid.h"
#include "Controller.h"
#include <fstream>
#include <string>
using namespace std;

//*************************
//***
//***Created by Clauda Ephrem		Date Jan 3, 2008
//***
//*** Edited by Vivian Jreig		Date April 3, 2008
//*** void Agent::getState () and added comments
//***
//*** Edited by Clauda Ephrem		Date April 10, 2008
//*** added two functions string Agent::getszId() and int Agent::getScore()
//***
//*** Edited by WFN on 13-May-2008
//*** Renamed "convert" function to "int2binstring"
//***
//*** Edited by WFN on 14-May-2008
//*** 1) Created new agent constructor taking an integer for strategy
//*** 2) Made the ID an integer instead of a string
//*** 3) re-wrote "Meet" method so only unpaired agents look for others
//*** 4) renamed getState to PrintState and called it from AgentIndex::GetAgentsState
//*** 5) re-introduced the #ifdef _DEBUG statements to cout.
//*** 6) removed the duplication of type count increment in "Reproduce" (since the constructor already increments)
//***
//*************************

Agent::Agent(long int iId, int iGen, Patch* phPatch, int LenOfMem, 
			 int strategyOfAgent, Controller * Cont, int Score)
{
	iID = iId;			// A binary number with N significant figures to identify the current agent
	iN = iGen;		// The significant figures of the ID used to denote the generation
    phCurrentLocation = phPatch; //The Pointer to the grid patch on which the agent currently is standing is equal to the inputed pointer of the physical patch
	agCurrentPartner = NULL;	//The agent has no current partners
	iPastPartnerId = 0;	//There's no ID for the last partner of the agent
	iCurrentScore = Score;		//Defining the current score of the agent to be equal to the inputed score
	iNumTurns = 0;				//There's no number of interactions so far with the current partner
	LengthOfMemory = LenOfMem;	//Defining the variable that stores the length of the memory array to be equal to the inputed value of LenOfMem
	CurrentOpponentMemory.clear();		//clearing the current opponent memeory
	Control = Cont;			
	IndexLocation=-3;

	switch(strategyOfAgent)		//switching to see which strategy the given agent is
	{
	case 0:		
		//if the agent's strategy is "NaiveCooperator" then increase the count of NaiveCooperator
		CurrentStrategy = NaiveCooperator;	
		StrategyArray= strategyNaiveCooperator;
		Control->grid->gridPopulation.countNaiveCooperator++;
		break;
	case 1:			
		//if the agent's strategy is "NaiveDefector" then increase the count of NaiveDefector
		CurrentStrategy = NaiveDefector;
		StrategyArray = strategyNaiveDefector;
		Control->grid->gridPopulation.countNaiveDefector++;
		break;
	case 2:				
		//if the agent's strategy is "Pavlov" then increase the count of Pavlov
		CurrentStrategy = Pavlov;
		StrategyArray = strategyPavlov;
		Control->grid->gridPopulation.countPavlov++;
		break;
	case 3:			
		//if the agent's strategy is "TFT" then increase the count of TFT
		CurrentStrategy = TFT;
		StrategyArray = strategyTFT;
		Control->grid->gridPopulation.countTFT++;
		break;
	case 4:					
		//if the agent's strategy is "WAC" then increase the count of WAC
		CurrentStrategy = WAC;
		StrategyArray = strategyWAC;
		Control->grid->gridPopulation.countWAC++;
		break;
	case 5:					
		//if the agent's strategy is "WAD" then increase the count of WAD
		CurrentStrategy = WAD;
		StrategyArray = strategyWAD;
		Control->grid->gridPopulation.countWAD++;
		break;
	default:
		#ifdef _DEBUG 
			cout<<"wrong Strategy on construct"<<endl;
		#else 
 	 	 NULL; 
	 	#endif

	}
	Control->AllAgents->Insert(this);	//Follow the above commands for all the agents so that we get a complete complete of each strategy
}



Agent::Agent(long int iId, int idSize, Patch* phPatch, int LenOfMem, 
			 Strategy strategyOfAgent, Controller * Cont, int Score)
{
	iID = iId;			// A binary number with N significant figures to identify the current agent
	iN = idSize;		// The significant figures of the ID used to denote the generation
    phCurrentLocation = phPatch; //The Pointer to the grid patch on which the agent currently is standing is equal to the inputed pointer of the physical patch
	agCurrentPartner = NULL;	//The agent has no current partners
	iPastPartnerId = 0;	//There's no ID for the last partner of the agent
	iCurrentScore = Score;		//Defining the current score of the agent to be equal to the inputed score
	iNumTurns = 0;				//There's no number of interactions so far with the current partner
	LengthOfMemory = LenOfMem;	//Defining the variable that stores the length of the memory array to be equal to the inputed value of LenOfMem
	CurrentStrategy = strategyOfAgent;	//Defining the current strategy of the afnet to be equal to the inputed strategy
	CurrentOpponentMemory.clear();		//clearing the current opponent memeory
	Control = Cont;			
	IndexLocation=-3;

	switch(strategyOfAgent)		//switching to see which strategy the given agent is
	{
	case NaiveCooperator:		//if the agent's strategy is "NaiveCooperator" then increase the count of NaiveCooperator
		StrategyArray= strategyNaiveCooperator;
		Control->grid->gridPopulation.countNaiveCooperator++;
		break;
	case NaiveDefector:			//if the agent's strategy is "NaiveDefector" then increase the count of NaiveDefector
		StrategyArray = strategyNaiveDefector;
		Control->grid->gridPopulation.countNaiveDefector++;
	break;
	case Pavlov:				//if the agent's strategy is "Pavlov" then increase the count of Pavlov
		StrategyArray = strategyPavlov;
		Control->grid->gridPopulation.countPavlov++;
		break;
	case TFT:					//if the agent's strategy is "TFT" then increase the count of TFT
		StrategyArray = strategyTFT;
		Control->grid->gridPopulation.countTFT++;
		break;
	case WAC:					//if the agent's strategy is "WAC" then increase the count of WAC
		StrategyArray = strategyWAC;
		Control->grid->gridPopulation.countWAC++;
		break;
	case WAD:					//if the agent's strategy is "WAD" then increase the count of WAD
		StrategyArray = strategyWAD;
		Control->grid->gridPopulation.countWAD++;
		break;
	default:
		#ifdef _DEBUG 
			cout<<"wrong Strategy on construct"<<endl;
		#else 
 	 	 NULL;
		#endif

	}
	Control->AllAgents->Insert(this);	//Follow the above commands for all the agents so that we get a complete complete of each strategy
}

//******************************
//*** Constructors for temporary agents
//***
//****************************

Agent::Agent()
{
}

Agent::Agent (int i)
{

}

void Agent::Decide()
{
	if (agCurrentPartner != NULL)	//checking to see if the agent is already partnered with another agent
	{
		string move="";
		int index = CastMemArrayToInt(CurrentOpponentMemory);
		move = StrategyArray[index];
		string partnerMove="";
		partnerMove = agCurrentPartner->Respond(this, move);
		UpdateScore(partnerMove, move);
		iNumTurns++;
		NewMemoryArray(partnerMove, move);
		if (iNumTurns > Control->NumPlay || partnerMove == "2" || move == "2")
		{
			agCurrentPartner->Dissociate(this);
			Dissociate(agCurrentPartner);//the move fct is here anyways
		}
	}
//	else
//		cout<<"the agent is not associated to any other agent"<<endl;
}

void Agent::Die()
//This is a commmand to distruct all agents that have an energy level less than zero 

{
	if(iCurrentScore <0)	//Checking to see if the score of the agent is less then zero; if yes then the agent will die
	{
		if (agCurrentPartner != NULL)	//checking to see if the agent is already partnered with another agent
		agCurrentPartner->Dissociate(this);		//if this agent has a partner then they should be split up
	
			switch(CurrentStrategy)		//Checking to see what straegy the agent was so that we decrease that strategy count
		{
			case NaiveCooperator:		//if the agent's strategy is "NaiveCooperator" then decrease the count of NaiveCooperator
				Control->grid->gridPopulation.countNaiveCooperator--;
				break;
			case NaiveDefector:			//if the agent's strategy is "NaiveDefector" then decrease the count of NaiveDefector
				Control->grid->gridPopulation.countNaiveDefector--;
				break;
			case Pavlov:				//if the agent's strategy is "Pavlov" then decrease the count of Pavlov
				Control->grid->gridPopulation.countPavlov--;
				break;
			case TFT:					//if the agent's strategy is "TFT" then decrease the count of TFT
				Control->grid->gridPopulation.countTFT--;
				break;
			case WAC:					//if the agent's strategy is "WAC" then decrease the count of WAC
				Control->grid->gridPopulation.countWAC--;
				break;
			case WAD:					//if the agent's strategy is "WAD" then decrease the count of WAD
				Control->grid->gridPopulation.countWAD--;
				break;
//			default:
//				cout<<"wrong Strategy"<<endl;
		}
		phCurrentLocation->Agent_departing(this);	//The dying agent should leave his current physical location
		Control->AllAgents->Remove(IndexLocation);	
		// delete [] this;
	}
}

Agent* Agent::getCurrentPartner()			//Returns the current partner of the agents
{
	return agCurrentPartner;
}

Strategy Agent::getCurrentStrategy()		//Returns the strategy of the current agent
{
	return CurrentStrategy;
}

Patch* Agent::getphCurrentLocation()		//REtyrns the physical location of the current agent
{
	return phCurrentLocation;
}


void Agent::Dissociate(Agent* agPartner)//  public dissociate(p_partner): if the current partner
//  sends this message with its correct pointer, the agent
//  saves the partner ID, sets the agCurrentPartner to
//  null, and Moves. 
{

		if (agCurrentPartner == NULL)
			return;
	if (agPartner == agCurrentPartner)
	{
		iPastPartnerId = agCurrentPartner->iID;
		agCurrentPartner = NULL;
		Move();
	}
//	else
//		cout<<"The agent calling the dissociate fct is not the current Partner"<<endl;

}


void Agent::Associate(Agent* agPartner)	//Partnering up agents and storing their respective partneres in Agent*
{
	if (agCurrentPartner != NULL)	//checking to see if the agent is already partnered with another agent
	{
//		cout<<"Agent already associated with some other agent"<<endl;
	}
	else
	{
		iNumTurns = 0;   // We initialize the number of interactions so far of the agent with the new current partner
		agCurrentPartner = agPartner; // Defining that the pointer to current partner is pointing at the agent partner
		if (agPartner->iID != iPastPartnerId) 
		{
			CurrentOpponentMemory.clear();	
		}
	}
}



void Agent::Meet()
{
		if (agCurrentPartner == NULL)	//  if unpaired
		{
			agCurrentPartner = phCurrentLocation->GetNextUnpairedAgent(this); 
			if (agCurrentPartner != NULL)
			{
			agCurrentPartner->Associate(this); //If agent finds an unpaired partner they associate together
			CurrentOpponentMemory.clear();	//Clears the memeory of the current opponent
			iNumTurns=0;	//Initilaizes the number of interactions so far with the current partner
			}
		}
}

void Agent::Move()
{
	Patch* phDestination;	//Pointer to the grid patch where the agent wants to move
	if (agCurrentPartner != NULL)
	{
		//cout<<"cannot move since the current agent is still associated with a partner"<<endl;
	}
	else	//if agent is not associated with a partner then he can move
	{
		int m = Control->getMobility();
		phDestination =Control->grid->getDestination(phCurrentLocation, m);
		phCurrentLocation->Agent_departing(this);	//agent is leaving his curretn destination
		phCurrentLocation = phDestination;	//agents new location is his desired destination
		if (phCurrentLocation->IsPatchFull())	//in case the desired destination is full the agent will re-move his location
		{
			Move();
			return;
		}
		else
		phCurrentLocation->Agent_arriving(this);
	}
}


void Agent::Reproduce()
{
	if (iCurrentScore > Control->ReproductionThreshold)	//checks if If self.score is above
//  controller.threshold
	{
	
//  The new agents ID is derived from self.ID by adding one
//  more significant binary figure and setting it to "1", 
		iN++;
		int iNewId = iID|(1<<iN);
		Agent* NewAgent = new Agent( iNewId, iN, phCurrentLocation, LengthOfMemory, CurrentStrategy, Control, iCurrentScore/2);	
		//creating the new agent identical the old but having half of the other agents score 
		iCurrentScore =  iCurrentScore/2 + iCurrentScore%2;	
		//the parent gets half the socre plus one if it is an odd number
		phCurrentLocation->Agent_arriving(NewAgent);
		NewAgent->StrategyArray = StrategyArray;
		//NewAgent->szPastPartnerId = szPastPartnerId;
	

	}
}


string Agent::Respond(Agent* agPartner, string Move)
{
	//  public function Respond(p_partner, move) returns move:
//  This is tricky since the responding agent must generate
//  its move before learning of the partner's move (encoded 
//  in the parameter passed), but then must set its own
//  score and history etc. based on both own move and
//  partner move.  Returns own move so that partner can do
//  the same.  Raises an error message if p_partner != 
//  self.partner.
	string move = "";
	if(agPartner = agCurrentPartner)
	{
		int index = CastMemArrayToInt(CurrentOpponentMemory);
		move = StrategyArray[index];
		UpdateScore(Move, move);
		NewMemoryArray (Move, move);
	}
//	else
//		cout<<"The current agent is not associated to any other agent"<<endl;
	

	return move;
}

void Agent::UpdateScore (string PartnerMove, string SelfMove) //Function to update self.score given opponent.move
{
	if (PartnerMove == "0")	//if the agents partners strategy is to defect
	{
		if (SelfMove == "1")	//and the current agents strategy is to cooperate
			iCurrentScore+= CoopAlone;	//update the agents score by adding the value of coopalone
		if (SelfMove == "0")	//if the agents strategy is also to defect
			iCurrentScore+= DefBoth;	//update the agents score by adding the value of DefBoth
		else
			iCurrentScore+= 0;
	}
	else if (PartnerMove == "1")	//if the agents partners strategy is to cooperate
	{
		if (SelfMove == "1")	//and the current agents strategy is also to cooperate
			iCurrentScore+= CoopBoth;	//update the agents score by adding the value of defect
		if (SelfMove == "0")	//if the agents strategy is to cooperate
			iCurrentScore+= DefAlone;	//update the agents score by adding the value of DefAlone
		else
			iCurrentScore+= 0;
	}
	else
		iCurrentScore+= 0;
}

void Agent::NewMemoryArray (string PartnerMove, string selfMove  )	//  Function to update the Memory Array
{
	CurrentOpponentMemory.insert(CurrentOpponentMemory.begin(), selfMove + PartnerMove);
	if (CurrentOpponentMemory.size() > LengthOfMemory)
	{	
		CurrentOpponentMemory.pop_back();
	//if we want to and it or or it it is easy to be changed
	}
}

Agent::~Agent()
{
}

int Agent::getIndexLocation()	//getting the index location of the agent
{
	return IndexLocation;
}

void Agent::SetIndexLocation ( int a)	//setting the index location of the agent to a value "a"
{
	IndexLocation = a;
}

int Agent::CastMemArrayToInt(vector<string> Mem)
{
	string temp="";
	int index = 0;
	int Length = Mem.size();
	Length--;
	while (Length >= 0)
	{
		//index += pow(2.0, (double)Length);
		index += Length<<1;
		Length--;
	}
	for (int i=0; i<Length; i++)
	{
		if (CurrentStrategy != Pavlov)
			temp += Mem.at(i).substr(1,1);
		else 
			temp += Mem.at(i);
	}
	if (temp.size()!= 0)
		index += binstring2int(temp);
	return index;
}




void Agent::printState (ofstream& outfile)	
//***** a function to print the state of the agent to a file *****
{
		outfile<<getszId()
			<<"="<<iCurrentScore
			<<" @("
			<<phCurrentLocation->getXCoordinate()
			<<","
			<<phCurrentLocation->getYCoordinate()
			<<") # ";
		switch(CurrentStrategy)
			{
				case NaiveCooperator:
					outfile<<"C   - ";
					break;
				case NaiveDefector:	//if the new agent's strategy is "NaiveDefector" then increase the count of NaiveDefector
					outfile<<"D   - ";
					break;
				case Pavlov:	//if the new agent's strategy is "Pavlov" then increase the count of Pavlov
					outfile<<"Pav - ";
					break;
				case TFT:	//if the new agent's strategy is "TFT" then increase the count of TFT
					outfile<<"TfT - ";
					break;
				case WAC:	//if the new agent's strategy is "WAC" then increase the count of WAC
					outfile<<"waC - ";
					break;
				case WAD:	//if the new agent's strategy is "WAD" then increase the count of WAD
					outfile<<"waD - ";
					break;
		}

		if (agCurrentPartner != NULL)
			{
				outfile<<agCurrentPartner->getszId()
				<<" @("
				<<agCurrentPartner->getphCurrentLocation()->getXCoordinate()
				<<","
				<<agCurrentPartner->getphCurrentLocation()->getYCoordinate()
				<<")"
				<<endl;
			}
		else
			outfile<<" NULL"<<endl;

		
		// cout<<"Agent "<<iID<<" Strategy "<<CurrentStrategy<<" Score"<<iCurrentScore<<" Position "<<phCurrentLocation<<"\n";
		
}
//*********************** 
//*** gets the integer ID 
//*** added 15-May-2008 by WFN
//***********************
//***** gets the ID of the agent as a binary string
string Agent::getszId()
{
	return int2binstring(iID, iN);
}

//int Agent::getiId()
//{
//	return iID;
//}
//
//
////***** returns the score of the agent
//int Agent::getScore()
//{
//	return iCurrentScore;
//}
//				