/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2020  Warzone 2100 Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

/**
 * @file scores.c
 * Deals with all the mission results gubbins.
 * Alex W. McLean
*/

#include <string.h>

// --------------------------------------------------------------------
#include "lib/framework/wzapp.h"
#include "lib/framework/wzconfig.h"
#include "lib/framework/math_ext.h"
#include "lib/framework/strres.h"
#include "lib/gamelib/gtime.h"
#include "console.h"
#include "scores.h"
#include "lib/ivis_opengl/pieblitfunc.h"
#include "lib/ivis_opengl/pietypes.h"
#include "lib/ivis_opengl/piefunc.h"
#include "lib/ivis_opengl/piemode.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/piepalette.h"
#include "objects.h"
#include "droiddef.h"
#include "basedef.h"
#include "statsdef.h"
#include "hci.h"
#include "miscimd.h"
#include "lib/ivis_opengl/piematrix.h"
#include "display3d.h"
#include "mission.h"
#include "game.h"
#include "lib/sound/audio.h"
#include "lib/sound/audio_id.h"
#include "intimage.h"
#include "loop.h" //for prevMissionType

#include "activity.h"
#include "multistat.h"
#include "clparse.h"

#include <algorithm>

#define	BAR_CRAWL_TIME	(GAME_TICKS_PER_SEC*3)

#define MT_Y_POS	(MISSIONRES_TITLE_Y  + D_H + 80)

#define DROID_LEVELS	9

#define LC_X	32
#define RC_X	320+32
#define	RANK_BAR_WIDTH	100
#define STAT_BAR_WIDTH	100

enum MR_STRING
{
	STR_MR_UNITS_LOST,
	STR_MR_UNITS_KILLED,
	STR_MR_STR_LOST,
	STR_MR_STR_BLOWN_UP,
	STR_MR_UNITS_BUILT,
	STR_MR_UNITS_NOW,
	STR_MR_STR_BUILT,
	STR_MR_STR_NOW,

	STR_MR_LEVEL_ROOKIE,
	STR_MR_LEVEL_GREEN,
	STR_MR_LEVEL_TRAINED,
	STR_MR_LEVEL_REGULAR,
	STR_MR_LEVEL_VETERAN,
	STR_MR_LEVEL_CRACK,
	STR_MR_LEVEL_ELITE,
	STR_MR_LEVEL_SPECIAL,
	STR_MR_LEVEL_ACE
};


// return translated string
static const char *getDescription(MR_STRING id)
{
	switch (id)
	{
	case STR_MR_UNITS_LOST   : return _("Own Units: %u");
	case STR_MR_UNITS_KILLED : return _("Enemy Units: %u");
	case STR_MR_STR_LOST     : return _("Own Structures: %u");
	case STR_MR_STR_BLOWN_UP : return _("Enemy Structures: %u");
	case STR_MR_UNITS_BUILT  : return _("Units Manufactured: %u");
	case STR_MR_UNITS_NOW    : return _("Total Units: %u");
	case STR_MR_STR_BUILT    : return _("Structures Built: %u");
	case STR_MR_STR_NOW      : return _("Total Structures: %u");

	case STR_MR_LEVEL_ROOKIE : return _("Rookie: %u");
	case STR_MR_LEVEL_GREEN  : return P_("rank", "Green: %u");
	case STR_MR_LEVEL_TRAINED: return _("Trained: %u");
	case STR_MR_LEVEL_REGULAR: return _("Regular: %u");
	case STR_MR_LEVEL_VETERAN: return _("Professional: %u");
	case STR_MR_LEVEL_CRACK  : return _("Veteran: %u");
	case STR_MR_LEVEL_ELITE  : return _("Elite: %u");
	case STR_MR_LEVEL_SPECIAL: return _("Special: %u");
	case STR_MR_LEVEL_ACE    : return _("Hero: %u");
	}

	// make compiler shut up
	return "";
}

