//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $Workfile:     $
// $Date:         $
//
//------------------------------------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#pragma warning (disable:4786)
#include "PlrPersist.h"
#include "TextFile.h"

#include <map>
#include <string>
using namespace std;

//------------------------------------------------------------------------------------------------------
// Function:	CPlrPersist::generate
// Purpose:	fills in the fields of this with the data in the given CPlayer object
// Input:	cp - the player object to get data from
//------------------------------------------------------------------------------------------------------
void CPlrPersist::generate(CPlayer& cp)
{
	kills=deaths=timeon=0;
	valid=true;
	WONID=cp.WONID;
	matches=1;
	
	lastplayed=cp.logofftime;

	//do perteam stuff
	CTimeIndexedList<int>::iterator teamiter=cp.teams.begin();
	for (teamiter;teamiter!=cp.teams.end();++teamiter)
	{
		int tdt=teamiter->data;
		kills+=cp.perteam[teamiter->data].kills;
		deaths+=cp.perteam[teamiter->data].deaths;
		timeon+=cp.perteam[teamiter->data].timeon;

		map<string,int>::iterator it;
		it=cp.perteam[teamiter->data].weaponKills.begin();
		for (it;it!=cp.perteam[teamiter->data].weaponKills.end();++it)
		{
			string name=it->first;
			int kills=it->second;
			weapmap[name]+=kills;
		}
	}

	CTimeIndexedList<player_class>::iterator clsit=cp.allclassesplayed.begin();
	for (clsit;clsit!=cp.allclassesplayed.end();++clsit)
	{
		string classname=plrClassNames[clsit->data];
		classmap[classname]+=cp.allclassesplayed.howLong(clsit->data);
	}
	
	CTimeIndexedList<string>::iterator nameiter;
	for (nameiter=cp.aliases.begin();nameiter!=cp.aliases.end();++nameiter)
	{
		nickmap[nameiter->data]+=cp.aliases.howLong(nameiter->data);
	}

	pair<time_t,time_t> startstop;
	startstop.first=cp.logontime;
	startstop.second=cp.logofftime;

	playtimes.push_back(startstop);
}

//------------------------------------------------------------------------------------------------------
// Function:	CPlrPersist::merge
// Purpose:	 merges the stats of another CPlrPersist object into this one.
//	This is the key operation of this class.  This is how player stats are kept up 
//	to date over time.  If the two data files have playtimes that overlap, they 
//	are not merged (unless the mergeOverlaps flag is true)
// Input:	other - the CPlrPersist object that we want to merge into this one
//				mergeOverlaps - if true, overlapping playtimes are ignored. 
//------------------------------------------------------------------------------------------------------
void CPlrPersist::merge(CPlrPersist& other,bool mergeOverlaps)
{
	if (!other.valid)
		return;	//don't modify
	if (WONID!=other.WONID)
	{
		g_pApp->warning("merging stats for two different WONIDs (%lu, %lu)",WONID,other.WONID);
		
	}
	else
	{
		//do playtimes first to see if overlaps occur
		list<pair<time_t,time_t> >::iterator itOther=other.playtimes.begin();
		for (itOther;itOther!=other.playtimes.end();++itOther)
		{
			list<pair<time_t,time_t> >::iterator overlap=timesOverlap(itOther->first,itOther->second);
			time_t overlapSecond=overlap->second;
			time_t overlapFirst=overlap->first;
			if (mergeOverlaps || overlap==playtimes.end())
				playtimes.push_back(*itOther);
			else
			{
				g_pApp->warning("not merging stats for WON ID# %lu, playtime ranges overlap\n\t((%lu-%lu) overlaps with (%lu-%lu))",WONID,itOther->first,itOther->second,overlap->first,overlap->second);
				return;
			}
		}
	}


	matches+=other.matches;
	kills+=other.kills;
	deaths+=other.deaths;
	timeon+=other.timeon;
	if (other.lastplayed > lastplayed)
		lastplayed=other.lastplayed;

	//do names
	map<string,int>::iterator it;
	it=other.nickmap.begin();
	for (it;it!=other.nickmap.end();++it)
	{
		string name=it->first;
		int time=it->second;
		nickmap[name]+=time;
	}
	
	//do weapons
	it=other.weapmap.begin();
	for (it;it!=other.weapmap.end();++it)
	{
		string name=it->first;
		int kills=it->second;
		weapmap[name]+=kills;
	}
	
	//do classes
	it=other.classmap.begin();
	for (it;it!=other.classmap.end();++it)
	{
		string name=it->first;
		int time=it->second;
		classmap[name]+=time;
	}

}

