/*
    PIP - Platform Independent Primitives
	ValueTree translator
    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 "picli.h"
#include "pidir.h"
#include "pifile.h"
#include "pijson.h"
#include "pivaluetree.h"
#include "pivaluetree_conversions.h"
#include "ts_file.h"

using namespace PICoutManipulators;
using Attribute          = PIValueTree::Attribute;

const char help_string[] = "Read input files or entire directories as PIValueTree\n"
						   "according to extension, and create QtLinguist translation\n"
						   "file (*.ts). Extensions:\n"
						   " * conf - PIValueTreeConversions::fromText\n"
						   " * json - PIValueTreeConversions::fromJSON\n"
						   " * bin  - binary deserialization\n"
						   "Output file can be translated by QtLinguist and used\n"
						   "by QAD PIValueTreeEdit widget to translate strings.\n"
						   "";

void header() {
	piCout << Bold << "PIP ValueTree translator";
	piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
	piCout << Green << Bold << "Usage:" << Default
		   << "\"pip_vtt [-hHqn] -l <lang> -o <output_file> <file1/dir1> [<file2/dir2>] [<file3/dir3>] [...]\"" << 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 << "";
	piCout << Bold << "Output control";
	piCout << "-l <lang>         " << Green << "- translation language (e.g. en_US, ru_RU)";
	piCout << "-n, --no-obsolete " << Green << "- drop unused translations in output file";
	piCout << "-o <output_file>  " << Green << "- output file for translation (QtLinguist *.ts file)";
	piCout << "";
	piCout << Bold << "Input control";
	piCout << "<file/dir>        " << Green << "- add file or dir translation";
}

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


const PIString contextName = "QAD::PIValueTreeEdit";


void gatherStrings(TSFile::Context & context, const PIValueTree & vt, const PIString & loc) {
	const static PIStringList attrs({Attribute::prefix, Attribute::suffix, Attribute::toolTip});
	for (const auto & c: vt.children()) {
		context.confirm(c.name(), loc);
		context.confirm(c.comment(), loc);
		for (const auto & a: attrs) {
			if (c.attributes().contains(a)) {
				context.confirm(c.attributes().value(a).toString(), loc);
			}
		}
		if (!c.isArray()) gatherStrings(context, c, loc);
	}
}


int main(int argc, char * argv[]) {
	PICLI cli(argc, argv);
	// piCout << cli.rawArguments();
	cli.setOptionalArgumentsCount(-1);
	cli.addArgument("output", true);
	cli.addArgument("language", true);
	cli.addArgument("help");
	cli.addArgument("Help");
	cli.addArgument("quiet");
	cli.addArgument("no-obsolete");
	if (cli.hasArgument("Help")) {
		help();
		return 0;
	}
	if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.argumentValue("language").isEmpty() ||
		cli.optionalArguments().isEmpty()) {
		usage();
		return 0;
	}
	piDebug           = !cli.hasArgument("quiet");
	PIString out_path = cli.argumentValue("output");

	PIStringList files;
	const static PIStringList ext({"conf", "json", "bin"});
	for (const PIString & a: cli.optionalArguments()) {
		if (PIDir::isExists(a)) {
			auto dl = PIDir(a).allEntries();
			for (const auto & i: dl) {
				if (!i.isFile()) continue;
				if (ext.contains(i.extension().toLowerCase().trim())) files << i.path;
			}
		} else {
			if (PIFile::isExists(a)) files << a;
		}
	}

	piCout << Cyan << Bold << "Reading ts file ...";
	auto content = TSFile::read(out_path);
	if (content.lang.isEmpty()) content.lang = cli.argumentValue("language");
	content.markAllMissing();
	piCout << Cyan << Bold << "Reading done";

	piCout << Cyan << Bold << "Read" << files.size_s() << "files ...";
	auto & context(content.contexts[contextName]);
	PIDir out_dir(PIFile::FileInfo(out_path).dir());
	for (const auto & p: files) {
		PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim();
		PIFile f(p, PIIODevice::ReadOnly);
		if (ext == "conf") gatherStrings(context, PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())), out_dir.relative(p));
		if (ext == "json")
			gatherStrings(context,
						  PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))),
						  out_dir.relative(p));
		if (ext == "bin") gatherStrings(context, piDeserialize<PIValueTree>(f.readAll()), PIString());
	}
	piCout << Cyan << Bold << "Reading done";

	piCout << Cyan << Bold << "Writing ts file ...";
	if (!TSFile::write(out_path, content, cli.hasArgument("no-obsolete"))) return 1;
	piCout << Cyan << Bold << "Writing done";

	return 0;
}
