#include "common.h"

#include "Stats.h"
#include "Text.h"
#include "World.h"

int32 CStats::DaysPassed;
int32 CStats::HeadsPopped;
int32 CStats::CommercialPassed;
int32 CStats::IndustrialPassed;
int32 CStats::SuburbanPassed;
int32 CStats::NumberKillFrenziesPassed;
int32 CStats::PeopleKilledByOthers;
int32 CStats::HelisDestroyed;
int32 CStats::PedsKilledOfThisType[NUM_PEDTYPES];
int32 CStats::TimesDied;
int32 CStats::TimesArrested;
int32 CStats::KillsSinceLastCheckpoint;
float CStats::DistanceTravelledInVehicle;
float CStats::DistanceTravelledOnFoot;
int32 CStats::ProgressMade;
int32 CStats::TotalProgressInGame;
int32 CStats::CarsExploded;
int32 CStats::PeopleKilledByPlayer;
float CStats::MaximumJumpDistance;
float CStats::MaximumJumpHeight;
int32 CStats::MaximumJumpFlips;
int32 CStats::MaximumJumpSpins;
int32 CStats::BestStuntJump;
int32 CStats::NumberOfUniqueJumpsFound;
int32 CStats::TotalNumberOfUniqueJumps;
int32 CStats::PassengersDroppedOffWithTaxi;
int32 CStats::MoneyMadeWithTaxi;
int32 CStats::MissionsGiven;
int32 CStats::MissionsPassed;
char CStats::LastMissionPassedName[8];
int32 CStats::TotalLegitimateKills;
int32 CStats::ElBurroTime;
int32 CStats::Record4x4One;
int32 CStats::Record4x4Two;
int32 CStats::Record4x4Three;
int32 CStats::Record4x4Mayhem;
int32 CStats::LivesSavedWithAmbulance;
int32 CStats::CriminalsCaught;
int32 CStats::HighestLevelAmbulanceMission;
int32 CStats::FiresExtinguished;
int32 CStats::LongestFlightInDodo;
int32 CStats::TimeTakenDefuseMission;
int32 CStats::TotalNumberKillFrenzies;
int32 CStats::TotalNumberMissions;
int32 CStats::RoundsFiredByPlayer;
int32 CStats::KgsOfExplosivesUsed;
int32 CStats::InstantHitsFiredByPlayer;
int32 CStats::InstantHitsHitByPlayer;
int32 CStats::BestTimeBombDefusal;
int32 CStats::mmRain;
int32 CStats::CarsCrushed;
int32 CStats::FastestTimes[CStats::TOTAL_FASTEST_TIMES];
int32 CStats::HighestScores[CStats::TOTAL_HIGHEST_SCORES];

void CStats::Init()
{
	PeopleKilledByOthers = 0;
	PeopleKilledByPlayer = 0;
	RoundsFiredByPlayer = 0;
	CarsExploded = 0;
	HelisDestroyed = 0;
	ProgressMade = 0;
	KgsOfExplosivesUsed = 0;
	InstantHitsFiredByPlayer = 0;
	InstantHitsHitByPlayer = 0;
	CarsCrushed = 0;
	HeadsPopped = 0;
	TimesArrested = 0;
	TimesDied = 0;
	DaysPassed = 0;
	NumberOfUniqueJumpsFound = 0;
	mmRain = 0;
	MaximumJumpFlips = 0;
	MaximumJumpSpins = 0;
	MaximumJumpDistance = 0;
	MaximumJumpHeight = 0;
	BestStuntJump = 0;
	TotalNumberOfUniqueJumps = 0;
	Record4x4One = 0;
	LongestFlightInDodo = 0;
	Record4x4Two = 0;
	PassengersDroppedOffWithTaxi = 0;
	Record4x4Three = 0;
	MoneyMadeWithTaxi = 0;
	Record4x4Mayhem = 0;
	LivesSavedWithAmbulance = 0;
	ElBurroTime = 0;
	CriminalsCaught = 0;
	MissionsGiven = 0;
	HighestLevelAmbulanceMission = 0;
	MissionsPassed = 0;
	FiresExtinguished = 0;
	DistanceTravelledOnFoot = 0;
	TimeTakenDefuseMission = 0;
	NumberKillFrenziesPassed = 0;
	DistanceTravelledInVehicle = 0;
	TotalNumberKillFrenzies = 0;
	TotalNumberMissions = 0;
	KillsSinceLastCheckpoint = 0;
	TotalLegitimateKills = 0;
	for (int i = 0; i < TOTAL_FASTEST_TIMES; i++)
		FastestTimes[i] = 0;
	for (int i = 0; i < TOTAL_HIGHEST_SCORES; i++)
		HighestScores[i] = 0;
	for (int i = 0; i < NUM_PEDTYPES; i++)
		PedsKilledOfThisType[i] = 0;
	IndustrialPassed = 0;
	CommercialPassed = 0;
	SuburbanPassed = 0;
}

