/*
  File name: CCytadelaConfig.cpp
  Copyright: (C) 2006 - 2013 Tomasz Kazmierczak

  This file is part of Cytadela

 *   This program 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 "CCytadelaConfig.h"
#include "CSDLClass.h"
#include "Localization.h"
#include <cassert>
#include <fstream>
#include <string.h>

using namespace std;

class CConfigEntry {
private:
	string mEntryName;
	string::size_type findValuePosition(string &src);
public:
	CConfigEntry() {};
	CConfigEntry(const char *entryname) { setName(entryname); }
	void setName(const char *entryname) { mEntryName = entryname; }
	bool readValue(int32_t &dest, string &src);
	bool readValue(float &dest, string &src);
	bool readValue(char *dest, string &src, size_t destbuffsize);
};

string::size_type CConfigEntry::findValuePosition(string &src)
{
	//first find the position of the entry's name
	string::size_type position = src.find(mEntryName, 0);
	if(position == string::npos)
		return string::npos;
	//shift the position by the entry's name length
	position += mEntryName.length();

	//now find the '=' sign
	string::size_type equalsPos = src.find('=', position);
	if(position == string::npos or equalsPos > position)
		return string::npos;

	//and finally find the position of the value
	return src.find_first_not_of("=", position);
}

bool CConfigEntry::readValue(int32_t &dest, string &src)
{
	//find the position of the entry's value
	string::size_type position = findValuePosition(src);

	if(position == string::npos)
		return false;
	//get the C string from the C++ string
	const char *value = src.c_str();
	//get the value
	dest = atoi(&value[position]);
	return true;
}

bool CConfigEntry::readValue(float &dest, string &src)
{
	//find the position of the entry's value
	string::size_type position = findValuePosition(src);
	if(position == string::npos)
		return false;
	//get the C string from the C++ string
	const char *value = src.c_str();
	//get the value
	dest = atof(&value[position]);
	return true;
}

bool CConfigEntry::readValue(char *dest, string &src, size_t destbuffsize)
{
	//find the position of the entry's value
	string::size_type position = findValuePosition(src);
	if(position == string::npos)
		return false;

	//find the position of the first quotation mark
	string::size_type firstQuotationMark = src.find('\"', position);
	if(firstQuotationMark == string::npos or firstQuotationMark > position)
		return false;
	string::size_type valueBeginning = firstQuotationMark + 1;
	//and the second one
	string::size_type secondQuotationMark = src.find('\"', valueBeginning);
	if(secondQuotationMark == string::npos)
		return false;

	//the max of chars that we can copy into dest
	size_t count = secondQuotationMark - valueBeginning;
	if(count >= destbuffsize)
		count = destbuffsize - 1;

	memset(dest, 0, destbuffsize);
	src.copy(dest, count, valueBeginning);

	return true;
}

CCytadelaConfig *CCytadelaConfig::instance()
{
	static CCytadelaConfig inst;
	return &inst;
}

void CCytadelaConfig::init(char *sysDirPath)
{
	//path to the config file
	sprintf(mConfigFilePath, "%s/cytadela.cfg", sysDirPath);

	//init the game speed settings array
	static float gameSpeedSettings[] = {1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f,
	                                    1.6f, 1.7f, 1.8f, 1.9f, 2.0f};
	mGameSpeedSettings = gameSpeedSettings;
	//number of settings
	mNumOfGameSpeedSettings = sizeof(gameSpeedSettings) / sizeof(float);

	//init the mouse speed settings array
	static float mouseSpeedSettings[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f,
	                                     7.0f, 8.0f, 9.0f, 10.0f, 0.1f, 0.2f,
	                                     0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f};
	mMouseSpeedSettings = mouseSpeedSettings;
	//number of settings
	mNumOfMouseSpeedSettings = sizeof(mouseSpeedSettings) / sizeof(float);

	//localization
	sprintf(mLocalization, CYTADELA_DEFAULT_LOCALIZATION);
	//difficulty
	mDifficult = true;
	//default game speed
	mGameSpeedSetting = 5;
	mGameSpeed = gameSpeedSettings[5];
	//default mouse speed
	mMouseSpeedSetting = 0;
	mMouseSpeed = mouseSpeedSettings[0];

	//try to load the config file
	ifstream configFile(mConfigFilePath);
	string configuration;
	while(configFile.good() and not configFile.eof()) {
		string tmp;
		configFile >> tmp;
		configuration += tmp;
	}
	configFile.close();

	//get the game's speed setting from the config
	CConfigEntry entry("game_speed");
	float setting;
/*	if(entry.readValue(setting, configuration)) {
		int32_t i;
		//find the loaded speed setting in the settings array
		for(i = 0; i < mNumOfGameSpeedSettings; ++i)
			if(setting == mGameSpeedSettings[i]) break;
		//if found then set it
		if(i < mNumOfGameSpeedSettings) {
			mGameSpeedSetting = i;
			mGameSpeed = setting;
		}
	}*/
	//get the mouse's speed setting from the config
	entry.setName("mouse_speed");
	if(entry.readValue(setting, configuration)) {
		int32_t i;
		//find the loaded speed setting in the settings array
		for(i = 0; i < mNumOfMouseSpeedSettings; ++i)
			if(setting == mMouseSpeedSettings[i]) break;
		//if not found then set the default speed
		if(i < mNumOfMouseSpeedSettings) {
			mMouseSpeedSetting = i;
			mMouseSpeed = setting;
		}
	}

	//get the screen resolution
	entry.setName("game_screen_width");
	entry.readValue(mScreenSize[0], configuration);
	entry.setName("game_screen_height");
	entry.readValue(mScreenSize[1], configuration);

	CSDLClass *sdl = CSDLClass::instance();
	const ScreenSize *sizes = sdl->getScreenSizes();
	int32_t screenSizesCount = sdl->getScreenSizesCount();
	//search for actual screen size in the ScreenSizes array
	int32_t i;
	for(i = 0; i < screenSizesCount; ++i) {
		if((mScreenSize[0] == sizes[i].resolution[0]) and
			(mScreenSize[1] == sizes[i].resolution[1]))
			break;
	}
	//if not found then change to the first available
	if(i == screenSizesCount)
		i = sdl->findFirstAvailableScreenSize(-1);
	mScreenSize[0] = sizes[i].resolution[0];
	mScreenSize[1] = sizes[i].resolution[1];

	//get the localization name
	entry.setName("language");
	entry.readValue(mLocalization, configuration, sizeof(mLocalization));

	//get the audio status
	entry.setName("audio");
	char text[8] = {0};
	entry.readValue(text, configuration, sizeof(text));
	if(strcmp(text, "no") == 0)
		mAudioStatus = false;
	else mAudioStatus = true;

	//get the fullscreen status
	entry.setName("fullscreen");
	text[0] = 0;
	entry.readValue(text, configuration, sizeof(text));
	if(strcmp(text, "no") == 0)
		mFullScreen = false;
	else mFullScreen = true;

	mInitialized = true;
}

