/*
    PIP - Platform Independent Primitives
    Variable, Struct (simple serialization)
    Copyright (C) 2013  Ivan Pelipenko peri4ko@gmail.com

    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 "pivariable.h"


bool PIVariant::operator ==(const PIVariant & v) const {
	if (type != v.type) return false;
	switch (type) {
	case PIVariant::Bool: return vBool == v.vBool;
	case PIVariant::Char: return vChar == v.vChar;
	case PIVariant::Short: return vShort == v.vShort;
	case PIVariant::Int: return vInt == v.vInt;
	case PIVariant::Long: return vLong == v.vLong;
	case PIVariant::LLong: return vLLong == v.vLLong;
	case PIVariant::UChar: return vUChar == v.vUChar;
	case PIVariant::UShort: return vUShort == v.vUShort;
	case PIVariant::UInt: return vUInt == v.vUInt;
	case PIVariant::ULong: return vULong == v.vULong;
	case PIVariant::ULLong: return vULLong == v.vULLong;
	case PIVariant::Float: return vFloat == v.vFloat;
	case PIVariant::Double: return vDouble == v.vDouble;
	case PIVariant::LDouble: return vLDouble == v.vLDouble;
	case PIVariant::String: return vString == v.vString;
	case PIVariant::StringList: return vStringList == v.vStringList;
	};
	return false;
}


void PIVariant::setValueOnly(const PIString & s) {
	switch (type) {
	case PIVariant::Bool: vBool = s.toBool(); break;
	case PIVariant::Char: vChar = s.toChar(); break;
	case PIVariant::Short: vShort = s.toShort(); break;
	case PIVariant::Int: vInt = s.toInt(); break;
	case PIVariant::Long: vLong = s.toLong(); break;
	case PIVariant::LLong: vLLong = s.toLLong(); break;
	case PIVariant::UChar: vUChar = s.toChar(); break;
	case PIVariant::UShort: vUShort = s.toShort(); break;
	case PIVariant::UInt: vUInt = s.toInt(); break;
	case PIVariant::ULong: vULong = s.toLong(); break;
	case PIVariant::ULLong: vULLong = s.toLLong(); break;
	case PIVariant::Float: vFloat = s.toFloat(); break;
	case PIVariant::Double: vDouble = s.toDouble(); break;
	case PIVariant::LDouble: vLDouble = s.toLDouble(); break;
	case PIVariant::String: vString = s; break;
	case PIVariant::StringList: vStringList = s.split("%|%"); break;
	};
}


PIString PIVariant::stringValue() const {
	switch (type) {
	case PIVariant::Bool: return (vBool ? "true" : "false");
	case PIVariant::Char: return PIString::fromNumber(vChar);
	case PIVariant::Short: return PIString::fromNumber(vShort);
	case PIVariant::Int: return PIString::fromNumber(vInt);
	case PIVariant::Long: return PIString::fromNumber(vLong);
	case PIVariant::LLong: return PIString::fromNumber(static_cast<long>(vLLong));
	case PIVariant::UChar: return PIString::fromNumber(vUChar);
	case PIVariant::UShort: return PIString::fromNumber(vUShort);
	case PIVariant::UInt: return PIString::fromNumber(vUInt);
	case PIVariant::ULong: return PIString::fromNumber(static_cast<long>(vULong));
	case PIVariant::ULLong: return PIString::fromNumber(static_cast<long>(vULLong));
	case PIVariant::Float: return PIString::fromNumber(vFloat);
	case PIVariant::Double: return PIString::fromNumber(vDouble);
	case PIVariant::LDouble: return PIString::fromNumber(vLDouble);
	case PIVariant::String: return vString;
	case PIVariant::StringList: return vStringList.join("%|%");
	};
	return vString;
}


PIVariant PIVariant::readFromString(const PIString & s) {
	int i = s.find(':');
	if (i < 0 || s.length() < 2) return PIVariant(s);
	PIVariant ret;
	ret.type = PIVariant::fromString(s.left(i));
	ret.setValueOnly(s.right(s.length() - i - 1));
	return ret;
}


PIVariant::Type PIVariant::fromString(const PIString & str) {
	PIString s = str.trimmed().toLowerCase().replace(" ", "");
	if (s == "bool") return PIVariant::Bool;
	if (s == "char" || s == "sbyte") return PIVariant::Char;
	if (s == "short" || s == "short int" || s == "signed short" || s == "signed short int" || s == "sword") return PIVariant::Short;
	if (s == "int" || s == "signed" || s == "signed int") return PIVariant::Int;
	if (s == "long" || s == "long int" || s == "signed long" || s == "signed long int" || s == "sdword") return PIVariant::Long;
	if (s == "llong" || s == "long long" || s == "long long int" || s == "signed long long" || s == "signed long long int" || s == "sqword") return PIVariant::LLong;
	if (s == "uchar" || s == "byte") return PIVariant::UChar;
	if (s == "ushort" || s == "unsigned short" || s == "unsigned short int" || s == "word") return PIVariant::UShort;
	if (s == "uint" || s == "unsigned" || s == "unsigned int") return PIVariant::UInt;
	if (s == "ulong" || s == "unsigned long" || s == "unsigned long int" || s == "dword") return PIVariant::ULong;
	if (s == "ullong" || s == "unsigned long long" || s == "unsigned long long int" || s == "qword") return PIVariant::ULLong;
	if (s == "float") return PIVariant::Float;
	if (s == "double" || s == "real") return PIVariant::Double;
	if (s == "ldouble" || s == "long double") return PIVariant::LDouble;
	if (s == "pistring" || s == "string") return PIVariant::String;
	if (s == "pistringlist" || s == "vector<string>" || s == "vector<pistring>" || s == "pivector<string>" || s == "pivector<pistring>") return PIVariant::StringList;
	return PIVariant::Double;
}


PIString PIVariant::toString(const PIVariant::Type & var) {
	switch (var) {
	case PIVariant::Bool: return "bool";
	case PIVariant::Char: return "char";
	case PIVariant::Short: return "short";
	case PIVariant::Int: return "int";
	case PIVariant::Long: return "long";
	case PIVariant::LLong: return "llong";
	case PIVariant::UChar: return "uchar";
	case PIVariant::UShort: return "ushort";
	case PIVariant::UInt: return "uint";
	case PIVariant::ULong: return "ulong";
	case PIVariant::ULLong: return "ullong";
	case PIVariant::Float: return "float";
	case PIVariant::Double: return "double";
	case PIVariant::LDouble: return "ldouble";
	case PIVariant::String: return "string";
	case PIVariant::StringList: return "stringlist";
	}
	return "double";
}


uint PIVariant::variableSize(const PIVariant::Type & var) {
	switch (var) {
	case PIVariant::Bool: return sizeof(bool);
	case PIVariant::Char: return sizeof(char);
	case PIVariant::Short: return sizeof(short);
	case PIVariant::Int: return sizeof(int);
	case PIVariant::Long: return sizeof(long);
	case PIVariant::LLong: return sizeof(llong);
	case PIVariant::UChar: return sizeof(uchar);
	case PIVariant::UShort: return sizeof(ushort);
	case PIVariant::UInt: return sizeof(uint);
	case PIVariant::ULong: return sizeof(ulong);
	case PIVariant::ULLong: return sizeof(ullong);
	case PIVariant::Float: return sizeof(float);
	case PIVariant::Double: return sizeof(double);
	case PIVariant::LDouble: return sizeof(ldouble);
	default: break;
	}
	return 0;
}


double PIVariant::variableValue(const void * var_ptr, const PIVariant::Type & var) {
	switch (var) {
	case PIVariant::Bool: return (double)(*((bool * )var_ptr));
	case PIVariant::Char: return (double)(*((char * )var_ptr));
	case PIVariant::Short: return (double)(*((short * )var_ptr));
	case PIVariant::Int: return (double)(*((int * )var_ptr));
	case PIVariant::Long: return (double)(*((long * )var_ptr));
	case PIVariant::LLong: return (double)(*((llong * )var_ptr));
	case PIVariant::UChar: return (double)(*((uchar * )var_ptr));
	case PIVariant::UShort: return (double)(*((ushort * )var_ptr));
	case PIVariant::UInt: return (double)(*((uint * )var_ptr));
	case PIVariant::ULong: return (double)(*((ulong * )var_ptr));
	case PIVariant::ULLong: return (double)(*((ullong * )var_ptr));
	case PIVariant::Float: return (double)(*((float * )var_ptr));
	case PIVariant::Double: return (double)(*((double * )var_ptr));
	case PIVariant::LDouble: return (ldouble)(*((ldouble * )var_ptr));
	default: break;
	}
	return 0.;
}


void PIVariable::setVariable(const PIString & str) {
	type_ = PIVariant::fromString(str);
	size_ = PIVariant::variableSize(type_);
}


void PIVariable::writeVariable(void * dest) {
	switch (type_) {
	case PIVariant::Bool: *((bool * )((ullong)dest + offset)) = value_ > 0.; return;
	case PIVariant::Char: *((char * )((ullong)dest + offset)) = char(value_); return;
	case PIVariant::Short: *((short * )((ullong)dest + offset)) = short(value_); return;
	case PIVariant::Int: *((int * )((ullong)dest + offset)) = int(value_); return;
	case PIVariant::Long: *((long * )((ullong)dest + offset)) = long(value_); return;
	case PIVariant::LLong: *((llong * )((ullong)dest + offset)) = llong(value_); return;
	case PIVariant::UChar: *((uchar * )((ullong)dest + offset)) = uchar(value_); return;
	case PIVariant::UShort: *((ushort * )((ullong)dest + offset)) = ushort(value_); return;
	case PIVariant::UInt: *((uint * )((ullong)dest + offset)) = uint(value_); return;
	case PIVariant::ULong: *((ulong * )((ullong)dest + offset)) = ulong(value_); return;
	case PIVariant::ULLong: *((ullong * )((ullong)dest + offset)) = ullong(value_); return;
	case PIVariant::Float: *((float * )((ullong)dest + offset)) = float(value_); return;
	case PIVariant::Double: *((double * )((ullong)dest + offset)) = value_; return;
	case PIVariant::LDouble: *((ldouble * )((ullong)dest + offset)) = ldouble(value_); return;
	default: break;
	}
}


void PIStruct::parseFile(const PIString & file) {
	PIConfig conf(file, PIIODevice::ReadOnly);
	PIVariable var;
	PIString ts;
	uint sz = 0;
	vars.clear();
	for (int i = 0; i < conf.entriesCount(); ++i) {
		var.setVariable(conf.getValue(i));
		var.setName(conf.getName(i));
		var.offset = sz;
		sz += var.size();
		ts = conf.getComment(i);
		if (ts.length() > 0)
			var.setValue(ts.toDouble());
		else var.setValue(0.);
		vars.push_back(var);
	}
	size_ = sz;
}


void PIStruct::readData(const void * data) {
	for (uint i = 0; i < vars.size(); ++i)
		vars[i].readVariable(data);
}


void PIStruct::writeData(void * data) {
	for (uint i = 0; i < vars.size(); ++i)
		vars[i].writeVariable(data);
}

