#include "cfg-vec.h"

#include "scm-mem.h"
#include <string.h>
#include <stdio.h>

// -------------------------------------------------------------------------------------------- //
// * Implementation *
// -------------------------------------------------------------------------------------------- //


scm::cfgVector::cfgVector() :
	size(0), nalloc(0)
{
	name_memsize = 1;
	name = new char[name_memsize];
	*name = '\0';
}

scm::cfgVector::cfgVector(const cfgValue& _value) :
	size(1), nalloc(1)
{
	value = new cfgValue[nalloc];
	*value = _value;

	name_memsize = 1;
	name = new char[name_memsize];
	*name = '\0';
}

scm::cfgVector::~cfgVector()
{
	if (nalloc > 0) delete[] value;
	delete[] name;
}

scm::cfgVector::cfgVector(const cfgVector& x) :
	size(x.size), nalloc(size)
{
	if (nalloc > 0) {
		value = new cfgValue[nalloc];
		for (int k = 0; k < size; k++)
			value[k] = x.value[k];
	}

	name_memsize = strlen(x.name) + 1;
	name = new char[name_memsize];
	strcpy(name, x.name);
}
// -------------------------------------------------------------------------------------------- //

// * swap & assignment * //
// -------------------------------------------------------------------------------------------- //
const scm::cfgVector& scm::cfgVector::operator=(cfgVector x)
{
	swap(x);
	return (*this);
}

void scm::cfgVector::swap(cfgVector& x)
{
	scm::swap(size, x.size);
	scm::swap(nalloc, x.nalloc);
	scm::swap(value, x.value);

	scm::swap(name_memsize, x.name_memsize);
	scm::swap(name, x.name);
}
// -------------------------------------------------------------------------------------------- //

// * set & add calls * //
// -------------------------------------------------------------------------------------------- //
void scm::cfgVector::set(const cfgValue& _value)
{
	if (nalloc == 0) 
	{
		nalloc = 1;
		value = new cfgValue[nalloc];
	}

	size = 1;
	*value = _value;
}

void scm::cfgVector::set(const cfgValue& _value, const int _size)
{
	if (nalloc < _size)
	{
		if (nalloc > 0) delete[] value;

		nalloc = max(nalloc + c_nalloc_inc, _size);
		value = new cfgValue[nalloc];
	}

	size = _size;
	for (int k = 0; k < size; k++)
		value[k] = _value;
}

void scm::cfgVector::set(const int* x, const int _size)
{
	if (nalloc < _size)
	{
		if (nalloc > 0) delete[] value;

		nalloc = max(nalloc + c_nalloc_inc, _size);
		value = new cfgValue[nalloc];
	}

	size = _size;
	for (int k = 0; k < size; k++)
		value[k] = cfgValue(x[k]);
}

void scm::cfgVector::set(const float* x, const int _size)
{
	if (nalloc < _size)
	{
		if (nalloc > 0) delete[] value;

		nalloc = max(nalloc + c_nalloc_inc, _size);
		value = new cfgValue[nalloc];
	}

	size = _size;
	for (int k = 0; k < size; k++)
		value[k] = cfgValue(x[k]);
}

void scm::cfgVector::set(const double* x, const int _size)
{
	if (nalloc < _size)
	{
		if (nalloc > 0) delete[] value;

		nalloc = max(nalloc + c_nalloc_inc, _size);
		value = new cfgValue[nalloc];
	}

	size = _size;
	for (int k = 0; k < size; k++)
		value[k] = cfgValue(x[k]);
}

void scm::cfgVector::set(const long double* x, const int _size)
{
	if (nalloc < _size)
	{
		if (nalloc > 0) delete[] value;

		nalloc = max(nalloc + c_nalloc_inc, _size);
		value = new cfgValue[nalloc];
	}

	size = _size;
	for (int k = 0; k < size; k++)
		value[k] = cfgValue(x[k]);
}

bool scm::cfgVector::change(const int idx, const cfgValue& _value)
{
	if ((idx < 0) || (idx >= size)) return false;
	
	value[idx] = _value;
	return true;
}

void scm::cfgVector::append(const cfgValue& _value)
{
	if (size == nalloc)
	{
		if (nalloc == 0) 
		{
			set(_value);
			return;
		}

		cfgValue *hvalue;
		hvalue = new cfgValue[nalloc + c_nalloc_inc];

		for (int k = 0; k < size; k++) {
			hvalue[k].swap(value[k]);
		}

		delete[] value;	// size > 0
		value = hvalue;

		nalloc += c_nalloc_inc;
	}

	value[size] = _value;
	size++;
}

void scm::cfgVector::set_name(const char* ex_name)
{
	if (ex_name == NULL) return;	// keeping old name

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

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

	strcpy(name, ex_name);
}
// -------------------------------------------------------------------------------------------- //

// * get calls * //
// -------------------------------------------------------------------------------------------- //
const scm::cfgValue scm::cfgVector::get(const int idx) const
{
	if ((idx < 0) || (idx >= size)) return cfgValue();
	return value[idx];
}