//------------------------------------------------------------------------------------------------------
// Function:	CPlrPersist::read
// Purpose:	 fills in the fields of this by reading data out of a file
// Input:	f - the file from which to read the data
//------------------------------------------------------------------------------------------------------
void CPlrPersist::read(CTextFile& f)
{
	if (!f.isValid())
	{
		kills=deaths=timeon=0; WONID=-1;
		valid=false;
		return;
	}

	if(WONID==-1)
	{
		//parse it out of f;
		string s=f.fileName();
		char buf[100];
		int startpos=s.find_last_of(g_pApp->os->pathSeperator());
		int endpos=s.find_last_of(".");
		if (endpos == -1)
			return;
		if (startpos==-1)
			startpos=0;
		
		s.copy(buf,(endpos-startpos),startpos);
		buf[endpos-startpos]=0;
		WONID=strtoul(buf,NULL,10);
		if (!WONID)
		{
			WONID=-1;
			valid=false;
			return;
		}
			

	}
	
	

	valid=false;
	
	if (!f.eof()) kills=f.readInt();	else return;
	if (!f.eof()) deaths=f.readInt(); else return;
	if (!f.eof()) timeon=f.readULong(); else return;
	if (!f.eof()) matches=f.readInt(); else return;
	if (!f.eof()) lastplayed=f.readULong(); else return;

	string next;

	if (!f.eof())
	{
		f.discard("names");
		next= f.peekNextString();
		while ( next!="endnames")
		{
			string name=f.readString();
			int timeon=f.readInt();
			nickmap[name]=timeon;
			next=f.peekNextString();
		}
		f.discard("endnames");
	} else return;
	
	if (!f.eof())
	{
		f.discard("weapons");
		next= f.peekNextString();
		while (next!="endweapons")
		{
			string name=f.readString();
			int kills=f.readInt();
			weapmap[name]=kills;
			next=f.peekNextString();
		}
		f.discard("endweapons");
	} else return;

	if (!f.eof())
	{
		f.discard("classes");
		next= f.peekNextString();
		while (next!="endclasses")
		{
			string name=f.readString();
			int timeused=f.readInt();
			classmap[name]=timeused;
			next=f.peekNextString();
		}
		f.discard("endclasses");
	} else return;

	if (!f.eof())
	{
		f.discard("playtimes");
		next= f.peekNextString();
		while (next!="endplaytimes")
		{
			pair<time_t,time_t> startstop;
			startstop.first=f.readULong();
			startstop.second=f.readULong();
			playtimes.push_back(startstop);
			next=f.peekNextString();
		}
		f.discard("endplaytimes");
	} else return;

	valid=true;
	
}

//------------------------------------------------------------------------------------------------------
// Function:	CPlrPersist::read
// Purpose:	 converts the WONID to a file name (<wonid>.tfs) and passes execution
//	off to the above read function.
// Input:	WONID - the WONID of the player whose datafile we want to read
//------------------------------------------------------------------------------------------------------
void CPlrPersist::read(unsigned long WONID)
{
	
	string file=g_pApp->playerDirectory;
	char buf[100];
	file+=g_pApp->os->ultoa(WONID,buf,10);
	file+=".tfs";

	this->WONID=WONID;

	CTextFile f(file.c_str());
	read(f);
}



