/*
 * \file Options.c
 * \brief: implementation of the Options class, derived from DataSet class
 */

/*Headers: {{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <vector>
#include <algorithm>
#include <cstring>

#include "./DataSet.h"
#include "../shared/shared.h"
#include "../io/io.h"
#include "../include/include.h"
#include "../shared/shared.h"
#include "../EnumDefinitions/EnumDefinitions.h"
#include "../io/io.h"
/*}}}*/

/*Object constructors and destructor*/
/*FUNCTION Options::Options(){{{*/
Options::Options(){
	return;
}
/*}}}*/
/*FUNCTION Options::~Options(){{{*/
Options::~Options(){
	return;
}
/*}}}*/

/*Object management*/
/*FUNCTION Options::AddOption{{{*/
int  Options::AddOption(Option* in_option){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*In debugging mode, check that the option is not a NULL pointer*/
	_assert_(in_option);

	/*Also, check the option name*/
	if(!in_option->name) _error2_("input option has an empty name");
	if(strchr(in_option->name,'.')) _error2_("Option \"" << in_option->name << "\" has a protected character \".\"");
	if(strchr(in_option->name,'[')) _error2_("Option \"" << in_option->name << "\" has a protected character \"[\"");
	if(strchr(in_option->name,']')) _error2_("Option \"" << in_option->name << "\" has a protected character \"]\"");

	/*Finally, check that no option of the same name already exists in the dataset*/
	for(object=objects.begin();object<objects.end();object++){

		option=(Option*)(*object); 
		if (!strcmp(option->name,in_option->name)){
			_error2_("Options \"" << in_option->name << "\" found multiple times");
			break;
		}
	}

	/*OK, all checks went well, add option to dataset*/
	this->AddObject(in_option);

	return 1;
}
/*}}}*/
/*FUNCTION Options::Get(int* pvalue, char* name){{{*/
void Options::Get(int* pvalue,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		_error2_("option of name \"" << name << "\" not found, and no default value has been provided");
	}
}
/*}}}*/
/*FUNCTION Options::Get(int* pvalue, char* name,int default_value){{{*/
void Options::Get(int* pvalue,const char* name,int default_value){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, a default is provided here*/
	else{
		*pvalue=default_value;
	}
}
/*}}}*/
/*FUNCTION Options::Get(IssmDouble* pvalue, char* name){{{*/
void Options::Get(IssmDouble* pvalue,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		_error2_("option of name \"" << name << "\" not found, and no default value has been provided");
	}
}
/*}}}*/
/*FUNCTION Options::Get(IssmDouble* pvalue, char* name,IssmDouble default_value){{{*/
void Options::Get(IssmDouble* pvalue,const char* name,IssmDouble default_value){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, a default is provided here*/
	else{
		*pvalue=default_value;
	}
}
/*}}}*/
/*FUNCTION Options::Get(bool* pvalue, char* name){{{*/
void Options::Get(bool* pvalue,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		_error2_("option of name \"" << name << "\" not found, and no default value has been provided");
	}
}
/*}}}*/
/*FUNCTION Options::Get(bool* pvalue, char* name,bool default_value){{{*/
void Options::Get(bool* pvalue,const char* name,bool default_value){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, a default is provided here*/
	else{
		*pvalue=default_value;
	}
}
/*}}}*/
/*FUNCTION Options::Get(char** pvalue, char* name){{{*/
void Options::Get(char** pvalue,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;
	char* outstring=NULL;
	int   stringsize;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		_error2_("option of name \"" << name << "\" not found, and no default value has been provided");
	}

}
/*}}}*/
/*FUNCTION Options::Get(char** pvalue, char* name,char* default_value){{{*/
void Options::Get(char** pvalue,const char* name,const char* default_value){

	vector<Object*>::iterator object;
	Option* option=NULL;
	char* outstring=NULL;
	int   stringsize;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue);
	}
	/*Else, the Option does not exist, a default is provided here*/
	else{
		stringsize=strlen(default_value)+1;
		outstring=xNew<char>(stringsize);
		xMemCpy<char>(outstring,default_value,stringsize);
		*pvalue=outstring;
	}

}
/*}}}*/
/*FUNCTION Options::Get(char*** ppvalue,int* numel,char* name){{{*/
void Options::Get(char*** ppvalue,int* numel,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;
	Option* option2=NULL;
	Options* options=NULL;
	int   i;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		/*If the object is a Cell, copy the strings from its options dataset*/ 
		if(option->ObjectEnum()==OptionCellEnum){
			if (option->NumEl()) {
				*ppvalue=xNew<char*>(option->NumEl());
				if (numel) *numel=option->NumEl();
				option->Get(&options);
				for (i=0; i<option->NumEl(); i++) {
					option2=((Option *)options->GetObjectByOffset(i));
					if(option2->ObjectEnum()==OptionCharEnum)
						option2->Get(&((*ppvalue)[i]));
					else
						((*ppvalue)[i])=NULL;
				}
			}
		}
		/*If the object is a Char, copy the strings from its concatenation*/ 
		else if(option->ObjectEnum()==OptionCharEnum){
			option->Get(ppvalue,numel);
		}
		/*Else: not supported*/
		else{
			_error2_("Cannot recover field \"" << name << "\" for an option of type " << EnumToStringx(option->ObjectEnum()));
		}
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		*ppvalue=NULL;
		if (numel) *numel=0;
	}

}
/*}}}*/
/*FUNCTION Options::Get(IssmDouble** pvalue,int* numel,const char* name){{{*/
void Options::Get(IssmDouble** pvalue,int* numel,const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Get option*/
	option=GetOption(name);

	/*If the pointer is not NULL, the option has been found*/
	if(option){
		option->Get(pvalue,numel);
	}
	/*Else, the Option does not exist, no default provided*/
	else{
		_error2_("option of name \"" << name << "\" not found, and no default value has been provided");
	}
}
/*}}}*/
/*FUNCTION Options::GetOption{{{*/
Option* Options::GetOption(const char* name){

	vector<Object*>::iterator object;
	Option* option=NULL;

	/*Go through options and find option: */
	for ( object=objects.begin() ; object < objects.end(); object++ ){

		option=(Option*)(*object); 
		if (!strncmp(name,option->name,strlen(option->name))){

			/*OK, now do we have a complete name? If not, it is a cell or a structure, we need to go further*/
			if(!strcmp(name,option->name)){
				return option;
			}
			else{
				/*If the object is a Cell, recursive call to its options*/ 
				if(option->ObjectEnum()==OptionCellEnum){
					return ((OptionCell*)option)->values->GetOption(name);
				}
				/*If the object is a Struct loop over its size and recursive call*/
				else if(option->ObjectEnum()==OptionStructEnum){
					for(int i=0;i<option->numel;i++){
						_assert_(((OptionStruct*)option)->values[i]);
						return ((OptionStruct*)option)->values[i]->GetOption(name);
					}
				}
				/*Else: not supported*/
				else{
					_error2_("Cannot recover field \"" << name << "\" for an option of type " << EnumToStringx(option->ObjectEnum()));
				}
			}
		}
	}

	/*Option not found return NULL pointer*/
	return NULL;
}
/*}}}*/