void CStats::RegisterFastestTime(int32 index, int32 time)
{
	assert(index >= 0 && index < TOTAL_FASTEST_TIMES);
	if (FastestTimes[index] == 0)
		FastestTimes[index] = time;
	else
		FastestTimes[index] = Min(FastestTimes[index], time);
}

void CStats::RegisterHighestScore(int32 index, int32 score)
{
	assert(index >= 0 && index < TOTAL_HIGHEST_SCORES);
	HighestScores[index] = Max(HighestScores[index], score);
}

void CStats::RegisterElBurroTime(int32 time)
{
	ElBurroTime = (ElBurroTime && ElBurroTime < time) ? ElBurroTime : time;
}

void CStats::Register4x4OneTime(int32 time)
{
	Record4x4One = (Record4x4One && Record4x4One < time) ? Record4x4One : time;
}

void CStats::Register4x4TwoTime(int32 time)
{
	Record4x4Two = (Record4x4Two && Record4x4Two < time) ? Record4x4Two : time;
}

void CStats::Register4x4ThreeTime(int32 time)
{
	Record4x4Three = (Record4x4Three && Record4x4Three < time) ? Record4x4Three : time;
}

void CStats::Register4x4MayhemTime(int32 time)
{
	Record4x4Mayhem = (Record4x4Mayhem && Record4x4Mayhem < time) ? Record4x4Mayhem : time;
}

void CStats::AnotherLifeSavedWithAmbulance()
{
	++LivesSavedWithAmbulance;
}

void CStats::AnotherCriminalCaught()
{
	++CriminalsCaught;
}

void CStats::RegisterLevelAmbulanceMission(int32 level)
{
	HighestLevelAmbulanceMission = Max(HighestLevelAmbulanceMission, level);
}

void CStats::AnotherFireExtinguished()
{
	++FiresExtinguished;
}

void CStats::RegisterLongestFlightInDodo(int32 time)
{
	LongestFlightInDodo = Max(LongestFlightInDodo, time);
}

void CStats::RegisterTimeTakenDefuseMission(int32 time)
{
	TimeTakenDefuseMission = (TimeTakenDefuseMission && TimeTakenDefuseMission < time) ? TimeTakenDefuseMission : time;
}

void CStats::AnotherKillFrenzyPassed()
{
	++NumberKillFrenziesPassed;
}

void CStats::SetTotalNumberKillFrenzies(int32 total)
{
	TotalNumberKillFrenzies = total;
}

void CStats::SetTotalNumberMissions(int32 total)
{
	TotalNumberMissions = total;
}

wchar *CStats::FindCriminalRatingString()
{
	int rating = FindCriminalRatingNumber();

	if (rating < 10) return TheText.Get("RATNG1");
	if (rating < 25) return TheText.Get("RATNG2");
	if (rating < 70) return TheText.Get("RATNG3");
	if (rating < 150) return TheText.Get("RATNG4");
	if (rating < 250) return TheText.Get("RATNG5");
	if (rating < 450) return TheText.Get("RATNG6");
	if (rating < 700) return TheText.Get("RATNG7");
	if (rating < 1000) return TheText.Get("RATNG8");
	if (rating < 1400) return TheText.Get("RATNG9");
	if (rating < 1900) return TheText.Get("RATNG10");
	if (rating < 2500) return TheText.Get("RATNG11");
	if (rating < 3200) return TheText.Get("RATNG12");
	if (rating < 4000) return TheText.Get("RATNG13");
	if (rating < 5000) return TheText.Get("RATNG14");
	return TheText.Get("RATNG15");
}

int32 CStats::FindCriminalRatingNumber()
{
	int32 rating;

	rating = FiresExtinguished + 10 * HighestLevelAmbulanceMission + CriminalsCaught + LivesSavedWithAmbulance
		+ 30 * HelisDestroyed + TotalLegitimateKills - 3 * TimesArrested - 3 * TimesDied
		+ CWorld::Players[CWorld::PlayerInFocus].m_nMoney / 5000;
	if (rating <= 0) rating = 0;

	if (InstantHitsFiredByPlayer > 100)
		rating += (float)CStats::InstantHitsHitByPlayer / (float)CStats::InstantHitsFiredByPlayer * 500.0f;
	if (TotalProgressInGame)
		rating += (float)CStats::ProgressMade / (float)CStats::TotalProgressInGame * 1000.0f;
	if (!IndustrialPassed && rating >= 3521)
		rating = 3521;
	if (!CommercialPassed && rating >= 4552)
		rating = 4552;
	return rating;
}