bool scm::cfgVector::get(const int idx, int* _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get(const int idx, float* _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get(const int idx, double* _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get(const int idx, long double* _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get(const int idx, char** c_str) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(c_str);
}

bool scm::cfgVector::get(const int idx, std::string& _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get(const int idx, bool* _value) const
{
	if ((idx < 0) || (idx >= size)) return false;

	return value[idx].get(_value);
}

bool scm::cfgVector::get_if_single(int* _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

bool scm::cfgVector::get_if_single(float* _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

bool scm::cfgVector::get_if_single(double* _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

bool scm::cfgVector::get_if_single(long double* _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

bool scm::cfgVector::get_if_single(char** c_str) const
{
	if (size == 1) return (*value).get(c_str);
	return false;
}

bool scm::cfgVector::get_if_single(std::string& _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

bool scm::cfgVector::get_if_single(bool* _value) const
{
	if (size == 1) return (*value).get(_value);
	return false;
}

int scm::cfgVector::get_size() const
{
	return size;
}

const char* scm::cfgVector::get_name() const
{
	return name;
}
// -------------------------------------------------------------------------------------------- //

// * check calls * //
// -------------------------------------------------------------------------------------------- //
bool scm::cfgVector::is_defined() const
{
	for (int k = 0; k < size; k++) {
		if (value[k].get_type() == cfgValue::IS_UNDEF) 
			return false;
	}

	return (!is_empty());
}

bool scm::cfgVector::is_empty() const
{
	return (size == 0);
}

bool scm::cfgVector::is_single() const
{
	return (size == 1);
}

bool scm::cfgVector::is_name_empty() const
{
	return ((*name) == '\0');
}

bool scm::cfgVector::is_name_eq(const cfgVector& x) const
{
	return (!strcmp(name, x.name));
}

bool scm::cfgVector::is_name_eq(const char* _name) const
{
	if (_name == NULL) return false;
	return (!strcmp(name, _name));
}
// -------------------------------------------------------------------------------------------- //

// * print * //
// -------------------------------------------------------------------------------------------- //
void scm::cfgVector::print() const
{
	printf(" > '%s'[%i] = ", name, get_size());

	for (int k = 0; k < get_size(); k++)
	{
		value[k].print_typed();
		if (k < get_size() - 1) printf(", ");
	}
	printf("\n");
}

void scm::cfgVector::print_value() const
{
	if (size == 0) return;
	if (size == 1) {
		(*value).print();
		return;
	}

	printf("[");
	(*value).print();

	for (int k = 1; k < get_size(); k++)
	{
		printf(", ");
		value[k].print();
	}
	printf("]");
}
// -------------------------------------------------------------------------------------------- //

// * operators * //
// -------------------------------------------------------------------------------------------- //
scm::cfgVector& scm::cfgVector::operator+=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] += x.value[k];
	}
	else
		size = 0;	// setting null vector

	return (*this);
}
scm::cfgVector& scm::cfgVector::operator-=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] -= x.value[k];
	}
	else
		size = 0;	// setting null vector

	return (*this);
}

scm::cfgVector& scm::cfgVector::operator*=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] *= x.value[k];
	}
	else
		size = 0;	// setting null vector

	return (*this);
}

scm::cfgVector& scm::cfgVector::operator/=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] /= x.value[k];
	}
	else
		size = 0;	// setting null vector

	return (*this);
}

scm::cfgVector& scm::cfgVector::operator%=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] %= x.value[k];
	}
	else
		size = 0;	// setting null vector
	
	return (*this);
}

scm::cfgVector& scm::cfgVector::operator^=(const cfgVector& x)
{
	if (size == x.size)
	{
		for (int k = 0; k < size; k++)
			value[k] ^= x.value[k];
	}
	else
		size = 0;	// setting null vector

	return (*this);
}

const scm::cfgVector scm::cfgVector::operator+(const cfgVector& x) const
{
	return cfgVector(*this) += x;
}
const scm::cfgVector scm::cfgVector::operator-(const cfgVector& x) const
{
	return cfgVector(*this) -= x;
}
const scm::cfgVector scm::cfgVector::operator*(const cfgVector& x) const
{
	return cfgVector(*this) *= x;
}
const scm::cfgVector scm::cfgVector::operator/(const cfgVector& x) const
{
	return cfgVector(*this) /= x;
}
const scm::cfgVector scm::cfgVector::operator%(const cfgVector& x) const
{
	return cfgVector(*this) %= x;
}
const scm::cfgVector scm::cfgVector::operator^(const cfgVector& x) const
{
	return cfgVector(*this) ^= x;
}

const scm::cfgVector scm::cfgVector::operator==(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++) 
			res.value[k] = (value[k] == x.value[k]);
	}

	return res;
}
const scm::cfgVector scm::cfgVector::operator!=(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = (value[k] != x.value[k]);
	}

	return res;
}
const scm::cfgVector scm::cfgVector::operator<(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = (value[k] < x.value[k]);
	}

	return res;
}
const scm::cfgVector scm::cfgVector::operator>(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = (value[k] > x.value[k]);
	}

	return res;
}
const scm::cfgVector scm::cfgVector::operator<=(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = (value[k] <= x.value[k]);
	}

	return res;
}
const scm::cfgVector scm::cfgVector::operator>=(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = (value[k] >= x.value[k]);
	}

	return res;
}