static PIELIGHT getColour(int id)
{
	switch (id)
	{
	case STR_MR_STR_LOST	 :
	case STR_MR_UNITS_LOST   :
		return WZCOL_MENU_SCORE_LOSS;
	case STR_MR_STR_BLOWN_UP :
	case STR_MR_UNITS_KILLED :
		return WZCOL_MENU_SCORE_DESTROYED;
	case STR_MR_UNITS_BUILT  :
	case STR_MR_UNITS_NOW    :
	case STR_MR_STR_BUILT    :
	case STR_MR_STR_NOW      :
		return WZCOL_MENU_SCORE_BUILT;
	case STR_MR_LEVEL_ROOKIE :
	case STR_MR_LEVEL_GREEN  :
	case STR_MR_LEVEL_TRAINED:
	case STR_MR_LEVEL_REGULAR:
	case STR_MR_LEVEL_VETERAN:
	case STR_MR_LEVEL_CRACK  :
	case STR_MR_LEVEL_ELITE  :
	case STR_MR_LEVEL_SPECIAL:
	case STR_MR_LEVEL_ACE    :
		return WZCOL_MENU_SCORE_RANK;
	default			 :
		return WZCOL_BLACK;
	}
}

STAT_BAR	infoBars[] =
{
	{LC_X, 100, STAT_BAR_WIDTH, 16, 10, STR_MR_UNITS_LOST, 0, false, true, 0},	// left column		STAT_UNIT_LOST
	{LC_X, 120, STAT_BAR_WIDTH, 16, 20, STR_MR_UNITS_KILLED, 0, false, true, 0},			 //	STAT_UNIT_KILLED
	{LC_X, 160, STAT_BAR_WIDTH, 16, 30, STR_MR_STR_LOST, 0, false, true, 0},				 //	STAT_STR_LOST
	{LC_X, 180, STAT_BAR_WIDTH, 16, 40, STR_MR_STR_BLOWN_UP, 0, false, true, 0}, 			 //	STAT_STR_BLOWN_UP
	{LC_X, 220, STAT_BAR_WIDTH, 16, 50, STR_MR_UNITS_BUILT, 0, false, true, 0}, 			 //	STAT_UNITS_BUILT
	{LC_X, 240, STAT_BAR_WIDTH, 16, 60, STR_MR_UNITS_NOW, 0, false, true, 0}, 				 //	STAT_UNITS_NOW
	{LC_X, 260, STAT_BAR_WIDTH, 16, 70, STR_MR_STR_BUILT, 0, false, true, 0}, 				 //	STAT_STR_BUILT
	{LC_X, 280, STAT_BAR_WIDTH, 16, 80, STR_MR_STR_NOW, 0, false, false, 0}, 				 //	STAT_STR_NOW

	{RC_X, 100, RANK_BAR_WIDTH, 16, 10, STR_MR_LEVEL_ROOKIE, 0, false, true, 0},	// right column	//	STAT_ROOKIE
	{RC_X, 120, RANK_BAR_WIDTH, 16, 20, STR_MR_LEVEL_GREEN, 0, false, true, 0}, 			 		//	STAT_GREEN
	{RC_X, 140, RANK_BAR_WIDTH, 16, 30, STR_MR_LEVEL_TRAINED, 0, false, true, 0}, 		 			//	STAT_TRAINED
	{RC_X, 160, RANK_BAR_WIDTH, 16, 40, STR_MR_LEVEL_REGULAR, 0, false, true, 0}, 		 			//	STAT_REGULAR
	{RC_X, 180, RANK_BAR_WIDTH, 16, 50, STR_MR_LEVEL_VETERAN, 0, false, true, 0}, 		 			//	STAT_VETERAN
	{RC_X, 200, RANK_BAR_WIDTH, 16, 60, STR_MR_LEVEL_CRACK, 0, false, true, 0}, 			 		//	STAT_CRACK
	{RC_X, 220, RANK_BAR_WIDTH, 16, 70, STR_MR_LEVEL_ELITE, 0, false, true, 0}, 			 		//	STAT_ELITE
	{RC_X, 240, RANK_BAR_WIDTH, 16, 80, STR_MR_LEVEL_SPECIAL, 0, false, true, 0}, 		 			//	STAT_SPECIAL
	{RC_X, 260, RANK_BAR_WIDTH, 16, 90, STR_MR_LEVEL_ACE, 0, false, true, 0}, 			 			//	STAT_ACE

	{0, 0, 0, 0, 0, 0, 0, false, false, 0}
};

