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

using namespace PICoutManipulators;

const char help_string[] = "-M (Metainfo)\n"
						   "Generate classes and structs info. It contains\n"
						   "names, subclass info, all methods and variables.\n"
						   "Place information:\n"
						   " * classes and structs - PICODEINFO::classes()\n"
						   " * methods - ClassInfo::functions\n"
						   " * variables - ClassInfo::variables\n"
						   "\n"
						   "-E (Enums)\n"
						   "Generate enumeration descriptions.\n"
						   "Useful for GUI integrations, because\n"
						   "you can obtain enumerators value and name.\n"
						   " * enums - PICODEINFO::enums()\n"
						   "\n"
						   "-S (Stream operators)\n"
						   "Generate store/restore operators with format\n"
						   "BINARY_STREAM_WRITE(<type>);\n"
						   "BINARY_STREAM_READ (<type>);\n"
						   "Only public variables used. All variables stored/restored\n"
						   "using PIChunkStream. IDs are variable number, starting from 1.\n"
						   "You can override ID with PIMETA(id=<ID>). If in class or struct\n"
						   "PIMETA(simple-stream) presence, then variables stored/restored\n"
						   "with simple << and >> operators.\n"
						   "If PIMETA(no-stream) presence, then class or struct ignored.\n"
						   "\n"
						   "-J (JSON serialization)\n"
						   "Generate serialize/deserialize methods for classes and structures.\n"
						   "These methods uses by PIJSON::serialize(T v) and PIJSON::deserialize(json)\n"
						   "allow automatic conversion to/from PIJSON. Use member name as key.\n"
						   "Member can be skipped by providing PIMETA(id=-).\n"
						   "If PIMETA(no-json) presence, then class or struct ignored.\n"
						   "\n"
						   "-G (Getter functions)\n"
						   "Generate anonymous access methods for member typenames, values and offsets.\n"
						   "Every class or struct member typename can be obtained with:\n"
						   "const char * getMemberType(const char * class_name, const char * member_name)\n"
						   "Member value can be obtained with:\n"
						   "PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name)\n"
						   "where \"p\" - class or struct pointer, and returns serialized value.\n"
						   "PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name)\n"
						   "where \"p\" - class or struct pointer, and returns value as registered PIVariant.\n"
						   "If PIMETA(no-getter) presence, then class or struct ignored.\n"
						   "";


void header() {
	piCout << Bold << "PIP Code model generator";
	piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
	piCout << Green << Bold << "Usage:" << Default
		   << "\"pip_cmg [-hHqPpsAMESTGJ] -o <output_file> [-I<include_dir1>] [-I<include_dir1>] [...] [-D<define1>] [-D<define1>] [...] "
			  "<file1> [<file2>] [<file3>] [...]\""
		   << NewLine;
}


void usage() {
	header();
	piCout << Green << Bold << "Details:";
	piCout << Bold << "Debug control";
	piCout << "-h               " << Green << "- display this message and exit";
	piCout << "-H               " << Green << "- display details help";
	piCout << "-q               " << Green << "- quiet, no debug output to console";
	piCout << "-P               " << Green << "- print list of all parsed files to console before exit";
	piCout << "-p               " << Green << "- print list of all parsed files without file with \"main\" function to console before exit";
	piCout << "";
	piCout << Bold << "Parsing control";
	piCout << "-s               " << Green << "- single file (don`t follow includes)";
	piCout << "-I<include_dir>  " << Green << "- add include dir (e.g. -I.. -I../some_dir -I/usr/include)";
	piCout << "-D<define>       " << Green
		   << "- add define to preprocessor, macro PICODE is always defined (e.g. -DMY_DEFINE will add MY_DEFINE define)";
	piCout << "";
	piCout << Bold << "Output control";
	piCout << "-A               " << Green << "- write all";
	piCout << "-M               " << Green << "- write metainfo";
	piCout << "-E               " << Green << "- write enums";
	piCout << "-S               " << Green << "- write stream operators";
	piCout << "-G               " << Green << "- write getter functions";
	piCout << "-J               " << Green << "- write JSON functions";
	// piCout << "-T               " << Green << "- write text serialize functions";
	piCout << "-o <output_file> " << Green
		   << "- output file for code model without extension (e.g. \"ccm\" - files \"ccm.h\" and \"ccm.cpp\" will be created)";
	piCout << "";
	piCout << Bold << "Input control";
	piCout << "<file>           " << Green << "- add file to code model, all includes of this file will be proceed (e.g. \"main.cpp\")";
}


void help() {
	header();
	piCout << help_string;
}


PIString toCName(const PIString & s) {
	PIString ret(s.trimmed());
	if (ret.isEmpty()) return ret;
	ret.replaceAll('<', '_').replaceAll('>', '_');
	for (int i = 0; i < ret.size_s(); ++i) {
		if (i == 0) {
			if (!(ret[i].isAlpha() || ret[i].toAscii() == '_')) {
				ret.pop_front();
				--i;
			}
		} else {
			if (!(ret[i].isAlpha() || ret[i].isDigit() || ret[i].toAscii() == '_')) {
				ret.remove(i);
				--i;
			}
		}
	}
	ret.replaceAll("__", "_");
	return ret;
}


PICodeParser::Entity * findEntity(Runtime & rt, const PIString & type) {
	return rt.parser.findEntityByName(type);
}
