/*
    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 "getter.h"

#include "stream.h"


void writeGetterTypeMembers(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;
		writeGetterTypeMembers(rt, p, var_prefix);
	}
	PISet<int> used_id;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
		auto type = findEntity(rt, m.type);
		if (type) {
			if (type->is_anonymous) {
				writeGetterTypeMembers(rt, type, var_prefix + m.name);
				continue;
			}
		}
		rt.ts << "\tif (strcmp(name, \"" << var_prefix << m.name << "\") == 0) return \"" << m.type;
		// if (m.isBitfield()) rt.ts << ":" << m.bits;
		rt.ts << "\";\n";
	}
}


void writeGetterValueMembers(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;
		writeGetterValueMembers(rt, p, var_prefix);
	}
	PISet<int> used_id;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue;
		auto type = findEntity(rt, m.type);
		if (type) {
			if (type->is_anonymous) {
				writeGetterValueMembers(rt, type, var_prefix + m.name);
				continue;
			}
		}
		rt.ts << "\tif (strcmp(name, \"" << var_prefix << m.name << "\") == 0) {";
		if (m.isBitfield()) {
			rt.ts << "ret = piSerialize(static_cast<" << m.type << ">(o->" << var_prefix << m.name << "));";
		} else
			rt.ts << "serialize(ret, o->" << var_prefix << m.name << ");";
		rt.ts << " return ret;}\n";
	}
}


void writeGetterOffsetMembers(Runtime & rt, const PICodeParser::Entity * e, PIString entity_name, PIString var_prefix) {
	if (var_prefix.isNotEmpty() && !var_prefix.endsWith('.')) var_prefix += ".";
	for (const PICodeParser::Entity * p: e->parents) {
		if (p->is_anonymous) continue;
		writeGetterOffsetMembers(rt, p, entity_name, var_prefix);
	}
	PISet<int> used_id;
	for (const PICodeParser::Member & m: e->members) {
		if (m.is_type_ptr || !m.dims.isEmpty() || m.isBitfield() || (m.visibility != PICodeParser::Public)) continue;
		auto type = findEntity(rt, m.type);
		if (type) {
			if (type->is_anonymous) {
				writeGetterOffsetMembers(rt, type, entity_name, var_prefix + m.name);
				continue;
			}
		}
		rt.ts << "\tif (strcmp(name, \"" << var_prefix << m.name << "\") == 0) ";
		rt.ts << "return PICODEINFO_OFFSET(" << entity_name << ", " << var_prefix << m.name << ");\n";
	}
}


void makeGetterType(Runtime & rt, const PICodeParser::Entity * e) {
	if (!needClassGetter(e)) return;
	rt.ts << "\nconst char * getterType" << toCName(e->name) << "(const char * name) {\n";
	rt.ts << "\tif (!name) return \"\";\n";
	writeGetterTypeMembers(rt, e);
	rt.ts << "\treturn \"\";\n}\n";
}


void makeGetterValue(Runtime & rt, const PICodeParser::Entity * e) {
	if (!needClassGetter(e)) return;
	rt.ts << "\nPIByteArray getterValue" << toCName(e->name) << "(const void * p, const char * name) {\n";
	rt.ts << "\tPIByteArray ret;\n";
	rt.ts << "\tif (!p || !name) return ret;\n";
	rt.ts << "\t" << e->name << " * o = (" << e->name << "*)p;\n";
	writeGetterValueMembers(rt, e);
	rt.ts << "\treturn ret;\n}\n";
}


void makeGetterOffset(Runtime & rt, const PICodeParser::Entity * e) {
	if (!needClassGetter(e)) return;
	rt.ts << "\nint getterOffset" << toCName(e->name) << "(const char * name) {\n";
	rt.ts << "\tif (!name) return 0;\n";
	writeGetterOffsetMembers(rt, e, e->name);
	rt.ts << "\treturn 0;\n}\n";
}


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