#include "cdutils_types.h"
#include "piconfig.h"
#include "pifile.h"
#include "pievaluator.h"
#include "cdutils_core.h"

using namespace CDUtils;

//int cdtype_debug_cnt = 1;

CDType::CDType() {
	index_ = -1;
	value_d = 0.;
	value_i = 0;
	value_b = calculated = false;
	cd_type_ = cdNull;
	parent = 0;
//	debug_cnt = cdtype_debug_cnt;
//	cdtype_debug_cnt++;
//	piCout << "[CDType]" << "create Null" << debug_cnt;
}




CDType::CDType(int i, const PIString & n, const PIString & t, const PIString & v, const PIString & f, const PIString & c, cdT cd_t) {
	index_ = i;
	name_ = n.trimmed();
	type_ = t.trimmed();
	value_s = v.trimmed();
	formula_ = f.trimmed();
	comment_ = c.trimmed();
	value_d = v.toDouble();
	value_i = v.toInt();
	value_b = v.toBool();
	cd_type_ = cd_t;
	calculated = false;
	parent = 0;
	if (type_ == "e") {
		enum_values = parseEnumComment(comment_);
//		piCout << enum_values.size() << enum_values;
	}
//	piCout << type_.size() << type_.toUTF8();
//	piCout << formula_.size() << formula_.toUTF8();
//	piCout << comment_.size() << comment_.toUTF8();
//	debug_cnt = cdtype_debug_cnt;
//	cdtype_debug_cnt++;
//	piCout << "[CDType] create" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK);
}


PIString CDType::type() const {
	if (type_.trimmed().isEmpty()) return "f";
//	piCout << "type =" << type_.trimmed() << ";" << type_ << "#";
	return type_;
}


PIString CDType::value() const {
	if (type_ == "b") return PIString::fromBool(value_b);
	return value_s;
}


void CDType::setValue(const PIString & value_) {
	formula_ = value_;
	value_d = formula_.toDouble();
	value_i = formula_.toInt();
	value_b = formula_.toBool();
}


void CDType::setFormula(const PIString & f) {
	formula_ = f;
	calculated = false;
	//PIEvaluator e;
	//calculate(&e);
}


bool CDType::calculate(PIEvaluator * e, PIVector<const CDType * > stack) {
	if (stack.contains(this)) {
		error_ = "Circular dependencies: ";
		piForeachC (CDType * k, stack)
			error_ << k->name() << " -> ";
		error_ << name();
		//piCout << error_;
		return false;
	}
	stack << this;
	if (calculated) return true;
	calculated = true;
	error_.clear();
	if (!parent) return true;
	//piCout << "calc" << name_ << (parent ? parent->alias : "root");
	value_s = formula_.trimmed();
	for (;;) {
		int ki = value_s.find("K[");
		if (ki < 0) break;
		int ke = value_s.find("]", ki + 2);
		if (ke < 0) break;
		PIString kp = value_s.mid(ki + 2, ke - ki - 2);
		//piCout << kp;
		CDType & k((*parent)[kp]);
		k.calculate(e, stack);
		value_s.replace(ki, ke - ki + 1, PIString::fromNumber(k.value_d));
	}
	value_d = formula_.toDouble();
	value_i = formula_.toInt();
	value_b = formula_.toBool();
	double ev = 0.;
	if (!e->check(value_s) && value_d == 0. && value_i == 0 && !value_b) {
		PIString f = formula_.trimmed().toLowerCase();
		if (f != "off" && f != "false" && f != "no" && !value_b) {
			error_ = e->error();
			return false;
		}
	} else
		if (e->isCorrect())
			ev = e->evaluate().real();
	//piCout << value_s << value_i << value_d << ev;
	//if ((value_d == 0.) || (piAbsd(value_d) < piAbsd(ev))) value_d = ev;
	//if ((value_i == 0) || (piAbsd(value_i) < piAbsd(ev))) value_i = int(ev);
	if ((value_d == 0.) || (ev != 0.)) value_d = ev;
	if ((value_i == 0) || (ev != 0.)) value_i = int(ev);
	value_b = value_b || (ev > 0.);
	if (value_i != 0) {
		if (value_d == 0.) value_d = value_i;
		value_b = value_i > 0;
	}
	if (value_d != 0.) {
		if (value_i == 0) value_i = value_d;
		value_b = value_d > 0.;
	}
	if (value_b) {
		if (value_d == 0.) value_d = 1.;
		if (value_i == 0) value_i = 1;
	}
	value_s = PIString::fromNumber(value_d);
	return true;
}


