#include "cfg-var.h"
#include "str-com.h"

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
#include <math.h>


// CPP 
namespace nse {

	// a = b, b = a
	template< typename T >
	void swap_vars(T& a, T& b) {
		T c = a; a = b; b = c;
	}

	// = x^p
	int ipow(const int x, const int p)
	{
		if (p == 0) return 1;
		if (p == 1) return x;

		int tmp = ipow(x, p / 2);
		if (p % 2 == 0) return tmp*tmp;
		else
			return x*tmp*tmp;
	}
}


// 
// Implementation: cfgVariable
//
nse::cfgVariable::cfgVariable(
) :

	type(IS_UNDEF),
	eint(0), edouble((double)0), estring(NULL), ebool(false)
{
	name_memsize = 1;
	name = new char[name_memsize];
	*name = '\0';
}

nse::cfgVariable::cfgVariable(
	const char* ex_name,
	const char* ex_value) :

	type(IS_UNDEF),
	eint(0), edouble((double)0), estring(NULL), ebool(false)
{
	name_memsize = 1;
	name = new char[name_memsize];
	*name = '\0';

	set(ex_name, ex_value);
}

nse::cfgVariable::cfgVariable(
	const char* ex_value) :

	type(IS_UNDEF),
	eint(0), edouble((double)0), estring(NULL), ebool(false)
{
	name_memsize = 1;
	name = new char[name_memsize];
	*name = '\0';

	set(NULL, ex_value);
}


nse::cfgVariable::~cfgVariable()
{
	clear();
	delete[] name;
}

nse::cfgVariable::cfgVariable(const cfgVariable& var)
{
	type = var.type;

	name_memsize = strlen(var.name) + 1;
	name = new char[name_memsize];
	strcpy(name, var.name);

	if (type == IS_INT) eint = var.eint;
	else
		if (type == IS_DOUBLE) edouble = var.edouble;
		else
			if (type == IS_STRING) {
				estring = new char[strlen(var.estring) + 1];
				strcpy(estring, var.estring);
			}
			else
				if (type == IS_BOOLEAN) ebool = var.ebool;
}

const nse::cfgVariable&
nse::cfgVariable::operator=(cfgVariable var)
{
	swap(var);
	return (*this);
}

void nse::cfgVariable::swap(
	cfgVariable& var)
{
	nse::swap_vars(type, var.type);
	nse::swap_vars(name, var.name);
	nse::swap_vars(eint, var.eint);
	nse::swap_vars(edouble, var.edouble);
	nse::swap_vars(estring, var.estring);
	nse::swap_vars(ebool, var.ebool);

	nse::swap_vars(name_memsize, var.name_memsize);
}

nse::cfgVariable&
nse::cfgVariable::operator+=(const cfgVariable& var)
{
	// (int) += (int)
	// (double) += (double)
	// (string) += (string)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint += var.eint;
	}
	else
		if ((type == IS_DOUBLE) && (var.type == IS_DOUBLE))
		{
			edouble += (double)var.edouble;
		}
		else
			if ((type == IS_STRING) && (var.type == IS_STRING))
			{
				const size_t length = strlen(estring) + strlen(var.estring) + 1;
				char *buf = new char[length];

				strcpy(buf, estring);
				strcat(buf, var.estring);

				delete[] estring;
				estring = buf;
			}
			else
			{
				clear();	// type:= IS_UNDEF
			}

	return (*this);
}

nse::cfgVariable&
nse::cfgVariable::operator-=(const cfgVariable& var)
{
	// (int) -= (int)
	// (double) -= (double)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint -= var.eint;
	}
	else
		if ((type == IS_DOUBLE) && (var.type == IS_DOUBLE)) {
			edouble -= var.edouble;
		}
		else
		{
			clear();	// type:= IS_UNDEF
		}

	return (*this);
}

nse::cfgVariable&
nse::cfgVariable::operator*=(const cfgVariable& var)
{
	// (int) *= (int)
	// (double) *= (double)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint *= var.eint;
	}
	else
		if ((type == IS_DOUBLE) && (var.type == IS_DOUBLE)) {
			edouble *= var.edouble;
		}
		else
		{
			clear();	// type:= IS_UNDEF
		}

	return (*this);
}

nse::cfgVariable&
nse::cfgVariable::operator/=(const cfgVariable& var)
{
	// (int) /= (int)
	// (double) /= (double)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint /= var.eint;
	}
	else
		if ((type == IS_DOUBLE) && (var.type == IS_DOUBLE)) {
			edouble /= var.edouble;
		}
		else
		{
			clear();	// type:= IS_UNDEF
		}

	return (*this);
}

nse::cfgVariable&
nse::cfgVariable::operator%=(const cfgVariable& var)
{
	// int %= (int)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint %= var.eint;
	}
	else
	{
		clear();	// type:= IS_UNDEF
	}

	return (*this);
}

nse::cfgVariable&
nse::cfgVariable::operator^=(const cfgVariable& var)
{
	// (int) ^= (int)
	// (double) ^= (double)
	if ((type == IS_INT) && (var.type == IS_INT)) {
		eint = ipow(eint, var.eint);
	}
	else
		if ((type == IS_DOUBLE) && (var.type == IS_DOUBLE)) {
			edouble = pow(edouble, var.edouble);
		}
		else
		{
			clear();	// type:= IS_UNDEF
		}

	return (*this);
}

