/*
    PIP - Platform Independent Primitives
	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 "parser.h"
#include "picli.h"
#include "pidir.h"
#include "pifile.h"
#include "piliterals_string.h"
#include "pitranslator.h"
#include "pitranslator_p.h"

using namespace PICoutManipulators;

const char help_string[] = "Read input files or entire directories as C++ sources\n"
						   "and create QtLinguist translation file (*.ts).\n"
						   "Search for next strings:\n"
						   " * PITranslator::tr(\"msg\"[, \"ctx\"])\n"
						   " * piTr(\"msg\"[, \"ctx\"])\n"
						   " * \"msg\"_tr\n"
						   "Output file can be translated by QtLinguist and used\n"
						   "by PIP ?????? to translate strings.\n"
						   "";

void header() {
	piCout << Bold << "PIP Translator";
	piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
	piCout << Green << Bold << "Usage:" << Default
		   << "\"pip_tr -P [-hHqnr] -l <lang> -o <output_file> <file1/dir1> [<file2/dir2>] [<file3/dir3>] [...]\"" << NewLine
		   << "\"pip_tr -C [-hHq] -o <output_file> <ts_file>\"" << NewLine;
}

void usage() {
	header();
	piCout << Green << Bold << "Details:";
	piCout << Bold << "Mode";
	piCout << "-P --Parse        " << Green << "- parse sources and create/update *.ts file";
	piCout << "-C --Compile      " << Green << "- compile *.ts file";
	piCout << "";
	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 for parse mode  or  binary file for compile mode)";
	piCout << "";
	piCout << Bold << "Input control";
	piCout << "-r, --recursive   " << Green << "- scan directories recursively";
	piCout << "<file/dir>        " << Green << "- add source file/dir for parse mode  or  *.ts file for compile mode";
}

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

enum Mode {
	mParse   = 1,
	mCompile = 2,
};

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");
	cli.addArgument("recursive");
	cli.addArgument("Parse");
	cli.addArgument("Compile");
	if (cli.hasArgument("Help")) {
		help();
		return 0;
	}
	int mode = 0;
	if (cli.hasArgument("Parse")) mode |= mParse;
	if (cli.hasArgument("Compile")) mode |= mCompile;
	if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty() ||
		(mode != mParse && mode != mCompile)) {
		usage();
		return 0;
	}
	if (mode == mParse && cli.argumentValue("language").isEmpty()) {
		usage();
		return 0;
	}
	piDebug           = !cli.hasArgument("quiet");
	PIString out_path = cli.argumentValue("output");

	if (mode == mParse) {
		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";

		bool recursive = cli.hasArgument("recursive");

		PIStringList files;
		const static PIStringList ext({"h", "hpp", "cpp", "cxx"});
		for (const PIString & a: cli.optionalArguments()) {
			if (PIDir::isExists(a)) {
				auto dl = recursive ? PIDir(a).allEntries() : PIDir(a).entries();
				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 << "Read" << files.size_s() << "files ...";
		PITimeMeasurer tm;
		PIDir out_dir(PIFile::FileInfo(out_path).dir());
		int count_all = 0, count_new = 0;
		for (const auto & p: files) {
			PIString file_loc = out_dir.relative(p);
			PIFile f(p, PIIODevice::ReadOnly);
			gatherStrings(content, PIString::fromUTF8(f.readAll()), file_loc, count_all, count_new);
		}
		auto elapsed = tm.elapsed();
		piCout << Cyan << Bold
			   << "Reading done, found %1 strings (%2 new) in %3 ms"_a.arg(count_all).arg(count_new).arg((int)elapsed.toMilliseconds());

		piCout << Cyan << Bold << "Writing ts file ...";
		if (!TSFile::write(out_path, content, cli.hasArgument("no-obsolete"))) return 1;
		piCout << Cyan << Bold << "Writing done";
	} else if (mode == mCompile) {
		piCout << Cyan << Bold << "Reading ts file ...";
		auto content = TSFile::read(cli.optionalArguments().front());
		if (content.lang.isEmpty()) content.lang = cli.argumentValue("language");
		piCout << Cyan << Bold << "Reading done";

		int wcount = 0;
		PITranslatorPrivate::Translation t;
		t.lang   = content.lang;
		auto cit = content.contexts.makeIterator();
		while (cit.next()) {
			auto * c = t.createContext(cit.key());
			auto mit = cit.value().messages.makeIterator();
			while (mit.next()) {
				const auto & m(mit.value());
				if (m.type[TSFile::Message::Unfinished] || m.type[TSFile::Message::Missing]) continue;
				c->add(m.source, m.translation);
				++wcount;
				// piCout << "tr" << m.source << "->" << m.translation;
			}
		}
		PIFile outf(out_path, PIIODevice::WriteOnly);
		if (!outf.isOpened()) {
			piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path());
			return 1;
		}
		outf.clear();
		piCout << Cyan << Bold << "Writing bin file ...";
		PITranslatorPrivate::writeHeader(&outf);
		outf.write(t.save());
		piCout << Cyan << Bold << "Writing done (%1 strings)"_a.arg(wcount);
	}

	return 0;
}