PIVariantTypes::Enum CDType::parseEnumComment(PIString c) {
	PIVariantTypes::Enum ret;
	if (c.isEmpty()) return ret;
	if (type_ == "e") {
		PIStringList sl = c.inBrackets('{', '}').split(",");
		int cval = 0;
		piForeach (PIString & s, sl) {
			s.trim();
			if (s.isEmpty()) continue;
			if (s[0].isDigit()) {
				int ind = s.find("-");
				if (ind > 0) {
					cval = s.left(ind).toInt();
					s.cutLeft(ind + 1).trim();
				}
			}
			ret << PIVariantTypes::Enumerator(cval, s);
			++cval;
		}
	}
	//piCout << c << "=" << ret;
	return ret;
}


//CDType::CDType(const CDType &cdt) {
//	index_ = cdt.index_;
//	name_ = cdt.name_;
//	type_ = cdt.type_;
//	value_s = cdt.value_s;
//	formula_ = cdt.formula_;
//	comment_ = cdt.comment_;
//	value_d = cdt.value_d;
//	value_i = cdt.value_i;
//	value_b = cdt.value_b;
//	cd_type_ = cdt.cd_type_;
//	debug_cnt = cdtype_debug_cnt;
//	cdtype_debug_cnt++;
//	piCout << "[CDType] copy" << debug_cnt << "->" << cdt.debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK);
//}


//CDType &CDType::operator =(const CDType &cdt) {
//	index_ = cdt.index_;
//	name_ = cdt.name_;
//	type_ = cdt.type_;
//	value_s = cdt.value_s;
//	formula_ = cdt.formula_;
//	comment_ = cdt.comment_;
//	value_d = cdt.value_d;
//	value_i = cdt.value_i;
//	value_b = cdt.value_b;
//	cd_type_ = cdt.cd_type_;
//	piCout << "[CDType] assign" << debug_cnt << "=" << cdt.debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK);
//	//debug_cnt = cdt.debug_cnt;
//	return *this;
//}


//CDType::~CDType() {
//	piCout << "[CDType] delete" << debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK);
//}


int CDSection::count(bool recursive) const {
	int ret = k.size_s();
	if (recursive) {
		PIMap<int, CDSection>::const_iterator i;
		for (i = s.begin(); i != s.end(); ++i)
			ret += i->second.count(recursive);
	}
	return ret;
}


int CDSection::sectionsCount() const {
	return s.size();
}


PIStringList CDSection::index_names() const {
	PIStringList ret;
	PIMap<int, CDType>::const_iterator i;
	for (i = k.begin(); i != k.end(); ++i)
		ret << i->second.name();
	return ret;
}


void CDSection::calculate() {
	/*CDCore::instance()->k_.*/prepareCalculate();
	PIEvaluator e;
	calculateRecursive(&e);
}


CDType & CDSection::getByName(const PIString & name_) {
	PIStringList np = name_.split(".");
	if (np.isEmpty()) return null;
	//piCout << np;
	CDSection * cs = this, * ns = 0;
	if (np.front().isEmpty()) {
		if (np.size_s() < 2) return null;
		cs = &(CDCore::instance()->k_);
		np.pop_front();
	}
	for (int i = 0; i < np.size_s() - 1; ++i) {
		if (np[i].isEmpty()) return null;
		bool isd = np[i][0].isDigit() || (np[i][0] == '-');
		int dv = 0;
		if (isd) dv = np[i].toInt();
		ns = 0;
		PIMap<int, CDSection>::iterator it;
		//piCout << np[i] << isd << dv;
		for (it = cs->s.begin(); it != cs->s.end(); ++it) {
			bool f = false;
			if (isd) f = (dv == it.key());
			else f = (np[i] == it.value().alias);
			//piCout << "s..." << it.key() << it.value().alias << f;
			if (f) {
				ns = &(it.value());
				break;
			}
		}
		//piCout << ns;
		if (!ns) return null;
		cs = ns;
	}
	PIMap<int, CDType>::iterator it;
	if (np.back().isEmpty()) return null;
	bool isd = np.back()[0].isDigit() || (np.back()[0] == '-');
	int dv = 0;
	if (isd) dv = np.back().toInt();
	//piCout << np.back() << isd << dv;
	for (it = cs->k.begin(); it != cs->k.end(); ++it) {
		bool f = false;
		if (isd) f = (dv == it.key());
		else f = (np.back() == it.value().name());
		//piCout << "k..." << it.key() << it.value().name() << f;
		if (f)
			return cs->k[it.key()];
	}
	return null;
}


CDType & CDSection::getByPath(const PIDeque<int> & path_) {
	if (path_.isEmpty()) return null;
	CDSection * s = this;
	for (int i = 0; i < path_.size_s() - 1; ++i)
		s = &(s->section(path_[i]));
	if (!s) return null;
	return (*s)[path_.back()];
}


