/*
** FORMATTERS.CPP
*/
#include <XMessages.h>
#include <XList.h>
#include <XView.h>
#include <XWindow.h>
#include <XButtons.h>
#include <XDialog.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <string.h>
#include "TBitmapView.h"
#include "myth_defs.h"
#include "tags.h"
#include "formatters.h"

static char formatters_text[256];

// core math
float incremental_round( float value, float increment )
{
	assert( increment );
	
	float float_value = value / increment;
	long trunc_value = float_value;
	float rounded;

	if( float_value > 0.0 )
	{
		if( float_value - (float)trunc_value < 0.5 )
			rounded = trunc_value * increment;
		else
			rounded = (trunc_value + 1) * increment;
	}
	else
	{
		if( float_value - (float)trunc_value > -0.5 )
			rounded = trunc_value * increment;
		else
			rounded = (trunc_value - 1) * increment;
	}
	
	return rounded;
}

char get_delta( char lower, char upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

char get_upper_bound( char lower, char delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}

unsigned char get_delta( unsigned char lower, unsigned char upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

unsigned char get_upper_bound( unsigned char lower, unsigned char delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}

short get_delta( short lower, short upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

short get_upper_bound( short lower, short delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}

unsigned short get_delta( unsigned short lower, unsigned short upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

unsigned short get_upper_bound( unsigned short lower, unsigned short delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}

long get_delta( long lower, long upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

long get_upper_bound( long lower, long delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}

unsigned long get_delta( unsigned long lower, unsigned long upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

unsigned long get_upper_bound( unsigned long lower, unsigned long delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}


float get_delta( float lower, float upper )
{
	if( upper - lower )
	{
		return upper - lower + 1;
	}
	
	return 0;
}

float get_upper_bound( float lower, float delta )
{
	if( delta )
		return lower + delta - 1;
		
	return lower + delta;
}



float short_fixed_to_float( short_fixed val )
{
	return (float)(((double)val) / (double)SHORT_FIXED_ONE);
}

short_fixed float_to_short_fixed( float val )
{
	long whole = (val < 0) ? ceil( val ) : floor( val );
	float decimal = (val < 0) ? (val - whole) * -1: val - whole;

#if 0
	decimal = (decimal * (float)(SHORT_FIXED_ONE + 0.5));
#else
	decimal = round( decimal * (float)SHORT_FIXED_ONE );
#endif
	
	
	if( val < 0 )
		return (short_fixed)((whole * SHORT_FIXED_ONE) - decimal);
		
	return (short_fixed)((whole * SHORT_FIXED_ONE) + decimal);
}


float fixed_to_float( fixed val )
{
	return (float)(((double)val) / (double)FIXED_ONE);
}

fixed float_to_fixed( float val )
{
	long whole = (val < 0) ? ceil( val ) : floor( val );
	float decimal = (val < 0) ? (val - whole) * -1: val - whole;

#if 0
	decimal = (decimal * (float)(FIXED_ONE + 0.5));
#else
	decimal = round( decimal * (float)FIXED_ONE );
#endif

	if( val < 0 )
		return (fixed)((whole * FIXED_ONE) - decimal);
		
	return (fixed)((whole * FIXED_ONE) + decimal);
}


float short_fixed_fraction_to_float( short_fixed_fraction val )
{
	return (float)(((double)val) / SHORT_FIXED_ONE);
}

short_fixed_fraction float_to_short_fixed_fraction( float val )
{
#if 0
	return (short_fixed_fraction)(val * (float)(SHORT_FIXED_ONE + 0.5));
#else
	return (short_fixed_fraction)round( val * (float)SHORT_FIXED_ONE );
#endif
}


float fixed_fraction_to_float( fixed_fraction val )
{
	return (float)(((double)val) / FIXED_ONE);
}

fixed_fraction float_to_fixed_fraction( float val )
{
#if 0
	return (short_fixed_fraction)(val * (float)(FIXED_ONE + 0.5));
#else
	return (short_fixed_fraction)round( val * (float)FIXED_ONE );
#endif
}


float short_percentage_to_float( short_fixed_fraction val )
{
	return ((float)(val)) / (SHORT_FIXED_ONE - 1);
}

short_fixed_fraction float_to_short_percentage( float val )
{
	return (short_fixed_fraction)PIN( incremental_round( val * (float)(SHORT_FIXED_ONE - 1 ), 1.0 ), 0, SHORT_FIXED_ONE - 1 );
}


float percentage_to_float( fixed_fraction val )
{
	return ((float)(val)) / (FIXED_ONE - 1);
}

fixed_fraction float_to_percentage( float val )
{
	return (fixed_fraction)PIN( incremental_round( val * (float)(FIXED_ONE - 1), 1.0), 0, FIXED_ONE - 1 );
}


float short_world_distance_to_float( short_world_distance val )
{
	return (float)(val / (float)WORLD_ONE);
}

short_world_distance float_to_short_world_distance( float val )
{
	return (short_world_distance)incremental_round( (val * (float)WORLD_ONE), 1 );
}


float world_distance_to_float( world_distance val )
{
	return (float)(val / (float)WORLD_ONE);
}

world_distance float_to_world_distance( float val )
{
	return (world_distance)incremental_round( (val * (float)WORLD_ONE), 1);
}


float angle_to_float( angle val )
{
#if 1
	return (float)(((val << (32 - ANGULAR_BITS)) * 360.0) / 4294967296);
#else
	return incremental_round( (((val << (32 - ANGULAR_BITS)) * 360.0) / 4294967296), 1.0 );
#endif
}

angle float_to_angle( float val )
{
	unsigned long temp = floor( ((val * (double)(1 << ANGULAR_BITS)) / 360.0) + 0.5 );
	return temp & (FULL_CIRCLE - 1);
}


//  GUI functions
char *format_float( double value, int precision )
{
	sprintf( formatters_text, "%.*f", precision, value );
	char *s = formatters_text + strlen( formatters_text ) - 1;
	
	while( s > formatters_text && *s == '0' && s[-1] != '.' )
	{
		*s = 0;
		--s;
	}
	
	return formatters_text;
}

void SetTag( XGWindow* window, int view_id, tag type, tag value )
{
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
	
	view->SetRefNum( value );
	if( value != 0 && value != 0xffffffff )
	{
		tag_entry *e = get_entry( type, value );
		if( e )
		{
			view->SetTitle( get_entry_name( e ) );
		}
		else
		{
			sprintf( formatters_text, "(missing tag '%s')", tagstr( value ) );
			view->SetTitle( formatters_text );
		}
	}
	else
	{
		view->SetTitle( "(tag unassigned)" );
	}
}

tag GetTag( XGWindow *window, int view_id )
{
	if( !window )
		return -1;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return -1;
	
	file_tag tag_id = view->GetRefNum();
	if (tag_id == 0) tag_id = -1;
	if (tag_id == '____') tag_id = -1;
	
	return tag_id;
}

void SetText( XGWindow* window, int view_id, const char *value )
{
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
	
	view->SetTitle( value ? value : "" );
}

void GetText( XGWindow* window, int view_id, char *value, int maxlen )
{
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
	
	view->GetTitle( formatters_text );
	strncpy( value, formatters_text, maxlen );
	value[maxlen - 1] = 0;
}

void SetInt( XGWindow* window, int view_id, int value )
{
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
	
	XGListControl *list;
	XGControl *btn;
	
	btn = dynamic_cast<XGControl *>(view);
	if( btn )
	{
		btn->SetValue( value );
		return;
	}
	
	list = dynamic_cast<XGListControl *>(view);
	if( list )
	{
		list->SetValue( value );
		return;
	}
	
	sprintf( formatters_text,"%ld",value );
	view->SetTitle( formatters_text );
}

int GetInt( XGWindow* window, int view_id )
{
	if( !window )
		return 0;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return 0;
	
	XGListControl *list;
	XGControl *btn;
	
	btn = dynamic_cast<XGControl *>(view);
	if( btn )
	{
		return btn->GetValue();
	}
	
	list = dynamic_cast<XGListControl *>(view);
	if( list )
	{
		return list->GetValue();
	}
	
	view->GetTitle( formatters_text );
	return atol( formatters_text );
}

void SetFloat( XGWindow* window, int view_id, double value, int precision )
{
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
		
	// this just returns formatters_text		
	format_float( value, precision );
	view->SetTitle( formatters_text );
}

double GetFloat( XGWindow* window, int view_id )
{
	if( !window )
		return 0.0;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return 0.0;
	
	view->GetTitle( formatters_text );
	return atof( formatters_text );
}

void SetFlag( XGWindow* window, int view_id, bool value )
{
	SetInt( window, view_id, (value ? 1 : 0) );
}

bool GetFlag( XGWindow* window, int view_id )
{
	return GetInt( window, view_id ) != 0;
}

void SetShortFixed( XGWindow* window, int view_id, short_fixed value )
{
	SetFloat( window, view_id, short_fixed_to_float( value ), 3 );
}

short_fixed GetShortFixed( XGWindow* window, int view_id )
{
	return float_to_short_fixed( GetFloat( window, view_id ) );
}

void SetFixed( XGWindow* window, int view_id, fixed value )
{
	SetFloat( window, view_id, fixed_to_float( value ), 4 );
}

fixed GetFixed( XGWindow* window, int view_id )
{
	return float_to_fixed( GetFloat( window, view_id ) );
}

void SetShortFixedFraction( XGWindow* window, int view_id, short_fixed_fraction value )
{
	SetFloat( window, view_id, short_fixed_fraction_to_float( value ), 3 );
}

short_fixed_fraction GetShortFixedFraction( XGWindow* window, int view_id )
{
	return float_to_short_fixed_fraction( GetFloat( window, view_id ) );
}

void SetFixedFraction( XGWindow* window,  int view_id, fixed_fraction value )
{
	SetFloat( window, view_id, fixed_fraction_to_float( value ), 4 );
}

fixed_fraction GetFixedFraction( XGWindow* window, int view_id )
{
	return float_to_fixed_fraction( GetFloat( window, view_id ) );
}

void SetShortPercentage( XGWindow* window, int view_id, short_fixed_fraction value )
{
	SetFloat( window, view_id, short_percentage_to_float( value ), 3 );
}

short_fixed_fraction GetShortPercentage( XGWindow* window, int view_id )
{
	return float_to_short_percentage( GetFloat( window, view_id ) );
}

void SetPercentage( XGWindow* window,  int view_id, fixed_fraction value )
{
	SetFloat( window, view_id, percentage_to_float( value ), 4 );
}

fixed_fraction GetPercentage( XGWindow* window, int view_id )
{
	return float_to_percentage( GetFloat( window, view_id ) );
}

void SetShortWorldDistance( XGWindow* window, int view_id, short_world_distance value )
{
	SetFloat( window, view_id, short_world_distance_to_float( value ), 3 );
}

short_world_distance GetShortWorldDistance(XGWindow* window, int view_id )
{
	return float_to_short_world_distance( GetFloat( window, view_id ) );
}

void SetWorldDistance(XGWindow* window, int view_id, world_distance value )
{
	SetFloat( window, view_id, world_distance_to_float( value ), 4 );
}

world_distance  GetWorldDistance(XGWindow* window, int view_id )
{
	return float_to_world_distance( GetFloat( window, view_id ) );
}

void SetAngle(XGWindow* window, int view_id, int value )
{
	SetFloat( window, view_id, angle_to_float( value ), 2 );
}

int GetAngle(XGWindow* window, int view_id )
{
	return float_to_angle( GetFloat( window, view_id ) );
}

void SetAngleInSeconds(XGWindow* window, int view_id, int value )
{
	SetFloat( window, view_id, angle_to_float( value ) * TICKS_PER_SECOND, 2 );
}

int GetAngleInSeconds(XGWindow* window, int view_id )
{
	return float_to_angle( GetFloat( window, view_id ) ) / TICKS_PER_SECOND;
}

void SetColor( XGWindow* window, int view_id, const struct rgb_color &rgb )
{
	enum
	{
		bufsize = 4
	};
	
	unsigned char c[bufsize*bufsize][3];

	for( int i = 0; i < bufsize * bufsize; ++i )
	{
		c[i][0] = rgb.red >> 8;
		c[i][1] = rgb.green >> 8;
		c[i][2] = rgb.blue >> 8;
	}
	
	if( !window )
		return;
		
	XGView *view = window->FindViewByID( view_id );
	if( !view )
		return;
	
	TBitmapView *bmp = dynamic_cast<TBitmapView *>(view);
	if( !bmp )
		return;
		
	bmp->SetBitmapRGB( c, bufsize, bufsize, 0 );
	bmp->InvalView();
}