void CPlrPersist::write()
{
	string file=g_pApp->playerDirectory;
	char buf[100];
	file+=g_pApp->os->ultoa(WONID,buf,10);
	file+=".tfs";

	FILE* fout=fopen(file.c_str(),"wt");

	fprintf(fout,"%li //kills\n",kills);
	fprintf(fout,"%li //deaths\n",deaths);
	fprintf(fout,"%lu //timeon\n",timeon);
	fprintf(fout,"%li //matches played\n",matches);
	fprintf(fout,"%lu //last played\n",lastplayed);
	
	map<string,int>::iterator it;
	
	fprintf(fout,"names\n");
	it=nickmap.begin();
	for (it;it!=nickmap.end();++it)
	{
		string name=it->first;
		int time=it->second;
		fprintf(fout,"\t\"%s\" %li //has used the name \"%s\" for %02li:%02li:%02li\n",name.c_str(),time,name.c_str(),Util::time_t2hours(time),Util::time_t2mins(time),Util::time_t2secs(time));
	}
	fprintf(fout,"endnames\n");

	fprintf(fout,"weapons\n");
	it=weapmap.begin();
	for (it;it!=weapmap.end();++it)
	{
		string name=it->first;
		int kills=it->second;
		fprintf(fout,"\t\"%s\" %li //has killed %li people with \"%s\"\n",name.c_str(),kills,kills,name.c_str());
	}
	fprintf(fout,"endweapons\n");

	fprintf(fout,"classes\n");
	it=classmap.begin();
	for (it;it!=classmap.end();++it)
	{
		string name=it->first;
		int time=it->second;
		fprintf(fout,"\t\"%s\" %li //has played as a \"%s\" for %02li:%02li:%02li\n",name.c_str(),time,name.c_str(),Util::time_t2hours(time),Util::time_t2mins(time),Util::time_t2secs(time));
	}
	fprintf(fout,"endclasses\n");

	fprintf(fout,"playtimes\n");
	list<pair<time_t,time_t> >::iterator it2=playtimes.begin();
	for (it2;it2!=playtimes.end();++it2)
	{
		char buf[500];
		time_t t1=it2->first;
		time_t t2=it2->second;
		bool doesOverlap;
		
		list<pair<time_t,time_t> >::iterator overlap=timesOverlap(it2->first,it2->second,false);
		doesOverlap= overlap!=playtimes.end();
			

		fprintf(fout,"\t%lu %lu //played from %s.",it2->first,it2->second,Util::makeDurationString(it2->first,it2->second,buf," to "));
		if (doesOverlap)
			fprintf(fout,"Warning! overlaps with time range (%lu-%lu)",overlap->first,overlap->second);
		fprintf(fout,"\n");

	}
	fprintf(fout,"endplaytimes\n");


	fclose(fout);

}


list<pair<time_t,time_t> >::iterator CPlrPersist::timesOverlap(time_t start, time_t end,bool testself)
{
	list<pair<time_t,time_t> >::iterator it;
	it=playtimes.begin();
	for (it;it!=playtimes.end();++it)
	{
		time_t itFirst=it->first;
		time_t itSecond=it->second;
		if (start == it->first && end == it->second)
		{
			if (testself)
				break;
		}
		//if start is in current range
		else if (start >= it->first  && start <= it->second)
			break;

		//if end is in current range
		else if (end >= it->first  && end <= it->second)
			break;

		//if the start is before this range and end is after
		else if (start <= it->first && end >= it->second)
			break;
	}
	return it;
}

string CPlrPersist::faveString(map<string,int>& theMap)
{
	string retstr;
	time_t max=0;
	map<string,int>::iterator it=theMap.begin();
	for (it;it!=theMap.end();++it)
	{
		if (it->second > max)
		{
			max=it->second;
			retstr=it->first;
		}
	}
	return retstr;
}

string CPlrPersist::faveName()
{
	return faveString(nickmap);
}
string CPlrPersist::faveWeap()
{
	string s=faveString(weapmap);
	faveweapkills=weapmap[s];
	return s;
}
string CPlrPersist::faveClass()
{
	return faveString(classmap);
}

double CPlrPersist::rank()
{
	return ((double)((double)kills - (double)deaths) * 1000.0) / (double)timeon;
}