void CDSection::write(PIIODevice * d, const PIString & prefix) {
	if (!d) return;
	if (k.isEmpty() && s.isEmpty()) return;
//	piCout << "[CDSection] write start";
	PIString l;
	if (prefix.isEmpty()) l = "[k]";
	else l = "[" + prefix + ".k]";
	l += "\n";
	d->write(l.toUTF8());
	l = "name = " + name + " \n";
	d->write(l.toUTF8());
	l = "alias = " + alias + " \n";
	d->write(l.toUTF8());
	PIMap<int, CDType>::iterator i;
	for (i = k.begin(); i != k.end(); ++i) {
		CDType & ck(i.value());
		l.clear(); l << ck.index() << ".f = " << ck.formula() << " #s " << ck.comment() << " \n";
		d->write(l.toUTF8());
		l.clear(); l << ck.index() << ".v = " << ck.value() << " #" << ck.type() << " " << ck.name() << " \n";
		d->write(l.toUTF8());
		if (!ck.enumValues().enum_list.isEmpty()) {
			l.clear(); l << ck.index() << ".ev = {";
			//PIVector<CDType::Enumerator> el = ck.enumValues();
			piForeachC (PIVariantTypes::Enumerator & e, ck.enumValues().enum_list)
				l << e.value << " - " << e.name << ", ";
			l.cutRight(2);
			l << "} \n";
			d->write(l.toUTF8());
		}
	}
	if (!s.isEmpty()) {
		if (prefix.isEmpty()) l = "s";
		else l = prefix + ".s";
		PIMap<int, CDSection>::iterator j;
		for (j = s.begin(); j != s.end(); ++j) {
			j.value().write(d, l + "." + PIString::fromNumber(j.key()));
		}
	}
	if (prefix.isEmpty()) {
		l = "[]\n";
		d->write(l.toUTF8());
	}
//	piCout << "[CDSection] write end";
}


void CDSection::read(const void * ep) {
//	piCout << "[CDSection] read start";
	k.clear();
	s.clear();
	PIConfig::Entry & e(*(PIConfig::Entry*)ep);
	name = e.getValue("k.name").value();
	alias = e.getValue("k.alias").value();
	PIConfig::Entry & kl = e.getValue("k");
	for (int i = 0; i < kl.childCount(); ++i) {
		const PIConfig::Entry * ke(kl.child(i));
		bool ok = false;
		int kid = ke->name().toInt(-1, &ok);
//		piCout << "[read]" << ke->name() << ke->value() << ok;
//		PIString n = ke->getValue("v").comment();
//		PIString t = n.takeLeft(1);
		if (ok) {
			CDType ck = CDType(kid, ke->getValue("v").comment(), ke->getValue("v").type(), ke->getValue("v").value(), ke->getValue("f").value(), ke->getValue("f").comment(), CDType::cdK);
			PIString ev = ke->getValue("ev", "");
			if (!ev.isEmpty())
				ck.enum_values = ck.parseEnumComment(ev);
			k[kid] = ck;
		}
	}
	PIConfig::Entry & sl = e.getValue("s");
	for (int i = 0; i < sl.childCount(); ++i) {
		const PIConfig::Entry * se(sl.child(i));
		int sid = se->name().toInt();
		s[sid].read(se);
	}
//	piCout << "[CDSection] read end";
}


