/*****************************************************************************
 * Copyright (c) 2014 Ted John
 * OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
 *
 * This file is part of OpenRCT2.
 *
 * OpenRCT2 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 3 of the License, or
 * (at your option) any later version.

 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************************/

#include "addresses.h"
#include "platform/platform.h"
#include "util/util.h"
#include "scenario.h"

// Scenario list
int gScenarioListCount = 0;
int gScenarioListCapacity = 0;
rct_scenario_basic *gScenarioList = NULL;

static void scenario_list_add(const char *path);
static void scenario_list_sort();
static int scenario_list_sort_compare(const void *a, const void *b);
static int scenario_scores_load();

rct_scenario_basic *get_scenario_by_filename(const char *filename)
{
	int i;
	for (i = 0; i < gScenarioListCount; i++)
		if (strcmp(gScenarioList[i].path, filename) == 0)
			return &gScenarioList[i];

	return NULL;
}

/**
 *
 *  rct2: 0x006775A8
 */
void scenario_load_list()
{
	int i, enumFileHandle;
	file_info enumFileInfo;

	// Load scores
	scenario_scores_load();

	// Set all scenarios to be invisible
	for (i = 0; i < gScenarioListCount; i++)
		gScenarioList[i].flags &= ~SCENARIO_FLAGS_VISIBLE;

	// Enumerate through each scenario in the directory
	enumFileHandle = platform_enumerate_files_begin(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char));
	if (enumFileHandle != INVALID_HANDLE) {
		while (platform_enumerate_files_next(enumFileHandle, &enumFileInfo)) {
			scenario_list_add(enumFileInfo.path);
		}
		platform_enumerate_files_end(enumFileHandle);
	}

	// Sort alphabetically
	scenario_list_sort();

	// Save the scores
	scenario_scores_save();
}

static void scenario_list_add(const char *path)
{
	char scenarioPath[MAX_PATH];
	rct_scenario_basic *scenario;
	rct_s6_header s6Header;
	rct_s6_info s6Info;

	// Get absolute path
	substitute_path(scenarioPath, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), path);

	// Load the basic scenario information
	if (!scenario_load_basic(scenarioPath, &s6Header, &s6Info))
		return;

	// Ignore scenarios where first header byte is not 255
	if (s6Info.editor_step != 255)
		return;

	// Check if scenario already exists in list, likely if in scores
	scenario = get_scenario_by_filename(path);
	if (scenario != NULL) {
		// Update the scenario information
		scenario->flags |= SCENARIO_FLAGS_VISIBLE;
		scenario->category = s6Info.category;
		scenario->objective_type = s6Info.objective_type;
		scenario->objective_arg_1 = s6Info.objective_arg_1;
		scenario->objective_arg_2 = s6Info.objective_arg_2;
		scenario->objective_arg_3 = s6Info.objective_arg_3;
		safe_strncpy(scenario->name, s6Info.name, 64);
		safe_strncpy(scenario->details, s6Info.details, 256);
	} else {
		// Check if the scenario list buffer has room for another scenario
		if (gScenarioListCount >= gScenarioListCapacity) {
			// Allocate more room
			gScenarioListCapacity += 16;
			gScenarioList = realloc(gScenarioList, gScenarioListCapacity * sizeof(rct_scenario_basic));
		}

		// Increment the number of scenarios
		scenario = &gScenarioList[gScenarioListCount];
		gScenarioListCount++;

		// Add this new scenario to the list
		safe_strncpy(scenario->path, path, 256);
		scenario->flags = SCENARIO_FLAGS_VISIBLE;
		if (RCT2_GLOBAL(0x009AA00C, uint8) & 1)
			scenario->flags |= SCENARIO_FLAGS_SIXFLAGS;
		scenario->category = s6Info.category;
		scenario->objective_type = s6Info.objective_type;
		scenario->objective_arg_1 = s6Info.objective_arg_1;
		scenario->objective_arg_2 = s6Info.objective_arg_2;
		scenario->objective_arg_3 = s6Info.objective_arg_3;
		safe_strncpy(scenario->name, s6Info.name, 64);
		safe_strncpy(scenario->details, s6Info.details, 256);
	}
}

/**
* Sort the list of scenarios. This used to be an insertion sort which took
* place as each scenario loaded. It has now been changed to a quicksort which
* takes place after all the scenarios have been loaded in.
*  rct2: 0x00677C3B
*/
static void scenario_list_sort()
{
	qsort(gScenarioList, gScenarioListCount, sizeof(rct_scenario_basic), scenario_list_sort_compare);
}

/**
 * Basic scenario information compare function for sorting.
 *  rct2: 0x00677C08
 */
static int scenario_list_sort_compare(const void *a, const void *b)
{
	return strcmp(((rct_scenario_basic*)a)->name, ((rct_scenario_basic*)b)->name);
}

/**
 * Gets the path for the scenario scores path.
 */
static void scenario_scores_get_path(utf8 *outPath)
{
	platform_get_user_directory(outPath, NULL);
	strcat(outPath, "scores.dat");
}

/**
 *
 *  rct2: 0x006775A8
 */
static int scenario_scores_load()
{
	SDL_RWops *file;
	char scoresPath[MAX_PATH];

	scenario_scores_get_path(scoresPath);

	// Free scenario list if already allocated
	if (gScenarioList != NULL) {
		free(gScenarioList);
		gScenarioList = NULL;
	}

	// Try and load the scores file

	// First check user folder and then fallback to install directory
	file = SDL_RWFromFile(scoresPath, "rb");
	if (file == NULL) {
		file = SDL_RWFromFile(get_file_path(PATH_ID_SCORES), "rb");
		if (file == NULL) {
			log_error("Unable to load scenario scores.");
			return 0;
		}
	}

	// Load header
	rct_scenario_scores_header header;
	if (SDL_RWread(file, &header, 16, 1) != 1) {
		SDL_RWclose(file);
		log_error("Invalid header in scenario scores file.");
		return 0;
	}
	gScenarioListCount = header.scenario_count;

	// Load scenario information with scores
	int scenarioListBufferSize = gScenarioListCount * sizeof(rct_scenario_basic);
	gScenarioListCapacity = gScenarioListCount;
	gScenarioList = malloc(scenarioListBufferSize);
	if (SDL_RWread(file, gScenarioList, scenarioListBufferSize, 1) == 1) {
		SDL_RWclose(file);
		return 1;
	}

	// Unable to load scores, free scenario list
	SDL_RWclose(file);
	gScenarioListCount = 0;
	gScenarioListCapacity = 0;
	free(gScenarioList);
	gScenarioList = NULL;
	return 0;
}

/**
 *
 *  rct2: 0x00677B50
 */
int scenario_scores_save()
{
	SDL_RWops *file;
	utf8 scoresPath[MAX_PATH];

	scenario_scores_get_path(scoresPath);

	file = SDL_RWFromFile(scoresPath, "wb");
	if (file == NULL) {
		log_error("Unable to save scenario scores.");
		return 0;
	}

	rct_scenario_scores_header header;
	header.scenario_count = gScenarioListCount;

	SDL_RWwrite(file, &header, sizeof(header), 1);
	if (gScenarioListCount > 0)
		SDL_RWwrite(file, gScenarioList, gScenarioListCount * sizeof(rct_scenario_basic), 1);

	SDL_RWclose(file);
	return 1;
}