// --------------------------------------------------------------------
static void fillUpStats(const END_GAME_STATS_DATA& stats);
// --------------------------------------------------------------------

/* The present mission data */
MISSION_DATA	missionData;
static	UDWORD	dispST;
static	bool	bDispStarted = false;
static	char	text[255];
static	char	text2[255];
extern bool Cheated;

// --------------------------------------------------------------------
/* Initialise the mission data info - done before each mission */
bool	scoreInitSystem()
{
	missionData.unitsBuilt		= 0;
	missionData.unitsKilled		= 0;
	missionData.unitsLost		= 0;

	missionData.strBuilt		= 0;
	missionData.strKilled		= 0;
	missionData.strLost			= 0;

	missionData.artefactsFound	= 0;
	scoreUpdateVar(WD_MISSION_STARTED); // total game time is just gameTime
	missionData.missionEnded	= 0;
	missionData.shotsOnTarget	= 0;
	missionData.shotsOffTarget	= 0;
	missionData.babasMowedDown	= 0;
	Cheated = false;
	bDispStarted = false;
	return (true);
}

// --------------------------------------------------------------------
// Updates a game statistic - more can be added if we need 'em
void	scoreUpdateVar(DATA_INDEX var)
{
	switch (var)
	{
	case	WD_UNITS_BUILT:
		missionData.unitsBuilt++;	// We've built another unit
		break;
	case	WD_UNITS_KILLED:
		missionData.unitsKilled++;	// We've destroyed an enemy unit
		break;
	case	WD_UNITS_LOST:
		missionData.unitsLost++;	// We've lost a unit
		break;
	case	WD_STR_BUILT:
		missionData.strBuilt++;		// Built a structure
		break;
	case	WD_STR_KILLED:
		missionData.strKilled++;	// Destroyed an enemy structure
		break;
	case	WD_STR_LOST:
		missionData.strLost++;		// Lost a structure
		break;
	case	WD_ARTEFACTS_FOUND:
		missionData.artefactsFound++;	// Got an artefact
		break;
	case	WD_MISSION_STARTED:
		missionData.missionStarted = gameTime;	// Init the mission start time
		break;									// Should be called once per mission
	case	WD_MISSION_ENDED:
		missionData.missionEnded = gameTime - missionData.missionStarted;	// Mission ended
		break;
	case	WD_SHOTS_ON_TARGET:
		missionData.shotsOnTarget++;	// We hit something
		break;
	case	WD_SHOTS_OFF_TARGET:
		missionData.shotsOffTarget++;	// Missed something
		break;
	case	WD_BARBARIANS_MOWED_DOWN:
		missionData.babasMowedDown++;	// Ran over a barbarian
		break;
	default:
		debug(LOG_FATAL, "Weirdy variable request from scoreUpdateVar");
		abort();
		break;
	}
}

// Builds an ascii string for the passed in components 4:02:23 for example.
void getAsciiTimeN(char *pDest, size_t destSize, unsigned time, bool showMs /*= false*/)
{
	int hours, minutes, seconds, milliseconds;
	getTimeComponents(time, &hours, &minutes, &seconds, &milliseconds);

	char const *hourColon = hours != 0 ? ":" : "";

	if (showMs)
	{
		snprintf(pDest, destSize, "%.0d%s%02d:%02d.%03d", hours, hourColon, minutes, seconds, milliseconds);
	}
	else
	{
		snprintf(pDest, destSize, "%.0d%s%02d:%02d", hours, hourColon, minutes, seconds);
	}
}