void CDSection::update(CDSection & v, UpdateModeFlags mode) {
	if (mode[SaveByIndex] && mode[SaveByName]) {
		piCout << "[CDSection] update error: SaveByIndex | SaveByName mode is denied!";
		return;
	}
	//piCout << "[CDSection] update start";
	//piCout << "before" << k.size() << v.k.size();

	PIMap<int, PIString> prev_k_f_bi;
	PIMap<PIString, PIString> prev_k_f_bn;
	PIMap<int, CDType>::iterator i;
	if (mode[SaveByIndex]) {
		for (i = k.begin(); i != k.end(); ++i)
			prev_k_f_bi[i.key()] = i.value().formula();
	}
	if (mode[SaveByName]) {
		for (i = k.begin(); i != k.end(); ++i)
			prev_k_f_bn[i.value().name_] = i.value().formula();
	}
	if (!mode[Merge])
		k.clear();
	for (i = v.k.begin(); i != v.k.end(); ++i) {
		int id = i.key();
		PIString n = i.value().name();
		k[id] = i.value();
		if (mode[SaveByIndex]) {
			if (prev_k_f_bi.contains(id))
				k[id].setFormula(prev_k_f_bi[id]);
		}
		if (mode[SaveByName]) {
			if (prev_k_f_bn.contains(n))
				k[id].setFormula(prev_k_f_bn[n]);
		}
	}

	PIMap<int, CDSection> prev_s_bi;
	PIMap<PIString, CDSection> prev_s_bn;
	PIMap<int, CDSection>::iterator j;
	if (mode[SaveByIndex]) {
		for (j = s.begin(); j != s.end(); ++j)
			prev_s_bi[j.key()] = j.value();
	}
	if (mode[SaveByName]) {
		for (j = s.begin(); j != s.end(); ++j)
			prev_s_bn[j.value().alias] = j.value();
	}
	if (!mode[Merge])
		s.clear();
	for (j = v.s.begin(); j != v.s.end(); ++j) {
		int id = j.key();
		PIString n = j.value().alias;
		s[id] = j.value();
		if (mode[SaveByIndex]) {
			if (prev_s_bi.contains(id))
				s[id] = prev_s_bi[id];
		}
		if (mode[SaveByName]) {
			if (prev_s_bn.contains(n))
				s[id] = prev_s_bn[n];
		}
		s[id].update(j.value(), mode);
	}

	/*PISet<int> used;
	for (i = k.begin(); i != k.end(); ++i) {
		if (v.k.contains(i.key())) {
			PIString f = k[i.key()].formula_;
			CDType & cdt = v.k[i.key()];
			cdt.formula_ = f;
			k[i.key()] = cdt;
			used << i.key();
		}
		if (mode) {
			CDType & ck(k[i.key()]);
			if (prev_k_f_bn.contains(ck.name_))
				ck.setFormula(prev_k_f_bn[ck.name_]);
		}
	}
	//piCout << " after" << k.size();
	for (i = v.k.begin(); i != v.k.end(); ++i) {
		if (!used.contains(i.key()))
			k[i.key()] = i.value();
		CDType & ck(k[i.key()]);
		ck.setFormula(prev_k_f_bn.value(ck.name_));
	}
	used.clear();
	PIMap<int, CDSection>::iterator j;
	for (j = s.begin(); j != s.end(); ++j) {
		if (v.s.contains(j.key()))
			j.value().update(v.s[j.key()], mode);
		used << j.key();
	}
	for (j = v.s.begin(); j != v.s.end(); ++j) {
		if (!used.contains(j.key()))
			s[j.key()] = j.value();
	}*/
//	piCout << "[CDSection] update end";
}


bool CDSection::isSameStructure(CDSection & v) {
	PIMap<PIString, int> k_ids;
	PIMap<int, CDType>::iterator i;
	for (i = k.begin(); i != k.end(); ++i)
		k_ids[i.value().name()] = i.key();
	for (i = v.k.begin(); i != v.k.end(); ++i) {
		if (!k_ids.contains(i.value().name())) continue;
		//piCout << i.key() << k[i.key()].name() << i.value().name();
		if (k[k_ids[i.value().name()]].index() != i.key())
			return false;
	}
	PIMap<int, CDSection>::iterator j;
	for (j = v.s.begin(); j != v.s.end(); ++j) {
		if (!s.contains(j.key())) continue;
		if (!s[j.key()].isSameStructure(j.value()))
			return false;
	}
	return true;
}


void CDSection::prepareCalculate() {
	PIMap<int, CDType>::iterator i;
	for (i = k.begin(); i != k.end(); ++i) {
		i.value().parent = this;
		i.value().calculated = false;
	}
	PIMap<int, CDSection>::iterator j;
	for (j = s.begin(); j != s.end(); ++j)
		j.value().prepareCalculate();
}


void CDSection::calculateRecursive(PIEvaluator * e) {
	PIMap<int, CDType>::iterator i;
	for (i = k.begin(); i != k.end(); ++i)
		i.value().calculate(e);
	PIMap<int, CDSection>::iterator j;
	for (j = s.begin(); j != s.end(); ++j)
		j.value().calculateRecursive(e);
}


void CDSection::makePath(PIDeque<int> p) {
	PIDeque<int> tp;
	PIMap<int, CDType>::iterator i;
	for (i = k.begin(); i != k.end(); ++i) {
		tp = p;
		tp << i.key();
		i.value().path_ = tp;
		//piCout << "path for" << i.value().name() << tp;
	}
	PIMap<int, CDSection>::iterator j;
	for (j = s.begin(); j != s.end(); ++j) {
		tp = p;
		tp << j.key();
		j.value().makePath(tp);
	}
}


PIVector<CDType * > CDSection::children(bool recursive) const {
	PIVector<CDType * > ret;
	PIMap<int, CDType>::const_iterator i;
	for (i = k.begin(); i != k.end(); ++i)
		ret << const_cast<CDType * >(&(i.value()));
	if (!recursive) return ret;
	PIMap<int, CDSection>::const_iterator j;
	for (j = s.begin(); j != s.end(); ++j)
		ret << j.value().children(true);
	return ret;
}


PIVariantTypes::Enum CDSection::enumValues() const {
	PIVariantTypes::Enum ret;
	PIMap<int, CDType>::const_iterator i;
	for (i = k.begin(); i != k.end(); ++i)
		ret << PIVariantTypes::Enumerator(i.key(), i.value().name());
	return ret;
}