const scm::cfgVector scm::cfgVector::operator-() const
{
	cfgVector res((*this));;
	for (int k = 0; k < size; k++) 
		res.value[k] = -value[k];

	return res;
}
const scm::cfgVector scm::cfgVector::operator+() const
{
	cfgVector res((*this));;
	for (int k = 0; k < size; k++)
		res.value[k] = +value[k];

	return res;
}

const scm::cfgVector scm::cfgVector::logical_and(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = value[k].logical_and(x.value[k]);
	}

	return res;
}

const scm::cfgVector scm::cfgVector::logical_or(const cfgVector& x) const
{
	cfgVector res;
	if (size == x.size)
	{
		res.set(cfgValue(), size);
		for (int k = 0; k < size; k++)
			res.value[k] = value[k].logical_or(x.value[k]);
	}

	return res;
}
// -------------------------------------------------------------------------------------------- //

// * functions * //
// -------------------------------------------------------------------------------------------- //
const scm::cfgVector 
scm::cfgVector::cfg_sin(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_sin(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_cos(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_cos(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_tan(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_tan(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_log(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_log(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_sqrt(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_sqrt(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_abs(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_abs(x.value[k]);

	return y;
}

const scm::cfgVector 
scm::cfgVector::cfg_to_string(const cfgVector& x)
{
	cfgVector y;
	y.set(cfgValue(), x.size);

	for (int k = 0; k < y.size; k++)
		y.value[k] = cfgValue::cfg_to_string(x.value[k]);

	return y;
}

const scm::cfgVector
scm::cfgVector::cfg_vecmin(const cfgVector& x)
{
	if (x.size > 0)
	{
		cfgValue _min = x.value[0];
		for (int k = 1; k < x.size; k++) 
		{
			cfgValue cmp = (x.value[k] < _min);
			
			bool is_less;
			if (!cmp.get(&is_less)) return cfgVector();

			if (is_less) _min = x.value[k];
		}
		return cfgVector(_min);
	}
	else
		return cfgVector();
}

const scm::cfgVector
scm::cfgVector::cfg_vecmax(const cfgVector& x)
{
	if (x.size > 0)
	{
		cfgValue _max = x.value[0];
		for (int k = 1; k < x.size; k++)
		{
			cfgValue cmp = (x.value[k] > _max);

			bool is_greater;
			if (!cmp.get(&is_greater)) return cfgVector();

			if (is_greater) _max = x.value[k];
		}
		return cfgVector(_max);
	}
	else
		return cfgVector();
}

const scm::cfgVector 
scm::cfgVector::cfg_min(const cfgVector& a, const cfgVector& b)
{
	cfgVector _min;
	if (a.size == b.size)
	{
		_min.set(cfgValue(), a.size);
		for (int k = 0; k < _min.size; k++)
			_min.value[k] = cfgValue::cfg_min(a.value[k], b.value[k]);
	}

	return _min;
}

const scm::cfgVector 
scm::cfgVector::cfg_max(const cfgVector& a, const cfgVector& b)
{
	cfgVector _max;
	if (a.size == b.size)
	{
		_max.set(cfgValue(), a.size);
		for (int k = 0; k < _max.size; k++)
			_max.value[k] = cfgValue::cfg_max(a.value[k], b.value[k]);
	}

	return _max;
}

const scm::cfgVector 
scm::cfgVector::cfg_dot_product(const cfgVector& a, const cfgVector& b)
{
	if ((a.size == b.size) && (a.size > 0))
	{
		cfgValue dp(0);
		for (int k = 0; k < a.size; k++)
			dp += a.value[k] * b.value[k];

		return cfgVector(dp);
	}
	else
		return cfgVector();
}

const scm::cfgVector 
scm::cfgVector::cfg_l2norm(const cfgVector& x)
{
	if (x.size == 0) return cfgVector();

	cfgValue norm((double)0);
	for (int k = 0; k < x.size; k++)
		norm += x.value[k] * x.value[k];

	return cfgVector(cfgValue::cfg_sqrt(norm));
}

const scm::cfgVector 
scm::cfgVector::cfg_cnorm(const cfgVector& x)
{
	if (x.size == 0) return cfgVector();

	cfgValue norm(cfgValue::cfg_abs(x.value[0]));
	for (int k = 1; k < x.size; k++)
	{
		cfgValue cfg_is_max = (cfgValue::cfg_abs(x.value[k]) > norm);
		bool is_max;
		if (!cfg_is_max.get(&is_max))
			return cfgVector();

		if (is_max) norm = cfgValue::cfg_abs(x.value[k]);
	}

	return cfgVector(norm);
}
// -------------------------------------------------------------------------------------------- //