/*
    PIP - Platform Independent Primitives
    Code model generator
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "json.h"

#include "pitranslator.h"


bool writeClassJSONMembersOut(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) {
	if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += ".";
	for (const PICodeParser::Entity * p: e->parents) {
		if (p->is_anonymous) continue;
		writeClassJSONMembersOut(rt, p, var_prefix);
	}
	PIVector<PICodeParser::Member> ml;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
		ml << m;
	}
	bool is_union = e->type == "union";
	for (const PICodeParser::Member & m: ml) {
		if (m.isBitfield()) continue;
		if (m.attributes[PICodeParser::Static]) continue;
		if (m.meta.value("id") == "-") continue;
		// if (m.meta.contains("id")) cnt = m.meta.value("id").toInt();
		auto type = findEntity(rt, m.type);
		if (type) {
			if (type->is_anonymous) {
				writeClassJSONMembersOut(rt, type, var_prefix + m.name);
				continue;
			}
		}
		if (m.dims.isEmpty()) {
			rt.ts << "\tret[\"" << var_prefix << m.name << "\"] = piSerializeJSON(v." << var_prefix << m.name << ");\n";
		} else {
			PIString ptype = m.type.left(m.type.find('[')).trim();
			PIString size  = m.dims[0];
			for (int i = 1; i < m.dims.size_s(); ++i) {
				size += " * ";
				size += m.dims[i];
			}
			rt.ts << "\tret[\"" << var_prefix << m.name << "\"] = piSerializeJSON(PIVector<" << ptype << " >((const " << ptype << " *)(v."
				  << var_prefix << m.name << "), " << size << "));\n";
		}
		if (is_union) break;
	}
	if (is_union) return true;
	return true;
}


bool writeClassJSONMembersIn(Runtime & rt, const PICodeParser::Entity * e, PIString var_prefix) {
	if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += ".";
	for (const PICodeParser::Entity * p: e->parents) {
		if (p->is_anonymous) continue;
		writeClassJSONMembersIn(rt, p, var_prefix);
	}
	PIVector<PICodeParser::Member> ml;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue;
		ml << m;
	}
	bool is_union = e->type == "union";
	PISet<int> used_id;
	for (const PICodeParser::Member & m: ml) {
		if (m.isBitfield()) continue;
		if (m.attributes[PICodeParser::Static]) continue;
		if (m.meta.value("id") == "-") continue;
		auto type = findEntity(rt, m.type);
		if (type) {
			if (type->is_anonymous) {
				writeClassJSONMembersIn(rt, type, var_prefix + m.name);
				continue;
			}
		}
		// if (m.meta.contains("id")) cnt = m.meta.value("id").toInt();
		if (m.dims.isEmpty()) {
			rt.ts << "\tpiDeserializeJSON(v." << var_prefix << m.name << ", js[\"" << var_prefix << m.name << "\"]);\n";
		} else {
			PIString ptype = m.type.left(m.type.find('[')).trim();
			PIString size  = m.dims[0];
			for (int i = 1; i < m.dims.size_s(); ++i) {
				size += " * ";
				size += m.dims[i];
			}
			rt.ts << "\t{\n\t\tPIVector<" << ptype << " > d;\n";
			rt.ts << "\t\tpiDeserializeJSON(d, js[\"" << var_prefix << m.name << "\"]);\n";
			rt.ts << "\t\tint cnt = piMini(d.size_s(), " << size << ");\n";
			rt.ts << "\t\tfor (int i = 0; i < cnt; ++i)\n";
			rt.ts << "\t\t\t((" << ptype << " *)(v." << var_prefix << m.name << "))[i] = d[i];\n";
			rt.ts << "\t}\n";
		}
		if (is_union) break;
	}
	if (is_union) return true;
	return true;
}


bool needClassJSON(const PICodeParser::Entity * e) {
	if (e->meta.contains("no-json")) return false;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
		if (m.attributes[PICodeParser::Static]) continue;
		if (m.meta.value("id") == "-") continue;
		return true;
	}
	for (const PICodeParser::Entity * p: e->parents) {
		if (p->is_anonymous) continue;
		if (needClassJSON(p)) return true;
	}
	return false;
}


bool makeClassJSON(Runtime & rt, const PICodeParser::Entity * e) {
	if (!needClassJSON(e)) return true;

	rt.ts << "\ntemplate<> inline PIJSON piSerializeJSON(const " << e->name << " & v) {\n";
	rt.ts << "\tPIJSON ret;\n";
	if (!writeClassJSONMembersOut(rt, e)) return false;
	rt.ts << "\treturn ret;\n}\n";

	rt.ts << "\ntemplate<> inline void piDeserializeJSON(" << e->name << " & v, const PIJSON & js) {\n";
	if (!writeClassJSONMembersIn(rt, e)) return false;
	rt.ts << "}\n";

	return true;
}
