/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <sys/types.h>

#ifndef WIN32
#include <sys/wait.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <config.h>
#include <stdexcept>

#include <mia/core/msgstream.hh>
#include <mia/core/history.hh>
#include <mia/core/cmdlineparser.hh>
#include <mia/core/iodata.hh>

using namespace std;
NS_MIA_USE

static const char *revision = "$Revision: 937 $:";


const SProgramDescription g_general_help = {
	{pdi_group,"Test"}, 
	{pdi_short,"Short help"}, 
	{pdi_description,"This program tests the history entries."}, 
	{pdi_example_descr, "Example text"}, 
	{pdi_example_code, "Example"}
}; 




typedef TDictMap<int> CDict;
const CDict::Table dict[] = {
	{"testa", 1, "test A"},
	{"testb", 2, "test B"},
	{"testc", 3, "test C"},
	{NULL, 0, ""}
};

CDict the_dict(dict);

namespace options {

	// required
	string some_single_string;
	float  some_float = -1.0f;
	// optional
	int some_int = -1;
	bool   true_bool = false;
	bool internal = false;
	int dict_val = 1;
#ifdef OPT_CAN_VECTOR
	vector<string> series_of_strings;
	vector<string> other_series_of_strings;
	vector<int> series_of_int;
	vector<float> series_of_float;
#endif
}

const char *float_str = "2.1";
const char *int_str = "1231";

#ifdef OPT_CAN_VECTOR
const char *vstring = "string1,string2,string3";
const char *ovstring = "ostring1,ostring2,ostring3";
const char *vint = "10,20,-10";
const char *vfloat = "10.1,20.2,-10.4";
#endif
const char *some_string = "some_string";
const char *dict_val = "testb";


bool fork_and_run_check(const char *me)
{
	pid_t pid = fork();
	if (!pid) { // child process
		execlp(me, me,
		       "-s",some_string,
		       "-f", float_str,
		       "-i", int_str,
		       "--tbool",
		       "-d", dict_val,
#ifdef OPT_CAN_VECTOR
		       "-v", vstring,
		       "-o",ovstring,
		       "-u", vint,
		       "-a", vfloat,
#endif
		       "-V", "message",
		       "--internal",
		       NULL);
		cvfail() << "unable to start myself" << endl;
		return -1;
	}else {
		int result = -1;
		return wait(&result) == pid;
	}
}

bool check_entry(const string& key, const string& val, const CHistoryRecord& hr)
{
	CHistoryRecord::const_iterator i = hr.find(key);
	if (i == hr.end()) {
		cvfail() << "key "<< key << " not found" << endl;
		return false;
	}
	bool success = val == i->second;
	if (!success)
		cvfail() << val << " vs. " << i->second << endl;
	return success;
}

int main(int argc, const char *argv[])
{

	CCmdOptionList options(g_general_help);
	options.add(make_opt( options::some_single_string, "string", 's', "some string option",  CCmdOption::required ));
	options.add(make_opt( options::some_float, "float", 'f', "some float option",CCmdOption::required ));
	options.add(make_opt( options::some_int, "int", 'i', "some int option", CCmdOption::required));
	options.add(make_opt( options::true_bool, "tbool", 't', "true_bool"));
	options.add(make_opt( options::internal, "internal", 'l', "internal test option" ));
	options.add(make_opt( options::dict_val, the_dict, "dict", 'd', "other vector of strings"));
#ifdef OPT_CAN_VECTOR
	options.add(make_opt( options::series_of_strings, "vstring", 'v', "a vector of strings", 
				    CCmdOption::required));
	options.add(make_opt( options::other_series_of_strings, "ovstring", 'o', "other vector of strings",
				    CCmdOption::required));
	options.add(make_opt( options::series_of_int, "vint", 'u', "a vector of int", 
				    CCmdOption::required));
	options.add(make_opt( options::series_of_float, "vfloat", 'a', "a vector of float",
				    CCmdOption::required));
#endif

	try {
		if (options.parse(argc, argv)) 
			return EXIT_SUCCESS; 
	}
	catch (invalid_argument& x) {
		assert(!options::internal);
	}

	CHistory& history = CHistory::instance();

	history.append(argv[0], revision, options);

	if (options::internal) {
		// the program was run from the inside, so start the option check
		if (options::some_single_string != string("some_string")) {
			cvfail() << "string failed" << endl;
			return -1;
		}
		if (options::some_float != 2.1f) {
			cvfail() << "float failed" << endl;
			return -1;
		}
		if (options::some_int  != 1231) {
			cvfail() << "int failed" << endl;
			return -1;
		}
		if (!options::true_bool) {
			cvfail() << "bool failed" << endl;
			return -1;
		}

		if (options::dict_val != 2) {
			cvfail() << "dict_val = " <<  dict_val << " -> "<< options::dict_val <<endl;
			cvfail() << "dict failed" << endl;
			return -1;
		}
#ifdef OPT_CAN_VECTOR
		if ((options::series_of_strings[0] != "string1")||
		    (options::series_of_strings[1] != "string2")||
		    (options::series_of_strings[2] != "string3")) {
			cvfail() << "vstring failed" << endl;
			return -1;
		}

		if ((options::other_series_of_strings[0] != "ostring1")||
		    (options::other_series_of_strings[1] != "ostring2")||
		    (options::other_series_of_strings[2] != "ostring3")) {
			cvfail() << "vstring failed" << endl;
			return -1;
		}

		if ((options::series_of_int.size() != 3) ||
		    (options::series_of_int[0] != 10)    ||
		    (options::series_of_int[1] != 20)    ||
		    (options::series_of_int[2] !=-10)) {
			cvfail() << "vint failed" << endl;
			return -1;
		}

		if ((options::series_of_float.size() != 3) ||
		    (options::series_of_float[0] != 10.1f)    ||
		    (options::series_of_float[1] != 20.2f)    ||
		    (options::series_of_float[2] !=-10.4f)) {
			cvfail() << " vfloat failed" << endl;
			return -1;
		}
#endif

		/** now check the history */
		CHistory::const_iterator i = history.begin();
		if (i == history.end()) {
			cvfail() << "no history record available" << endl;
			return -1;
		}
		if (!check_entry("+PROGRAM", argv[0], i->second))
		    return -1;
		if (!check_entry("+LIBMIA_VERSION", PACKAGE_VERSION, i->second))
			return -1;
		if (!check_entry("+USER", getenv("USER"), i->second))
			return -1;
		if (!check_entry("+VERSION", revision, i->second))
			return -1;
		if (!check_entry("float", float_str, i->second))
			return -1;
		if (!check_entry("int", int_str, i->second))
			return -1;
#ifdef OPT_CAN_VECTOR
		if (!check_entry("vstring", vstring, i->second))
			return -1;
		if (!check_entry("vint", vint, i->second))
			return -1;
		if (!check_entry("vfloat", vfloat, i->second))
			return -1;
		if (!check_entry("ovstring", ovstring, i->second))
			return -1;
#endif
		if (!check_entry("string", some_string, i->second))
			return -1;

		if (!check_entry("dict", dict_val, i->second)){
			return -1;
		}

		return EXIT_SUCCESS;

	} else {


		if (!fork_and_run_check(argv[0]))
			cvfail() << "fork_and_run_check\n";


		return EXIT_SUCCESS;

	}
}

