////////////////////////////////////////////////////////////////////////////////
// Implementation of configuration class for MechVM
// Copyright Björn Ganster 
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#include "Config.h"
#include "MemoryBlock.h"

#define FILE_NUMBER FILE_NO_CONFIG_CPP

////////////////////////////////////////////////////////////////////////////////
// Constructor

Config::Config()
: ViewerBackgroundColor (1, 1, 1), 
  inactiveModeButtonColor (0.33f, 0.33f, 0.33f), 
  ActiveModeButtonColor (0.66f, 0.66f, 0.66f),
  m_experimentalFeaturesEnabled (false)
{
}

////////////////////////////////////////////////////////////////////////////////
// Destructor

Config::~Config()
{
}

////////////////////////////////////////////////////////////////////////////////
// Read configuration

bool Config::readFromFile (const PathChar* FileName)
{
	SET_SCOPE;
	MemoryBlock mb;
	ConfigFN = BGCPString(FileName, CP_SYSTEM);

	if (mb.loadFromFile(FileName)) {
		size_t offset = 0;
		BGCPString line;
		size_t EOL = offset;
		bool done = offset >= mb.getSize();

		while (!done) {
			line.clear();
			while (!done) {
				SET_OP;
				char c = mb.get<char>(offset);
				if (c != 0x0a && c != 0x0d) {
					if (c != 0) {
						line.add(&c, CP_ASCII, 1);
					}
					++offset;
					if (offset >= mb.getSize()) {
						done = true;
					}
				} else
					done = true;
			}

			if (offset+1 < mb.getSize()) {
				SET_OP;
				char c = mb.get<char>(offset);
				char d = mb.get<char>(offset+1);

				if (c == 0x0d) {
					if (d != 0x0a) {
						++offset;
					} else {
						offset += 2;
					}
				} else if (c == 0x0a) {
					++offset;
				}
				done = offset >= mb.getSize();
			} else {
				done = true;
			}

			SET_OP;
			size_t len = line.getByteCount();
			size_t endOfFirstWord = line.scan (' ', 0, len);
			BGCPString key(line.getPtr<char>(), endOfFirstWord, line.getCP());
			BGCPString value(line.getPtr<char>(endOfFirstWord+1), len, line.getCP());
			keys.push_back(key);
			values.push_back (value);
		}
	}
	
	SET_OP;
	BGCPString msg = BGCPString::fromInt(keys.size()) + BGCPString(" keys loaded from config");
	msg.printLn();

	for (size_t i = 0; i < getKeyCount(); ++i) {
		BGCPString key = keys[i];
		BGCPString value = values[i];

		if (key == "ViewerBackgroundColor") {
			ViewerBackgroundColor = parseColor (value);
		} else if (key == "inactiveModeButtonColor") {
			inactiveModeButtonColor = parseColor (value);
		} else if (key == "TextColor") {
			TextColor = parseColor (value);
		} else if (key == "ActiveModeButtonColor") {
			ActiveModeButtonColor = parseColor (value);

		} else if (key == "Experimental_Features") {
			m_experimentalFeaturesEnabled = true;
		}
	}

	RETURN true;
}

////////////////////////////////////////////////////////////////////////////////
// Access config

Config* cfg;

Config* getConfig()
{
   return cfg;
}

Config* createConfig()
{
	cfg = new Config();
	return cfg;
}

////////////////////////////////////////////////////////////////////////////////
// Get/set information on installed games

void Config::addKeyValuePair (const BGCPString& key, const BGCPString& value)
{
	SET_SCOPE;
	keys.push_back(key);
	values.push_back(value);
	writeConfig();
	RETURN;
}