END_GAME_STATS_DATA	collectEndGameStatsData()
{
	END_GAME_STATS_DATA fullStats;
	fullStats.missionData = missionData;

	for (size_t i = 0; i < DROID_LEVELS; i++)
	{
		fullStats.numDroidsPerLevel.push_back(getNumDroidsForLevel(selectedPlayer, i));
	}

	fullStats.numUnits = 0;
	if (selectedPlayer < MAX_PLAYERS)
	{
		unsigned int idx = 0;
		do
		{
			DroidList* dList = nullptr;
			switch (idx)
			{
				case 0: dList = &apsDroidLists[selectedPlayer]; break;
				case 1: dList = &mission.apsDroidLists[selectedPlayer]; break;
				case 2: if (prevMissionType == LEVEL_TYPE::LDS_MKEEP_LIMBO) { dList = &apsLimboDroids[selectedPlayer]; } break;
				default: dList = nullptr;
			}
			if (!dList)
			{
				continue;
			}
			for (const DROID* psDroid : *dList)
			{
				if (psDroid == nullptr || isDead(psDroid))
				{
					continue;
				}
				++fullStats.numUnits;
				if (psDroid->isTransporter())
				{
					if (psDroid->psGroup == nullptr)
					{
						continue;
					}
					for (DROID *psCurr : psDroid->psGroup->psList)
					{
						if (psCurr != psDroid)
						{
							++fullStats.numUnits;
						}
					}
				}
			}
		} while (++idx < 3);
	}

	return fullStats;
}

void scoreDataToScreen(WIDGET *psWidget, ScoreDataToScreenCache& cache)
{
	int index, x, y, width, height;
	bool bMoreBars;

	if (!bDispStarted)
	{
		bDispStarted = true;
		dispST = realTime;
		audio_PlayTrack(ID_SOUND_BUTTON_CLICK_5);
	}

	fillUpStats(collectEndGameStatsData());

	pie_UniTransBoxFill(16 + D_W, MT_Y_POS - 16, pie_GetVideoBufferWidth() - D_W - 16, MT_Y_POS + 256 + 16, WZCOL_SCORE_BOX);
	iV_Box(16 + D_W, MT_Y_POS - 16, pie_GetVideoBufferWidth() - D_W - 16, MT_Y_POS + 256 + 16, WZCOL_SCORE_BOX_BORDER);

	cache.wzLabelText_UnitLosses.setText(_("Unit Losses"), font_regular);
	cache.wzLabelText_UnitLosses.render(LC_X + D_W, 80 + 16 + D_H, WZCOL_FORM_TEXT);
	cache.wzLabelText_StructureLosses.setText(_("Structure Losses"), font_regular);
	cache.wzLabelText_StructureLosses.render(LC_X + D_W, 140 + 16 + D_H, WZCOL_FORM_TEXT);
	cache.wzLabelText_ForceInformation.setText(_("Force Information"), font_regular);
	cache.wzLabelText_ForceInformation.render(LC_X + D_W, 200 + 16 + D_H, WZCOL_FORM_TEXT);

	index = 0;
	bMoreBars = true;
	while (bMoreBars)
	{
		/* Is it time to display this bar? */
		if (infoBars[index].bActive)
		{
			/* Has it been queued before? */
			if (infoBars[index].bQueued == false)
			{
				/* Don't do this next time...! */
				infoBars[index].bQueued = true;

				/* Play a sound */
				audio_PlayTrack(ID_SOUND_BUTTON_CLICK_5);
			}
			x = infoBars[index].topX + D_W;
			y = infoBars[index].topY + D_H;
			width = infoBars[index].width;
			height = infoBars[index].height;

			iV_Box(x, y, x + width, y + height, WZCOL_BLACK);

			/* Draw the background border box */
			pie_BoxFill(x - 1, y - 1, x + width + 1, y + height + 1, WZCOL_MENU_BACKGROUND);

			/* Draw the interior grey */
			pie_BoxFill(x, y, x + width, y + height, WZCOL_MENU_SCORES_INTERIOR);

			if (((realTime - dispST) > infoBars[index].queTime))
			{
				/* Now draw amount filled */
				const float mul = (realTime - dispST < BAR_CRAWL_TIME) ?
				                  (float)(realTime - dispST) / (float)BAR_CRAWL_TIME
				                  : 1.f;

				const int length = static_cast<int>((float)infoBars[index].percent / 100.f * (float)infoBars[index].width * mul);

				if (length > 4)
				{

					/* Black shadow */
					pie_BoxFill(x + 1, y + 3, x + length - 1, y + height - 1, WZCOL_MENU_SHADOW);

					/* Solid coloured bit */
					pie_BoxFill(x + 1, y + 2, x + length - 4, y + height - 4, getColour(index));
				}
			}
			/* Now render the text by the bar */
			snprintf(text, sizeof(text), getDescription((MR_STRING)infoBars[index].stringID), infoBars[index].number);
			if (index >= cache.wzInfoBarText.size())
			{
				cache.wzInfoBarText.resize(index + 1);
			}
			cache.wzInfoBarText[index].setText(text, font_regular);
			cache.wzInfoBarText[index].render(x + width + 16, y + 12, WZCOL_FORM_TEXT);

			/* If we're beyond STAT_ROOKIE, then we're on rankings */
			if (index >= STAT_GREEN && index <= STAT_ACE)
			{
				iV_DrawImage(IntImages, (UWORD)(IMAGE_LEV_0 + (index - STAT_GREEN)), x - 8, y + 2);
			}
		}
		/* Move onto the next bar */
		index++;
		if ((index > STAT_ACE) || (infoBars[index].topX == 0 && infoBars[index].topY == 0))
		{
			bMoreBars = false;
		}
	}

	/* We now need to display the mission time, game time, average unit experience level an number of artefacts found */

	/* Firstly, top of the screen, number of artefacts found */
	snprintf(text, sizeof(text), _("ARTIFACTS RECOVERED: %d"), missionData.artefactsFound);
	cache.wzInfoText_ArtifactsFound.setText(text, font_regular);
	cache.wzInfoText_ArtifactsFound.render((pie_GetVideoBufferWidth() - cache.wzInfoText_ArtifactsFound.width()) / 2, 300 + D_H, WZCOL_FORM_TEXT);

	/* Get the mission result time in a string - and write it out */
	getAsciiTime(text2, missionData.missionEnded);
	snprintf(text, sizeof(text), _("Mission Time - %s"), text2);
	cache.wzInfoText_MissionTime.setText(text, font_regular);
	cache.wzInfoText_MissionTime.render((pie_GetVideoBufferWidth() - cache.wzInfoText_MissionTime.width()) / 2, 320 + D_H, WZCOL_FORM_TEXT);

	/* Write out total game time so far */
	getAsciiTime(text2, gameTime);
	snprintf(text, sizeof(text), _("Total Game Time - %s"), text2);
	cache.wzInfoText_TotalGameTime.setText(text, font_regular);
	cache.wzInfoText_TotalGameTime.render((pie_GetVideoBufferWidth() - cache.wzInfoText_TotalGameTime.width()) / 2, 340 + D_H, WZCOL_FORM_TEXT);
	if (Cheated)
	{
		// A quick way to flash the text
		PIELIGHT cheatedTextColor = ((realTime / 250) % 2) ? WZCOL_RED : WZCOL_YELLOW;
		snprintf(text, sizeof(text), "%s", _("You cheated!"));
		cache.wzInfoText_Cheated.setText(text, font_regular);
		cache.wzInfoText_Cheated.render((pie_GetVideoBufferWidth() - cache.wzInfoText_Cheated.width()) / 2, 360 + D_H, cheatedTextColor);
	}
}

