/*
Copyright (C) 2000-2022  The Exult Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif


#include <cstdio>
#include "Configuration.h"
#include <iostream>
#include <cstdlib>
#include <string>
#include "exult_constants.h"
#include <vector>
#include <cassert>

using std::cout;
using std::cerr;
using std::endl;
using std::vector;
using std::string;
using std::pair;

enum DoOps { DoAdd, DoRem, DoGet };

using DoListPair = pair<DoOps, vector<string>>;

using DoList = vector<DoListPair>;

DoList          dolist;
Configuration   *config = new Configuration();
string          config_file_name;
bool            verbose = false; // dump verbose output to cerr

/* #include <cstd_usage_function> */
void usage(unsigned int i) {
	cout << "cmanip - simple commandline, conf/ file manipulator" << endl
#ifdef HAVE_CONFIG_H
	     << "    compiled with " << PACKAGE << " " << VERSION << endl
#endif
	     << endl
	     << "usage:" << endl
	     << "\tcmanip <conffile> [option [parameters]] ..." << endl
	     << endl
	     << "options:" << endl
	     << "\t[-a | add | create | mk | new | modify] <key> <value>" << endl
	     << "\t\t\t- adds the <value> to the <key> in the <conffile>" << endl
	     << "\t[-r | rem | remove | rm | del] <key>" << endl
	     << "\t\t\t- removes the <key> from the <conffile>" << endl
	     << "\t[-x | get | extract | rd | see | read] <key>" << endl
	     << "\t\t\t- extracts the value set for <key> from the <conffile> or \"unknown\"" << endl
	     << "\t[-v | verbose]\t- print verbose output to stderr" << endl
	     << endl
	     << "examples:" << endl
	     << "\tcmanip exult.cfg add config/foo stuff rem config/bar" << endl
	     << "\tcmanip exult.cfg new config/foo \"more stuff\" add config/bar useless" << endl
	     << "\tcmanip exult.cfg -v -r config/foo del config/bar" << endl
	     ;
	exit(i);
}

/* Turn the various command line parameters into operations that can be handled by process_ops */
void read_params(const int argc, char *argv[]) {
	config_file_name = argv[1];

	// "I'd appreciate your input"...
	for (int i = 2; i < argc; i++) {
		string s(argv[i]);

		/* Adds the value (argv[i+2]) to the key (argv[i+1]) in the conf file. */
		if ((s == "add") || (s == "create") || (s == "mk") || (s == "new") || (s == "-a") || (s == "modify")) {
			if (i + 2 >= argc) {
				cout << "error: insufficient parameters supplied for '" << s << "'" << endl;
				usage(1);
			}

			DoListPair dlp;
			dlp.first = DoAdd;
			dlp.second.emplace_back(argv[i + 1]);
			dlp.second.emplace_back(argv[i + 2]);
			dolist.push_back(dlp);
			i += 2;
		}
		/* Removes the key (argv[i+1]) from the conf file.
		    FIXME: Currently only sets it to 'null', since there is no 'remove' ability
		    in Configuration. */
		else if ((s == "rem") || (s == "remove") || (s == "rm") || (s == "mk") || (s == "del") || (s == "-r")) {
			if (i + 1 >= argc) {
				cout << "error: insufficient parameters supplied for '" << s << "'" << endl;
				usage(1);
			}

			DoListPair dlp;
			dlp.first = DoRem;
			dlp.second.emplace_back(argv[i + 1]);
			dolist.push_back(dlp);
			i++;
		}
		/* Added by Artaxerxes. Extracts the values for a given path. Do not change anything. Just read.*/
		else if ((s == "extract") || (s == "read") || (s == "rd") || (s == "see") || (s == "get") || (s == "-x")) {
			if (i + 1 >= argc) {
				cout << "error: insufficient parameters supplied for '" << s << "'" << endl;
				usage(1);
			}

			DoListPair dlp;
			dlp.first = DoGet;
			dlp.second.emplace_back(argv[i + 1]);
			dolist.push_back(dlp);
			i++;
		}
		/* Just turns on 'verbose' mode. Nothing of particular intrest. */
		else if ((s == "-v") || (s == "verbose"))
			verbose = true;
		else {
			cout << "error: unknown parameter \"" << s << "\"" << endl;
		}
	}
}

/* Walk over the operations list (dolist) and execute each operation in the order
    it was listed on the command line */
void process_ops() {
	for (auto i = dolist.begin(); i != dolist.end(); i++) {
		if (i->first == DoAdd) {
			assert(i->second.size() == 2);
			if (verbose) {
				string s;
				assert(config != nullptr);
				config->value(i->second[0].c_str(), s, "---nil---");
				cerr << "Original value of " << i->second[0] << " was " << s << endl;

			}

			assert(config != nullptr);
			config->set(i->second[0].c_str(), i->second[1].c_str(), false);

			if (verbose)
				cerr << "Added " << i->second[1] << " to " << i->second[0] << endl;
		}
		if (i->first == DoRem) {
			assert(i->second.size() == 1);
			if (verbose) {
				string s;
				assert(config != nullptr);
				config->value(i->second[0].c_str(), s, "---nil---");
				cerr << "Original value was " << i->second[0] << " was " << s << endl;

			}

			assert(config != nullptr);
			config->set(i->second[0].c_str(), "", false);

			if (verbose)
				cerr << "Removed " << i->second[0] << endl;
		}
		if (i->first == DoGet) {
			assert(i->second.size() == 1);
			if (verbose) {
				string s;
				assert(config != nullptr);
				config->value(i->second[0].c_str(), s, "unknown");
				cerr << "Return value for " << i->second[0] << " is " << s << endl;

			}

			assert(config != nullptr);
			//config->set(i->second[0].c_str(), "", false);
			string s;
			config->value(i->second[0].c_str(), s, "unknown");
			cout << s << endl;

		}
	}
}

int main(int argc, char *argv[]) {
	if (argc < 3)
		usage(0);

	read_params(argc, argv);

	if (verbose) {
		cerr << "Operations:" << endl;
		for (auto& i : dolist) {
			cerr << '\t' << ((i.first == DoAdd) ? "add" : ((i.first == DoRem) ? "rem" : ((i.first == DoGet) ? "get" : "unknown"))) << '\t';
			for (auto& j : i.second)
				cerr << j << '\t';
			cerr << endl;
		}
	}

	assert(config != nullptr);
	config->read_config_file(config_file_name);

	process_ops();

	assert(config != nullptr);
	config->write_back();

	return 0;
}
