#include "bitpersonality.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

// BitParameter

BitParameter::BitParameter( const char *name, i32 *ptr, i32 defaultValue_, i32 minv_, i32 maxv_ )
	: paramName(name), paramPtr(ptr), paramValue(defaultValue_), paramMin(minv_), paramMax(maxv_)
{
}

String BitParameter::getName() const
{
	return paramName;
}

i32 BitParameter::getDefaultValue() const
{
	return paramValue;
}

i32 BitParameter::getMinValue() const
{
	return paramMin;
}

i32 BitParameter::getMaxValue() const
{
	return paramMax;
}

i32 BitParameter::getValue() const
{
	ASSERT( paramPtr );
	return *paramPtr;
}

i32 BitParameter::setValue(i32 newValue)
{
	ASSERT( paramPtr );
	i32 old = *paramPtr;
	if ( newValue < paramMin )
		newValue = paramMin;
	else if ( newValue > paramMax )
		newValue = paramMax;
	*paramPtr = newValue;
	return old;
}

void BitParameter::reset()
{
	setValue( getDefaultValue() );
}

// AutoBitParameter

AutoBitParameter::AutoBitParameter( const char *name, i32 *ptr, i32 minv_, i32 maxv_ )
: BitParameter( name, ptr, *ptr, minv_, maxv_ )
{
	BitPersonality::get()->add( *this );
}


// BitPersonality

BitPersonality *BitPersonality::globalPers = 0;

BitPersonality *BitPersonality::get()
{
	return globalPers ? globalPers : globalPers = new BitPersonality;
}

bool BitPersonality::add( const BitParameter &par )
{
	if ( parMap.find( par.getName()) != parMap.end() )
		return 0;			// already her
	BitParameter *p = new BitParameter(par);
	parMap[ par.getName() ] = p;
	params.push_back(p);
	return 1;
}

BitPersonality::~BitPersonality()
{
	std::map< String, BitParameter *>::const_iterator ci = parMap.begin();
	size_t i;
	for (i=0; i<params.size(); i++)
		delete params[i];
	params.clear();
	parMap.clear();
}

BitParameter *BitPersonality::find( const String &parName ) const
{
	std::map< String, BitParameter *>::const_iterator ci = parMap.find( parName );
	return ci != parMap.end() ? ci->second : 0;
}

void BitPersonality::getParameters( std::vector< BitParameter * > &params )
{
	params = this->params;
}

bool BitPersonality::save( const String &fnm ) const
{
	FILE *f = fopen(fnm.ansi(), "w");
	if ( !f )
		return 0;
	size_t i;
	u32 maxlen = 0;
	for (i=0; i<params.size(); i++)
	{
		u32 len = params[i]->getName().length();
		if ( len > maxlen )
			maxlen = len;
	}
	for (i=0; i<params.size(); i++)
	{
		const BitParameter *p = params[i];
		String s = p->getName();
		while ( s.length() < maxlen )
			s += " ";
		if ( fprintf(f, "%s = %-6d    // default: %-6d, range: %d .. %d\n", 
			s.ansi(), 
			p->getValue(), 
			p->getDefaultValue(),
			p->getMinValue(),
			p->getMaxValue()
		) < 0 )
			return 0;
	}
	fclose(f);
	return 1;
}

static void skipSpace( const char *&c )
{
	while ( *c && *c <= 32 )
		c++;
}

static void skipSpaceAndEq( const char *&c )
{
	while ( *c && (*c <= 32 || *c == '=') )
		c++;
}

bool BitPersonality::load( const String &fnm )
{
	FILE *f = fopen(fnm.ansi(), "r");
	if ( !f )
		return 0;
	char buf[4096];
	while ( fgets( buf, sizeof(buf), f ) != 0 )
	{
		// parse param name
		const char *c = buf;
		skipSpace(c);
		if ( !isalpha(*c) )
			continue;			// ignore line
		const char *idstart = c;
		while ( *c && isalnum(*c) )
			c++;
		String id( idstart, c );
		// look for id
		BitParameter *par = find(id);
		if ( !par )
		{
			printf("warning: ignoring invalid parameter: `%s'\n", id.ansi());
			continue;
		}
		skipSpaceAndEq(c);
		long val = strtol( c, 0, 10 );
		par->setValue( (i32)val );
	}
	return 1;
}

void BitPersonality::reset()
{
	size_t i;
	for (i=0; i<params.size(); i++)
	{
		params[i]->reset();
	}
}