// check if we should really use const on return (all operators) ->
//
const nse::cfgVariable
nse::cfgVariable::operator+(const cfgVariable& var) const
{
	return cfgVariable(*this) += var;
}

const nse::cfgVariable
nse::cfgVariable::operator-(const cfgVariable& var) const
{
	return cfgVariable(*this) -= var;
}

const nse::cfgVariable
nse::cfgVariable::operator*(const cfgVariable& var) const
{
	return cfgVariable(*this) *= var;
}

const nse::cfgVariable
nse::cfgVariable::operator/(const cfgVariable& var) const
{
	return cfgVariable(*this) /= var;
}

const nse::cfgVariable
nse::cfgVariable::operator%(const cfgVariable& var) const
{
	return cfgVariable(*this) %= var;
}

const nse::cfgVariable
nse::cfgVariable::operator^(const cfgVariable& var) const
{
	return cfgVariable(*this) ^= var;
}

const nse::cfgVariable
nse::cfgVariable::operator-() const
{
	// -(int)
	// -(double)
	cfgVariable var;	// default: type:= IS_UNDEF

	if (type == IS_INT) {
		var = (*this);
		var.eint = -var.eint;
	}
	else
		if (type == IS_DOUBLE) {
			var = (*this);
			var.edouble = -var.edouble;
		}
	return var;
}

const nse::cfgVariable
nse::cfgVariable::operator+() const
{
	// +(int)
	// +(double)
	cfgVariable var;	// default: type:= IS_UNDEF

	if (type == IS_INT) {
		var = (*this);
	}
	else
		if (type == IS_DOUBLE) {
			var = (*this);
		}
	return var;
}
// <-
//

void nse::cfgVariable::set(const char* ex_name,
	const char* ex_value)
{
	clear();		// default: type = IS_UNDEF
	change_name(ex_name);

	if (is_integer(ex_value, &eint)) type = IS_INT;
	else
		if (is_double(ex_value, &edouble)) type = IS_DOUBLE;
		else
			if (is_valid_string(ex_value))
			{
				type = IS_STRING;
				estring = new char[strlen(ex_value) + 1];
				strcpyrm(estring, ex_value, '"');
				return;
			}
			else
				if (is_boolean(ex_value, &ebool)) type = IS_BOOLEAN;
}

void nse::cfgVariable::change_name(const char* ex_name)
{
	if (ex_name == NULL) return;	// keeping old name

	const size_t ex_length = strlen(ex_name) + 1;
	if (ex_length > name_memsize) {
		delete[] name;

		name_memsize = ex_length;
		name = new char[name_memsize];
	}

	strcpy(name, ex_name);
}


void nse::cfgVariable::clear()
{
	if (type == IS_STRING) {
		delete[] estring;
		estring = NULL;
	}

	type = IS_UNDEF;
}

int nse::cfgVariable::get_value(int* value) const
{
	if (type != IS_INT) return 0;

	(*value) = eint;
	return 1;
}

int nse::cfgVariable::get_value(float* value) const
{
	if (type != IS_DOUBLE) return 0;

	(*value) = (float)edouble;
	return 1;
}

int nse::cfgVariable::get_value(double* value) const
{
	if (type != IS_DOUBLE) return 0;

	(*value) = edouble;
	return 1;
}

int nse::cfgVariable::get_value(char** value) const
{
	if (type != IS_STRING) return 0;

	(*value) = new char[strlen(estring) + 1];
	strcpy(*value, estring);
	return 1;
}

int nse::cfgVariable::get_value(std::string& value) const
{
	if (type != IS_STRING) return 0;

	value = std::string(estring);
	return 1;
}

int nse::cfgVariable::get_value(bool* value) const
{
	if (type != IS_BOOLEAN) return 0;

	(*value) = ebool;
	return 1;
}


nse::cfgVariable::VAR_TYPE
nse::cfgVariable::get_type() const {
	return type;
}

bool nse::cfgVariable::is_varname(const char* ex_name) const
{
	if (ex_name == NULL) return false;
	return (!strcmp(ex_name, name));
}

bool nse::cfgVariable::is_valid_name() const
{
	return (strlen(name) > 0);
}

bool nse::cfgVariable::is_valid_type() const
{
	return (type != IS_UNDEF);
}

bool nse::cfgVariable::is_eq_name(const cfgVariable& var) const
{
	return (!strcmp(name, var.name));
}

void nse::cfgVariable::print() const
{
	if (type == IS_INT)
		printf(" > INT '%s' = %i\n", name, eint);
	else
		if (type == IS_DOUBLE)
			printf(" > DOUBLE '%s' = %f\n", name, edouble);
		else
			if (type == IS_STRING)
				printf(" > STRING '%s' = %s\n", name, estring);
			else
				if (type == IS_BOOLEAN)
					printf(" > BOOLEAN '%s' = %s\n", name, ebool ? "true" : "false");
				else
					printf(" > UNDEFINED '%s'\n", name);
}