// -----------------------------------------------------------------------------------
void	fillUpStats(const END_GAME_STATS_DATA& stats)
{
	UDWORD	i;
	UDWORD	maxi;
	float	scaleFactor;
	UDWORD	length;

	/* Do rankings first cos they're easier */
	for (i = 0, maxi = 0; i < DROID_LEVELS; i++)
	{
		maxi = std::max(maxi, stats.numDroidsPerLevel[i]);
	}

	/* Make sure we got something */
	if (maxi == 0)
	{
		scaleFactor = 0.f;
	}
	else
	{
		scaleFactor = (float)RANK_BAR_WIDTH / maxi;
	}

	/* Scale for percent */
	for (i = 0; i < DROID_LEVELS; i++)
	{
		length = static_cast<UDWORD>(scaleFactor * stats.numDroidsPerLevel[i]);
		infoBars[STAT_ROOKIE + i].percent = PERCENT(length, RANK_BAR_WIDTH);
		infoBars[STAT_ROOKIE + i].number = stats.numDroidsPerLevel[i];
	}

	/* Now do the other stuff... */
	/* Units killed and lost... */
	maxi = MAX(stats.missionData.unitsLost, stats.missionData.unitsKilled);
	if (maxi == 0)
	{
		scaleFactor = 0.f;
	}
	else
	{
		scaleFactor = (float)STAT_BAR_WIDTH / maxi;
	}

	length = static_cast<UDWORD>(scaleFactor * stats.missionData.unitsLost);
	infoBars[STAT_UNIT_LOST].percent = PERCENT(length, STAT_BAR_WIDTH);
	length = static_cast<UDWORD>(scaleFactor * stats.missionData.unitsKilled);
	infoBars[STAT_UNIT_KILLED].percent = PERCENT(length, STAT_BAR_WIDTH);

	/* Now do the structure losses */
	maxi = MAX(stats.missionData.strLost, stats.missionData.strKilled);
	if (maxi == 0)
	{
		scaleFactor = 0.f;
	}
	else
	{
		scaleFactor = (float)STAT_BAR_WIDTH / maxi;
	}

	length = static_cast<UDWORD>(scaleFactor * stats.missionData.strLost);
	infoBars[STAT_STR_LOST].percent = PERCENT(length, STAT_BAR_WIDTH);
	length = static_cast<UDWORD>(scaleFactor * stats.missionData.strKilled);
	infoBars[STAT_STR_BLOWN_UP].percent = PERCENT(length, STAT_BAR_WIDTH);

	/* Finally the force information - need amount of droids as well*/

	maxi = MAX(stats.missionData.unitsBuilt, stats.missionData.strBuilt);
	maxi = MAX(maxi, stats.numUnits);

	if (maxi == 0)
	{
		scaleFactor = 0.f;
	}
	else
	{
		scaleFactor = (float)STAT_BAR_WIDTH / maxi;
	}

	length = static_cast<UDWORD>(scaleFactor * stats.missionData.unitsBuilt);
	infoBars[STAT_UNITS_BUILT].percent = PERCENT(length, STAT_BAR_WIDTH);
	length = static_cast<UDWORD>(scaleFactor * stats.numUnits);
	infoBars[STAT_UNITS_NOW].percent = PERCENT(length, STAT_BAR_WIDTH);
	length = static_cast<UDWORD>(scaleFactor * stats.missionData.strBuilt);
	infoBars[STAT_STR_BUILT].percent = PERCENT(length, STAT_BAR_WIDTH);

	/* Finally the numbers themselves */
	infoBars[STAT_UNIT_LOST].number = stats.missionData.unitsLost;
	infoBars[STAT_UNIT_KILLED].number = stats.missionData.unitsKilled;
	infoBars[STAT_STR_LOST].number = stats.missionData.strLost;
	infoBars[STAT_STR_BLOWN_UP].number = stats.missionData.strKilled;
	infoBars[STAT_UNITS_BUILT].number =	stats.missionData.unitsBuilt;
	infoBars[STAT_UNITS_NOW].number = stats.numUnits;
	infoBars[STAT_STR_BUILT].number = stats.missionData.strBuilt;
}