void CStats::SaveStats(uint8 *buf, uint32 *size)
{
	CheckPointReachedSuccessfully();
	uint8 *buf_start = buf;
	*size = sizeof(PeopleKilledByPlayer) +
		sizeof(PeopleKilledByOthers) +
		sizeof(CarsExploded) +
		sizeof(RoundsFiredByPlayer) +
		sizeof(PedsKilledOfThisType) +
		sizeof(HelisDestroyed) +
		sizeof(ProgressMade) +
		sizeof(TotalProgressInGame) +
		sizeof(KgsOfExplosivesUsed) +
		sizeof(InstantHitsFiredByPlayer) +
		sizeof(InstantHitsHitByPlayer) +
		sizeof(CarsCrushed) +
		sizeof(HeadsPopped) +
		sizeof(TimesArrested) +
		sizeof(TimesDied) +
		sizeof(DaysPassed) +
		sizeof(mmRain) +
		sizeof(MaximumJumpDistance) +
		sizeof(MaximumJumpHeight) +
		sizeof(MaximumJumpFlips) +
		sizeof(MaximumJumpSpins) +
		sizeof(BestStuntJump) +
		sizeof(NumberOfUniqueJumpsFound) +
		sizeof(TotalNumberOfUniqueJumps) +
		sizeof(MissionsGiven) +
		sizeof(MissionsPassed) +
		sizeof(PassengersDroppedOffWithTaxi) +
		sizeof(MoneyMadeWithTaxi) +
		sizeof(IndustrialPassed) +
		sizeof(CommercialPassed) +
		sizeof(SuburbanPassed) +
		sizeof(ElBurroTime) +
		sizeof(DistanceTravelledOnFoot) +
		sizeof(DistanceTravelledInVehicle) +
		sizeof(Record4x4One) +
		sizeof(Record4x4Two) +
		sizeof(Record4x4Three) +
		sizeof(Record4x4Mayhem) +
		sizeof(LivesSavedWithAmbulance) +
		sizeof(CriminalsCaught) +
		sizeof(HighestLevelAmbulanceMission) +
		sizeof(FiresExtinguished) +
		sizeof(LongestFlightInDodo) +
		sizeof(TimeTakenDefuseMission) +
		sizeof(NumberKillFrenziesPassed) +
		sizeof(TotalNumberKillFrenzies) +
		sizeof(TotalNumberMissions) +
		sizeof(FastestTimes) +
		sizeof(HighestScores) +
		sizeof(KillsSinceLastCheckpoint) +
		sizeof(TotalLegitimateKills) +
		sizeof(LastMissionPassedName);

#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); buf += sizeof(data);
	CopyToBuf(buf, PeopleKilledByPlayer);
	CopyToBuf(buf, PeopleKilledByOthers);
	CopyToBuf(buf, CarsExploded);
	CopyToBuf(buf, RoundsFiredByPlayer);
	CopyToBuf(buf, PedsKilledOfThisType);
	CopyToBuf(buf, HelisDestroyed);
	CopyToBuf(buf, ProgressMade);
	CopyToBuf(buf, TotalProgressInGame);
	CopyToBuf(buf, KgsOfExplosivesUsed);
	CopyToBuf(buf, InstantHitsFiredByPlayer);
	CopyToBuf(buf, InstantHitsHitByPlayer);
	CopyToBuf(buf, CarsCrushed);
	CopyToBuf(buf, HeadsPopped);
	CopyToBuf(buf, TimesArrested);
	CopyToBuf(buf, TimesDied);
	CopyToBuf(buf, DaysPassed);
	CopyToBuf(buf, mmRain);
	CopyToBuf(buf, MaximumJumpDistance);
	CopyToBuf(buf, MaximumJumpHeight);
	CopyToBuf(buf, MaximumJumpFlips);
	CopyToBuf(buf, MaximumJumpSpins);
	CopyToBuf(buf, BestStuntJump);
	CopyToBuf(buf, NumberOfUniqueJumpsFound);
	CopyToBuf(buf, TotalNumberOfUniqueJumps);
	CopyToBuf(buf, MissionsGiven);
	CopyToBuf(buf, MissionsPassed);
	CopyToBuf(buf, PassengersDroppedOffWithTaxi);
	CopyToBuf(buf, MoneyMadeWithTaxi);
	CopyToBuf(buf, IndustrialPassed);
	CopyToBuf(buf, CommercialPassed);
	CopyToBuf(buf, SuburbanPassed);
	CopyToBuf(buf, ElBurroTime);
	CopyToBuf(buf, DistanceTravelledOnFoot);
	CopyToBuf(buf, DistanceTravelledInVehicle);
	CopyToBuf(buf, Record4x4One);
	CopyToBuf(buf, Record4x4Two);
	CopyToBuf(buf, Record4x4Three);
	CopyToBuf(buf, Record4x4Mayhem);
	CopyToBuf(buf, LivesSavedWithAmbulance);
	CopyToBuf(buf, CriminalsCaught);
	CopyToBuf(buf, HighestLevelAmbulanceMission);
	CopyToBuf(buf, FiresExtinguished);
	CopyToBuf(buf, LongestFlightInDodo);
	CopyToBuf(buf, TimeTakenDefuseMission);
	CopyToBuf(buf, NumberKillFrenziesPassed);
	CopyToBuf(buf, TotalNumberKillFrenzies);
	CopyToBuf(buf, TotalNumberMissions);
	CopyToBuf(buf, FastestTimes);
	CopyToBuf(buf, HighestScores);
	CopyToBuf(buf, KillsSinceLastCheckpoint);
	CopyToBuf(buf, TotalLegitimateKills);
	CopyToBuf(buf, LastMissionPassedName);

	assert(buf - buf_start == *size);
#undef CopyToBuf
}

