#include "config.h"
#include <cstdlib>
#include <cctype>


namespace NTPM {


void
Configurator::open(const string &_cfgfname)
{
    void parse_configuration_file(Configurator *_configurator);
    cfgfname = _cfgfname;
    parse_configuration_file(this);
}

void
Configurator::CallSubroutine(const string &subroutine)
{
    string key;
    Class *cls;

    find_class_and_key(subroutine, &cls, &key);
    if(cls)
	cls->CallSubroutine(key);
    else
	root_class.CallSubroutine("non existant subroutine");
}




void
Configurator::Subroutine::Execute() const
{
    for(map<string,string>::const_iterator it=bindings.begin();
	it != bindings.end(); ++it)
    {
	string key;
	Class *cls;


	if((*it).first.substr(0,4) == "call") {
	    owner->find_class_and_key((*it).second, &cls, &key, parent);
	    cls->CallSubroutine(key);
	} else {
	    owner->find_class_and_key((*it).first, &cls, &key, parent);
	    cls->bindings[key] = (*it).second;
	}
    }
}


void
Configurator::Class::print()
{
    cout << "class " << name << " child of "
	 << (parent?parent->name:"(orphan)") <<endl;
    for(map<string,string>::iterator it=bindings.begin();
	it != bindings.end(); ++it)
	cout << (*it).first << " : " << (*it).second << endl;
    for(list<Class>::iterator it=children.begin();
	it!=children.end(); ++it)
	(*it).print();
}


string
Configurator::Class::Lookup(const string &key) const
{
    map<string,string>::const_iterator bi = bindings.find(key);
    if(bi != bindings.end())
	return (*bi).second;

    string ret;

    if(parent) {
	ret = parent->Lookup(key);
	if(ret.length())
	    return ret;
    }

    for(list<string>::const_iterator it=parents.begin();
	it!=parents.end(); ++it) {
	const Class *par = const_cast<Configurator*>(owner)->find_class(*it);
	if(!par)
	    throw runtime_error("no class called " + (*it) +
				" (passed as argument to inherit)");
	ret = par->Lookup(key);
	if(ret.length())
	    return ret;
    }

    return string();
}


Configurator::Class *
Configurator::Class::GetChild(const string &childname)
{
    for(list<Class>::iterator it=children.begin(); it!=children.end(); ++it)
	if((*it).name == childname)
	    return &(*it);
    return NULL;
}

const Configurator::Class *
Configurator::Class::GetChild(const string &childname) const
{
    for(list<Class>::const_iterator it=children.begin();
	it!=children.end(); ++it)
	if((*it).name == childname)
	    return &(*it);
    return NULL;
}

void
Configurator::Class::CallSubroutine(const string &subroutine)
{
    for(list<Subroutine>::iterator it=subroutines.begin();
	it!=subroutines.end(); ++it)
	if((*it).name == subroutine) {
	    (*it).Execute();
	    return;
	}

    // didn't find one in this class. try the parent class.
    if(parent==NULL)
	throw runtime_error("No subroutine " + subroutine + " found.");
    parent->CallSubroutine(subroutine);
}


Configurator::Class *
Configurator::find_class(const string &classname, Class *start_from=NULL)
{
    Class *cls = start_from ? start_from : &root_class;
    string name = classname;
    if(name.empty())
	return cls;

    do {
	string::size_type next_sep = name.find_first_of(':');
	string comp = name.substr(0,next_sep);
	name = name.substr(next_sep+1,string::npos);

	if(!comp.empty())
	    cls = cls->GetChild(comp);
	if(next_sep == string::npos) return cls;
    } while(cls!=NULL);
    return NULL;
}

void
Configurator::find_class_and_key(const string &classpath,
				 Class **cls, string *key,
				 Class *starting_from)
{
    string::size_type key_pos = classpath.find_last_of(':');
    if(key_pos == string::npos) {
	*cls = &root_class;
	*key = classpath;
	return;
    }
    *cls = find_class(classpath.substr(0,key_pos), starting_from);
    *key = classpath.substr(key_pos+1, string::npos);
}


template <>
string
Configurator::lookup<string>(const Configurator::Class *cls, const string &key)
    const
{
    return cls->Lookup(key);
}


template <>
int
Configurator::lookup<int>(const Class *cls, const string &key) const
{
    return atoi(lookup<string>(cls, key).c_str());
}

template <>
float
Configurator::lookup<float>(const Class *cls, const string &key) const
{
    return atof(lookup<string>(cls, key).c_str());
}

template <>
bool
Configurator::lookup<bool>(const Class *cls, const string &key) const
{
    string v = lookup<string>(cls, key);
    return (tolower(v[0]=='t')) || (v[0]=='1');
}



string Configurator::lookup(const string &classpath) const {
    return lookup<string>(classpath);
}

string Configurator::lookup(const string &classname, const string &key) const {
    return lookup<string>(classname,key);
}


#define NTPM_TEST_CONFIG
#ifdef NTPM_TEST_CONFIG

template <class CONTAINER>
void
print_container(const CONTAINER &lst)
{
    cout << "{ ";
    for(typename CONTAINER::const_iterator it = lst.begin();
	it != lst.end(); ++it)
	cout << *it << ' ';
    cout << "}";
}

void
config_test(Configurator &cfg)
{
    if(!cfg.lookup<bool>("Library:Testing:config"))
	return;
    cout << "* Configuration test." << endl;
    cout << ":Bar::opt1 : " << cfg.lookup(":Bar::opt1") << endl;
    cout << "Bar:opt1 : " << cfg.lookup("Bar:", "opt1") << endl;
    cout << "Bar-Derived1:opt1 : "
	 << cfg.lookup("Bar:Bar-Derived1", "opt1") << endl;
    cout << "Bar-Derived2:opt1 : "
	 << cfg.lookup("Bar:Bar-Derived2:opt1") << endl;
    cout << "Bar:opt3 : " << cfg.lookup<int>("Bar:opt3") << endl;

    cout << ":Foo:Foo-Derived:opt2 : " <<
	cfg.lookup(":Foo:Foo-Derived:opt2") << endl;
    cout << ":Foo:Foo-Derived:opt1 : " <<
	cfg.lookup(":Foo:Foo-Derived:opt1") << endl;


    cout << "Before subroutine call: " << endl;
    cout << "Bar:Bar-Derived1:opt1 : "
	 << cfg.lookup("Bar:Bar-Derived1:opt1") << endl;
    cout << "Bar:Bar-Derived1:opt2 : "
	 << cfg.lookup("Bar:Bar-Derived1:opt2") << endl;
    cfg.CallSubroutine("Configuration1");
    cout << "After subroutine call: " << endl;
    cout << "Bar:Bar-Derived1:opt1 : "
	 << cfg.lookup("Bar:Bar-Derived1:opt1") << endl;
    cout << "Bar:Bar-Derived1:opt2 : "
	 << cfg.lookup("Bar:Bar-Derived1:opt2") << endl;

    cout << "Bar:list: " << cfg.lookup("Bar:list") << endl;
    cout << "Bar:list (as list): ";
    print_container(cfg.list_lookup<int>("Bar:list")); cout << endl;
}

#endif // NTPM_TEST_CONFIG

}
#if 1
int
main()
{
    NTPM::Configurator cfg("ntpm.conf");
    try {
    NTPM::config_test(cfg);
    } catch(const std::exception &e) {
	cerr << "Configuration error: " << e.what() << endl;
    }
}
#endif
