#include "cdutils_parser.h"
#include "cdutils_types.h"
#include "piiostring.h"
#include "piiobytearray.h"
#include "pifile.h"

using namespace CDUtils;

enum Phase {
	eName = 1,
	eBracketOpen,
	eBracketClose,
	eMemberName,
	eMemberEqual,
	eMemberValue,
	eMemberComma,
	eComment,
	eMultiComment
};


void removeComment(PIString & line, PIString * type, PIString * comment) {
	int ci = line.find("//");
	if (ci >= 0) {
		if (comment) *comment = line.right(line.size_s() - ci - 2);
		line.cutRight(line.size_s() - ci).trim();
		if (type && comment && !line.isEmpty()) {
			*type = comment->takeLeft(1);
			comment->trim();
		}
	}
}


void parseEnumLine(PIString & line, int * value, PIString * type, PIString * comment) {
	removeComment(line, type, comment);
	int ci = line.find("=");
	if (ci >= 0) {
		if (value) *value = line.right(line.size_s() - ci - 1).trim().toInt();
		line.cutRight(line.size_s() - ci).trim();
	}
	if (line.trim().endsWith(",")) line.cutRight(1);
}


void parseInsert(PIString line, PIString & alias, PIStringList & out) {
	out.clear();
	int ci = line.find("=");
	if (ci < 0) return;
	alias = line.right(line.size_s() - ci - 1).trim();
	line.cutRight(line.size_s() - ci).trim();
	while (line.find("[") > 0) {
		int is = line.find("["), ie = line.find("]");
		PIString arr = line.mid(is + 1, ie - is - 1);
		out << arr;
		line.cutMid(is, ie - is + 1);
	}
	if (!line.isEmpty()) out.insert(0, line);
}


PIVector<int> enumValues(const PIString & e, const PIMap<PIString, CDSection> & sections, PIStringList & enames) {
	PIVector<int> ret;
	enames.clear();
	if (sections.contains(e)) {
		ret = sections[e].indexes();
		enames = sections[e].index_names();
	} else {
		int v = e.toInt();
		if (v < 2) return ret;
		for (int i = 0; i < v; ++i) {
			ret << i;
			enames << "";//PIString::fromNumber(i);
		}
	}
	return ret;
}


CDSection CDParser::parse(PIIODevice * d, int cdsection_type) {
	CDType::cdT et = (CDType::cdT)cdsection_type;
	if (!d) return CDSection(et);
	if (!d->canRead()) return CDSection(et);
	//piCout << "[CDSection] parse start";
	CDSection cs(et);
	CDType ck;
	PIMap<PIString, CDSection> sections;
	PIMap<PIString, int> enum_values;
	PIString content, line, alias, type, comment;
	PIStringList iarr;
	if (PIStringAscii(d->className()) == PIStringAscii("PIFile")) {
		PIByteArray c = ((PIFile*)d)->readAll();
		c << uchar(0);
		content = PIString::fromUTF8((const char *)c.data());
	}
	if (PIStringAscii(d->className()) == PIStringAscii("PIIOString")) content = *(((PIIOString*)d)->string());
	if (PIStringAscii(d->className()) == PIStringAscii("PIIOByteArray")) content = PIString(*(((PIIOByteArray*)d)->byteArray()));
	PIIOString ios(&content);
	//int phase = 0;
	int cind = -1;
	while ((cind = content.find("enum", cind)) >= 0) {
		ios.seek(cind);
		line = ios.readLine().trim();
		type.clear();
		comment.clear();
		removeComment(line, &type, &comment);
		if (line.find("{") < 0) {
			cind += 4;
			continue;
		}
		line.cutLeft(line.find("enum") + 4).trim();
		line.cutRight(line.size_s() - line.find("{")).trim();
		if (line.isEmpty()) {
			cind += 4;
			continue;
		}
		cs = CDSection(et);
		cs.name = line;
		//piCout << "enum" << cs.name;
		int cev = 0;
//		cevalues.clear();
		while (!ios.isEnd()) {
			line = ios.readLine().trim();
			comment.clear();
			removeComment(line, &type, &comment);
			if (line.find("}") >= 0) break;
			if (line.isEmpty()) {
				if (comment.find("=") >= 0) {
					parseInsert(comment, alias, iarr);
					if (!iarr.isEmpty()) {
//						piCout << "#" << enum_values;
						if (!enum_values.contains(alias)) {
							piCout << "Parse error: can`t find section alias \"" << alias << "\"!";
							return CDSection(et);
						}
						if (!sections.contains(iarr.front())) {
							piCout << "Parse error: can`t find section \"" << iarr.front() << "\"!";
							return CDSection(et);
						}
						//piCout << "insert" << alias << iarr;
						int aval = enum_values.value(alias);
						CDSection is = sections.value(iarr.take_front()), ts;
						int ibpos = is.name.size_s();
						piForeachRC (PIString & a, iarr) {
							PIStringList enames;
							PIVector<int> evals = enumValues(a, sections, enames);
							//piCout << a << evals;
							for (int i = 0; i < evals.size_s(); ++i) {
								ts.section(evals[i]) = is;
								ts.section(evals[i]).alias = enames[i];
							}
							ts.name = is.name;
							ts.name.insert(ibpos, PIString("[") << a << "]");
							is = ts;
							ts = CDSection(et);
						}
						is.alias = alias;
						cs.section(aval) = is;
					}
				}
			} else {
				parseEnumLine(line, &cev, &type, &comment);
				//piCout << line << "=" << cev << "//" << type << comment;
				ck = CDType(cev, line, type, "", "", comment, et);
				if (type == "e") {
					if (comment.startsWith("${")) {
						comment.cutLeft(1);
						PIString en = comment.inBrackets('{', '}');
						comment.cutLeft(en.size_s() + 2).trim();
						ck.setEnumValues(sections.value(en).enumValues());
						ck.setComment(comment);
						//piCout << "enum" << en << ck.enumValues();
					}
				}
				cs[cev] = ck;
				//cevalues[line] = cev;
				enum_values[line] = cev;
				++cev;
			}
		}
		//piCout << cs.name << cs.k;
		sections[cs.name] = cs;
//		piCout << "#" << cevalues;
//		enum_values << cevalues;
		cind += 4;
	}
//	piCout << "[CDSection] parse end";
	switch (et) {
	case CDType::cdK: return sections.value("KDescription");
	case CDType::cdX: return sections.value("XDescription");
	case CDType::cdC: return sections.value("CDescription");
	case CDType::cdM: return sections.value("MDescription");
	default: return CDSection(et);
	}
	return CDSection(et);
}