void CStats::LoadStats(uint8 *buf, uint32 size)
{
	uint8* buf_start = buf;

#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); buf += sizeof(data);

	CopyFromBuf(buf, PeopleKilledByPlayer);
	CopyFromBuf(buf, PeopleKilledByOthers);
	CopyFromBuf(buf, CarsExploded);
	CopyFromBuf(buf, RoundsFiredByPlayer);
	CopyFromBuf(buf, PedsKilledOfThisType);
	CopyFromBuf(buf, HelisDestroyed);
	CopyFromBuf(buf, ProgressMade);
	CopyFromBuf(buf, TotalProgressInGame);
	CopyFromBuf(buf, KgsOfExplosivesUsed);
	CopyFromBuf(buf, InstantHitsFiredByPlayer);
	CopyFromBuf(buf, InstantHitsHitByPlayer);
	CopyFromBuf(buf, CarsCrushed);
	CopyFromBuf(buf, HeadsPopped);
	CopyFromBuf(buf, TimesArrested);
	CopyFromBuf(buf, TimesDied);
	CopyFromBuf(buf, DaysPassed);
	CopyFromBuf(buf, mmRain);
	CopyFromBuf(buf, MaximumJumpDistance);
	CopyFromBuf(buf, MaximumJumpHeight);
	CopyFromBuf(buf, MaximumJumpFlips);
	CopyFromBuf(buf, MaximumJumpSpins);
	CopyFromBuf(buf, BestStuntJump);
	CopyFromBuf(buf, NumberOfUniqueJumpsFound);
	CopyFromBuf(buf, TotalNumberOfUniqueJumps);
	CopyFromBuf(buf, MissionsGiven);
	CopyFromBuf(buf, MissionsPassed);
	CopyFromBuf(buf, PassengersDroppedOffWithTaxi);
	CopyFromBuf(buf, MoneyMadeWithTaxi);
	CopyFromBuf(buf, IndustrialPassed);
	CopyFromBuf(buf, CommercialPassed);
	CopyFromBuf(buf, SuburbanPassed);
	CopyFromBuf(buf, ElBurroTime);
	CopyFromBuf(buf, DistanceTravelledOnFoot);
	CopyFromBuf(buf, DistanceTravelledInVehicle);
	CopyFromBuf(buf, Record4x4One);
	CopyFromBuf(buf, Record4x4Two);
	CopyFromBuf(buf, Record4x4Three);
	CopyFromBuf(buf, Record4x4Mayhem);
	CopyFromBuf(buf, LivesSavedWithAmbulance);
	CopyFromBuf(buf, CriminalsCaught);
	CopyFromBuf(buf, HighestLevelAmbulanceMission);
	CopyFromBuf(buf, FiresExtinguished);
	CopyFromBuf(buf, LongestFlightInDodo);
	CopyFromBuf(buf, TimeTakenDefuseMission);
	CopyFromBuf(buf, NumberKillFrenziesPassed);
	CopyFromBuf(buf, TotalNumberKillFrenzies);
	CopyFromBuf(buf, TotalNumberMissions);
	CopyFromBuf(buf, FastestTimes);
	CopyFromBuf(buf, HighestScores);
	CopyFromBuf(buf, KillsSinceLastCheckpoint);
	CopyFromBuf(buf, TotalLegitimateKills);
	CopyFromBuf(buf, LastMissionPassedName);

	assert(buf - buf_start == size);
#undef CopyFromBuf
}

src/core/Stats.cpp