void Config::writeConfig()
{
	SET_SCOPE;

	// Write back changes to file
	BGCPString str;
	for (size_t i = 0; i < keys.size(); ++i) {
		if (!keys[i].empty() || !values[i].empty()) {
			str += keys[i];
			str += " ";
			str += values[i];
		}
		str += "\n";
	}

	str.convertToCP(CP_ASCII);
	str.saveToFile(ConfigFN.getAsPath(), CP_ASCII);
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////

 BGCPString Config::getValue (const BGCPString& key) const
{
	SET_SCOPE;
	size_t i = 0;

	while (i < keys.size()) {
		const BGCPString& keyI = keys[i];
		if (keyI == key) {
			RETURN values[i];
		} else {
			i++;
		}
	}

	RETURN BGCPString();
}

////////////////////////////////////////////////////////////////////////////////
// Find first line after given line that contains str

int Config::FindFirstKeyAfterLine (int line, const char* str)
{
	SET_SCOPE;

	while (line < (int) getKeyCount()) {
		if (getKey(line) == str) {
			RETURN line;
		} else
			++line;
	}

	RETURN -1;
}

////////////////////////////////////////////////////////////////////////////////
// Log string, either to screen or to screen and file

// Change key/value pair
void Config::setKeyValuePair (size_t i, const BGCPString& key, const BGCPString& value)
{
	SET_SCOPE;
    if (i < keys.size()) {
        keys[i] = key;
        values[i] = value;
        writeConfig();
    }
	RETURN;
}

// Insert key/value pair
void Config::insertKeyValuePair (size_t i, const BGCPString& key, const BGCPString& value)
{
	SET_SCOPE;
    if (i < keys.size()) {
        std::vector<BGCPString>::iterator keyI = keys.begin() + i;
        keys.insert(keyI, key);
        std::vector<BGCPString>::iterator valueI = values.begin() + i;
        values.insert(valueI, value);
        writeConfig();
    } else {
        setKeyValuePair(i, key, value);
    }
	RETURN;
}

bool Config::hasKeyValuePair (const char* key, const char* value)
{
	SET_SCOPE;
	bool found = false;

	for (size_t i = 0; i < keys.size(); ++i) {
		if (keys[i] == key
			&&  values[i] == value)
		{
			found = true;
		}
	}

	RETURN found;
}

int Config::findValue (const char* value)
{
	SET_SCOPE;

	for (size_t i = 0; i < keys.size(); ++i) {
		if (values[i] == value)
		{
			RETURN (int) i;
		}
	}

	RETURN -1;
}

double Config::getDouble(const BGCPString& key) const
{
	SET_SCOPE;
    BGCPString value = getValue (key);
    RETURN value.toDouble();
}

// Get settings
Color& Config::getViewerBackgroundColor()
{
	return ViewerBackgroundColor;
}

Color& Config::getInactiveModeButtonColor()
{
	return inactiveModeButtonColor;
}

Color& Config::getActiveModeButtonColor()
{
	return ActiveModeButtonColor;
}

Color& Config::getTextColor()
{
	return TextColor;
}

bool Config::experimentalFeaturesEnabled() const
{
	return m_experimentalFeaturesEnabled;
}

bool experimentalFeaturesEnabled()
{
	return getConfig()->experimentalFeaturesEnabled();
}

const BGCPString& Config::getValue (size_t i) const
{
	return values[i];
}
const BGCPString& Config::getKey (size_t i) const
{
	return keys[i];
}

// Get number of key/value pairs
size_t Config::getKeyCount() const
{
	return keys.size();
}

// Functions to query how joystick axis are assigned to aerospace fighter movement
int Config::getRollAxis()
{
	return 0;
}

int Config::getPitchAxis()
{
	return 1;
}

int Config::getThrottleAxis()
{
	// Logitech joystick: 3, Microsoft joystick: 2
	return 3;
}

// Remove game from config
void Config::removeGame(const BGCPString& gameNameToRemove)
{
	BGCPString currGameName;
	size_t lineToRead = 0;
	std::vector<BGCPString> newKeys;
	std::vector<BGCPString> newValues;

	// From here on, we may have to move further lines
	while (lineToRead < keys.size()) {
		if (keys[lineToRead] == "GameName") {
			currGameName = values[lineToRead];
		}
		if (currGameName != gameNameToRemove) {
			newKeys.push_back(keys[lineToRead]);
			newValues.push_back(values[lineToRead]);
		}
		++lineToRead;
	}

	keys = newKeys;
	values = newValues;
}