CCytadelaConfig::~CCytadelaConfig()
{
	assert(mInitialized);

	ofstream configFile(mConfigFilePath);

	configFile << "#Cytadela configuration file\n" << endl;

	//write the configuration
	configFile << "game_screen_width=" << mScreenSize[0] << ';' << endl;
	configFile << "game_screen_height=" << mScreenSize[1] << ';' << endl;
//	configFile << "game_speed=" << mGameSpeed << ';' << endl;
	configFile << "mouse_speed=" << mMouseSpeed << ';' << endl;
	configFile << "language=\"" << mLocalization << "\";" << endl;
	configFile << "audio=" << (mAudioStatus ? "\"yes\"" : "\"no\"") << ';' << endl;
	configFile << "fullscreen=" << (mFullScreen ? "\"yes\"" : "\"no\"") << ';' << endl;

	configFile.close();
}

void CCytadelaConfig::setScreenSize(int32_t width, int32_t height)
{
	assert(mInitialized);

	mScreenSize[0] = width;
	mScreenSize[1] = height;
}

void CCytadelaConfig::setLocalization(char *localization)
{
	assert(mInitialized);

	if(strlen(localization) < FILENAME_MAX)
		sprintf(mLocalization, "%s", localization);
}

void CCytadelaConfig::setNextGameSpeed()
{
	assert(mInitialized);

	mGameSpeedSetting++;
	if(mGameSpeedSetting == mNumOfGameSpeedSettings)
		mGameSpeedSetting = 0;
	mGameSpeed = mGameSpeedSettings[mGameSpeedSetting];
}

void CCytadelaConfig::setPrevGameSpeed()
{
	assert(mInitialized);

	mGameSpeedSetting--;
	if(mGameSpeedSetting < 0)
		mGameSpeedSetting = mNumOfGameSpeedSettings-1;
	mGameSpeed = mGameSpeedSettings[mGameSpeedSetting];
}

void CCytadelaConfig::setNextMouseSpeed()
{
	assert(mInitialized);

	mMouseSpeedSetting++;
	if(mMouseSpeedSetting == mNumOfMouseSpeedSettings)
		mMouseSpeedSetting = 0;
	mMouseSpeed = mMouseSpeedSettings[mMouseSpeedSetting];
}

void CCytadelaConfig::setPrevMouseSpeed()
{
	assert(mInitialized);

	mMouseSpeedSetting--;
	if(mMouseSpeedSetting < 0)
		mMouseSpeedSetting = mNumOfMouseSpeedSettings-1;
	mMouseSpeed = mMouseSpeedSettings[mMouseSpeedSetting];
}