// -----------------------------------------------------------------------------------
/* This will save out the score data */
bool writeScoreData(const char *fileName)
{
	WzConfig ini(fileName, WzConfig::ReadAndWrite);

	// Dump the scores for the current player
	ini.setValue("unitsBuilt", missionData.unitsBuilt);
	ini.setValue("unitsKilled", missionData.unitsKilled);
	ini.setValue("unitsLost", missionData.unitsLost);
	ini.setValue("strBuilt", missionData.strBuilt);
	ini.setValue("strKilled", missionData.strKilled);
	ini.setValue("strLost", missionData.strLost);
	ini.setValue("artefactsFound", missionData.artefactsFound);
	ini.setValue("missionStarted", missionData.missionStarted);
	ini.setValue("missionEnded", missionData.missionEnded);
	ini.setValue("shotsOnTarget", missionData.shotsOnTarget);
	ini.setValue("shotsOffTarget", missionData.shotsOffTarget);
	ini.setValue("babasMowedDown", missionData.babasMowedDown);

	// Everything is just fine!
	return true;
}

// -----------------------------------------------------------------------------------
/* This will read in the score data */
bool readScoreData(const char *fileName)
{
	WzConfig ini(fileName, WzConfig::ReadOnly);

	// Retrieve the score data for the current player
	missionData.unitsBuilt = ini.value("unitsBuilt").toInt();
	missionData.unitsKilled = ini.value("unitsKilled").toInt();
	missionData.unitsLost = ini.value("unitsLost").toInt();
	missionData.strBuilt = ini.value("strBuilt").toInt();
	missionData.strKilled = ini.value("strKilled").toInt();
	missionData.strLost = ini.value("strLost").toInt();
	missionData.artefactsFound = ini.value("artefactsFound").toInt();
	missionData.missionStarted = ini.value("missionStarted").toInt();
	missionData.missionEnded = ini.value("missionEnded").toInt();
	missionData.shotsOnTarget = ini.value("shotsOnTarget").toInt();
	missionData.shotsOffTarget = ini.value("shotsOffTarget").toInt();
	missionData.babasMowedDown = ini.value("babasMowedDown").toInt();

	/* Hopefully everything's just fine by now */
	return true;
}

