/*
    PIP - Platform Independent Primitives
    Crypth / decrypth files utility
    Andrey Bychkov work.a.b@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 "picrypt.h"
#include "pifile.h"

#include <stdio.h>

using namespace PICoutManipulators;


void usage() {
	piCout << Bold << "PIP Crypt Utility";
	piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
	piCout << Green << Bold << "Usage:" << Default
		   << "\"picrypt [-thr] [-g <pass>] [-o <out_file>] [{[-c <file>] [-d <file>]} {[-p <pass>] [-s <hash>] [-k <key_file>] [-x "
			  "<hex_key>]}]\""
		   << NewLine;
	piCout << Green << Bold << "Details:";
	piCout << "-h --help                " << Green << "- display this message and exit";
	piCout << "-g --genhash             " << Green << "- generate hash from <pass> string";
	piCout << "-t --text                " << Green << "- output in text base64";
	piCout << "-o --out <out_file>      " << Green << "- write out to file <out_file>";
	piCout << "-c --crypt <file>        " << Green << "- crypt file <file> using secret <pass> or <hash> or <key_file>";
	piCout << "-d --decrypt <file>      " << Green << "- decrypt file <file> using secret <pass> or <hash> or <key_file>";
	piCout << "-p --pass <pass>         " << Green << "- use secret from passphrase <pass>";
	piCout << "-s --secret <hash>       " << Green << "- use secret from hash <hash>";
	piCout << "-k --key <key_file>      " << Green << "- use secret from binary key_file <key_file>";
	piCout << "-x --hex [<hex_key>]     " << Green << "- use secret from hex hash <hex_key> or output in text hex";
	piCout << "-r --random              " << Green << "- generate random secret key";
}


int main(int argc, char * argv[]) {
	PICrypt::hash("");
	PICrypt crypt;
	PIByteArray bout;
	PICLI cli(argc, argv);
	cli.addArgument("genhash", true);
	cli.addArgument("out", true);
	cli.addArgument("crypt", true);
	cli.addArgument("decrypt", true);
	cli.addArgument("pass", true);
	cli.addArgument("secret", true);
	cli.addArgument("key", true);
	cli.addArgument("hex", 'x', "hex", true);
	cli.addArgument("help");
	cli.addArgument("text");
	cli.addArgument("random");

	if (!(cli.hasArgument("genhash") || cli.hasArgument("crypt") || cli.hasArgument("decrypt") || cli.hasArgument("random")) ||
	    cli.hasArgument("help")) {
		usage();
		return 0;
	}

	PIString out_file = cli.argumentValue("out");
	PIFile outf;
	if (!out_file.isEmpty()) {
		if (outf.open(out_file, PIIODevice::ReadWrite)) {
			outf.resize(0);
		} else
			piCout << "error: while open out file";
	}

	if (cli.hasArgument("genhash")) {
		PIString s = cli.argumentValue("genhash");
		bout       = crypt.hash(s);
	} else if (cli.hasArgument("random"))
		bout = crypt.generateKey();

	if (cli.hasArgument("crypt") || cli.hasArgument("decrypt")) {
		PIByteArray secret;
		if (cli.hasArgument("key")) {
			PIFile keyf(cli.argumentValue("key"), PIIODevice::ReadOnly);
			if (keyf.open())
				secret = keyf.readAll();
			else
				piCout << "error: while open key file";
		}
		if (cli.hasArgument("pass")) secret = crypt.hash(cli.argumentValue("pass"));
		if (cli.hasArgument("secret")) secret = PIByteArray::fromBase64(cli.argumentValue("secret").toByteArray());
		if (cli.hasArgument("hex")) secret = PIByteArray::fromHex(PIString::fromAscii(cli.argumentValue("hex").toByteArray()));
		if (secret.size() == crypt.sizeKey()) {
			PIFile inf;
			PIString in_file;
			PIByteArray bin;
			crypt.setKey(secret);
			if (cli.hasArgument("crypt")) in_file = cli.argumentValue("crypt");
			if (cli.hasArgument("decrypt")) in_file = cli.argumentValue("decrypt");
			if (inf.open(in_file, PIIODevice::ReadOnly)) {
				bin = inf.readAll();
				if (cli.hasArgument("crypt")) bout = crypt.crypt(bin);
				if (cli.hasArgument("decrypt")) bout = crypt.decrypt(bin);
			} else
				piCout << "error: while open input file";
		} else
			piCout << "error: invalid secret";
	}

	if (!bout.isEmpty()) {
		if (cli.hasArgument("text"))
			bout.convertToBase64();
		else if (cli.hasArgument("hex") && cli.argumentValue("hex").isEmpty())
			bout = bout.toHex().toByteArray();
		if (outf.isOpened()) {
			outf.write(bout);
			outf.close();
		} else
			fwrite(bout.data(), 1, bout.size(), stdout);
	}
	return 0;
}