void stdOutGameSummary(UDWORD realTimeThrottleSeconds, bool flush_output /* = true */)
{
	static UDWORD lastOutputRealTime = 0;
	if (realTimeThrottleSeconds > 0 && (realTime - lastOutputRealTime < (realTimeThrottleSeconds * GAME_TICKS_PER_SEC)))
	{
		return;
	}
	fprintf(stdout, "Game State [gameTime: %" PRIu32 "]\n", gameTime);
	fprintf(stdout, "--------------------------------------------------------------------------------------\n");
	if (ActivityManager::instance().getCurrentGameMode() != ActivitySink::GameMode::CAMPAIGN)
	{
		fprintf(stdout, " # | Player Name | Extrct Pwr | Units Killed | Structs (F/R) | Units Alive |  Power  |\n");
		fprintf(stdout, "-- | ----------- | ---------- | ------------ | ------------- | ----------- | ------- |\n");
		for (unsigned n = 0; n < std::min<unsigned>(MAX_PLAYERS, (unsigned)game.maxPlayers); ++n)
		{
			if (NetPlay.players[n].ai < 0)
			{
				// skip closed slots / open / empty slots
				continue;
			}
			uint32_t unitsKilled = getMultiPlayUnitsKilled(n);
			uint32_t numUnits = apsDroidLists[n].size();
			uint32_t numStructs = apsStructLists[n].size();
			uint32_t numFactories = 0;
			uint32_t numResearch = 0;
			uint32_t numFactoriesThatCanProduceConstructionUnits = 0;
			for (const STRUCTURE *psStruct : apsStructLists[n])
			{
				if (psStruct->status != SS_BUILT || psStruct->died != 0)
				{
					continue; // ignore structures that aren't completely built, or are "dead"
				}
				if (psStruct->isFactory())
				{
					numFactories++;
					if (psStruct->pStructureType->type == REF_FACTORY ||
						psStruct->pStructureType->type == REF_CYBORG_FACTORY)
					{
						numFactoriesThatCanProduceConstructionUnits++;
					}
				}
				else if (psStruct->pStructureType->type == REF_RESEARCH)
				{
					numResearch++;
				}
			}
			std::string structInfoString = std::to_string(numStructs) + " (" + std::to_string(numFactories) + "/" + std::to_string(numResearch) + ")";
			// NOTE: This duplicates the logic in rules.js - checkEndConditions()
			const bool playerCantDoAnything = (numFactoriesThatCanProduceConstructionUnits == 0) && (numUnits == 0);
			const char * deadStatus = playerCantDoAnything ? "x" : "";
			fprintf(stdout, "%2u | %11.11s | %10" PRIi64 " | %12" PRIi32 " | %13.13s | %11" PRIi32 " | %7" PRIi32 " | %s\n", n, getPlayerName(n), getExtractedPower(n), unitsKilled, structInfoString.c_str(), numUnits, getPower(n), deadStatus);
		}
	}
	fprintf(stdout, "--------------------------------------------------------------------------------------\n");
	if (flush_output)
	{
		fflush(stdout);
	}
	lastOutputRealTime = realTime;
}
