/*
    options_util.qc

    Utilities for the options menu

    Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>

    This program 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 2
    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 this program; if not, write to:

        Free Software Foundation, Inc.
        59 Temple Place - Suite 330
        Boston, MA  02111-1307, USA
*/

#include "cvar.h"
#include "draw.h"
#include "system.h"
#include "string.h"
#include "key.h"

#include "Array.h"
#include "gui/Text.h"
#include "gui/Rect.h"
#include "gui/Slider.h"
#include "options_util.h"

@implementation MenuGroup
-(id) init
{
	if ((self = [super init]))
		current = base = 0;
}

-(void)setBase:(integer)b
{
	if (b >= [views count])
		b = [views count] - 1;
	if (b < 0)
		b = 0;
	current = base = b;
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	switch (key) {
		case QFK_DOWN:
		case QFM_WHEEL_DOWN:
			[self next];
			return 1;
		case QFK_UP:
		case QFM_WHEEL_UP:
			[self prev];
			return 1;
		default:
			return [[views getItemAt:current]
						keyEvent:key
						unicode:unicode
						down:down];
	}
}

-(void) next
{
	if (++current >= [views count])
		current = base;
}

-(void) prev
{
	if (--current < base)
		current = [views count] - 1;
}

- (void) draw
{
	local View cur;

	[super draw];
	cur = (View) [views getItemAt:current];
	opt_cursor  (cur.xabs - 8, cur.yabs);
}
@end


@implementation CvarObject
-(id)init
{
	self = [super init];
	name = str_new ();
	return self;
}

-(id)initWithCvar:(string)cvname
{
	self = [self init];
	str_copy (name, cvname);
	return self;
}

-(void)dealloc
{
	str_free (name);
}
@end

@implementation CvarToggle
-(void)toggle
{
	Cvar_Toggle (name);
}

-(BOOL)value
{
	return Cvar_GetInteger (name);
}
@end

@implementation MouseToggle
-(void)toggle
{
	if (Cvar_GetFloat ("m_pitch") < 0) {
		Cvar_SetFloat ("m_pitch", 0.022);
	} else {
		Cvar_SetFloat ("m_pitch", -0.022);
	}
}

-(BOOL)value
{
	return Cvar_GetFloat ("m_pitch") < 0;
}
@end

@implementation RunToggle
-(void)toggle
{
	if (Cvar_GetFloat ("cl_forwardspeed") < 400) {
		Cvar_SetFloat ("cl_forwardspeed", 400);
		Cvar_SetFloat ("cl_backspeed", 400);
	} else {
		Cvar_SetFloat ("cl_forwardspeed", 200);
		Cvar_SetFloat ("cl_backspeed", 200);
	}
}

-(BOOL)value
{
	return Cvar_GetFloat  ("cl_forwardspeed") >= 400;
}
@end

@implementation CvarToggleView

-(void)update
{
	[value setText:[toggle value] ? "On" : "Off"];
}

-(id)initWithBounds:(Rect)aRect title:(string)_title :(CvarToggle)_toggle
{
	local Rect rect;

	self = [super initWithBounds:aRect];

	toggle = _toggle;
	
	rect = [[Rect alloc] initWithComponents:0 :0 :strlen (_title) * 8 :8];
	title = [[Text alloc] initWithBounds:rect text:_title];

	rect.size.width = 3 * 8;
	rect.origin.x = xlen - rect.size.width;
	value = [[Text alloc] initWithBounds:rect];

	[self addView:title];
	[self addView:value];

	[self update];

	return self;
}

-(void)toggle
{
	[toggle toggle];
	[self update];
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	switch (key) {
		case QFK_RETURN:
		case QFM_BUTTON1:
			[self toggle];
			return 1;
		default:
			return 0;
	}
}

@end

@implementation CvarColor
-(void)next
{
	local float val = Cvar_GetFloat (name);
	val = min_max_cnt (0, 13, 1, val, 1);
	Cvar_SetFloat (name, val);
}

-(void)prev
{
	local float val = Cvar_GetFloat (name);
	val = min_max_cnt (0, 13, 1, val, 0);
	Cvar_SetFloat (name, val);
}

-(integer)value
{
	return Cvar_GetInteger (name);
}
@end

@implementation CvarColorView
-(id)initWithBounds:(Rect)aRect :(CvarColor)_color
{
	self = [self initWithBounds:aRect];
	color = _color;
	return self;
}

-(void)next
{
	[color next];
}

-(void)prev
{
	[color prev];
}

-(void)draw
{
	local integer xl;
	xl = xlen / 8 - 2;
	text_box (xabs, yabs, xl, ylen / 8 - 2);
	xl = (xl + 1) & ~1;		// text_box only does multiples of 2
	Draw_Fill (xabs + 8, yabs + 8, xl * 8, ylen - 16,
			   [color value] * 16 + 8);
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	switch (key) {
		case QFK_DOWN:
		case QFM_WHEEL_DOWN:
			[self next];
			return 1;
		case QFK_UP:
		case QFM_WHEEL_UP:
			[self prev];
			return 1;
	}
}
@end

@implementation CvarRange

-(id)initWithCvar:(string)cvname min:(float)_min max:(float)_max step:(float)_step
{
	self = [super init];

	name = str_new ();
	str_copy (name, cvname);
	min = _min;
	max = _max;
	step = _step;

	return self;
}

-(void)inc
{
	local float val = Cvar_GetFloat (name);
	val = min_max_cnt (min, max, step, val, 1);
	Cvar_SetFloat (name, val);
}

-(void)dec
{
	local float val = Cvar_GetFloat (name);
	val = min_max_cnt (min, max, step, val, 0);
	Cvar_SetFloat (name, val);
}

-(float)value
{
	return Cvar_GetFloat (name);
}

-(integer)percentage
{
	return to_percentage(min, max, Cvar_GetFloat (name));
}

@end

@implementation CvarRangeView

-(void)update
{
	[slider setIndex:[range percentage]];
	[value setText:ftos ([range value])];
}

-(id)initWithBounds:(Rect)aRect title:(string)_title sliderWidth:(integer)width :(CvarRange)_range
{
	local Rect rect;

	self = [super initWithBounds:aRect];

	range = _range;
	
	rect = [[Rect alloc] initWithComponents:0 :0 :strlen (_title) * 8 :8];
	title = [[Text alloc] initWithBounds:rect text:_title];

	rect.origin.x += rect.size.width + 8;
	rect.size.width = width;
	if (rect.origin.x + rect.size.width > xlen)
		rect.size.width = xlen - rect.origin.x;
	slider = [[Slider alloc] initWithBounds:rect size:100];

	rect.origin.x += rect.size.width + 8;
	rect.size.width = xlen - rect.origin.x;
	value = [[Text alloc] initWithBounds:rect];

	[self addView:title];
	[self addView:slider];
	[self addView:value];

	[self update];

	return self;
}

-(void)inc
{
	[range inc];
	[self update];
}

-(void)dec
{
	[range dec];
	[self update];
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	switch (key) {
		case QFK_RIGHT:
			[self inc];
			return 1;
		case QFK_LEFT:
			[self dec];
			return 1;
	}
}

@end

@implementation CrosshairCvar
-(void) next
{
	local integer val = Cvar_GetInteger (name);
	Cvar_SetInteger (name, (val + 1) % 4);
}

-(integer) crosshair
{
	return Cvar_GetInteger (name);
}
@end

@implementation CrosshairView
{
	CrosshairCvar crosshair;
}

-(id)initWithBounds:(Rect)aRect :(CrosshairCvar)_crosshair
{
	self = [self initWithBounds:aRect];
	crosshair = _crosshair;
	return self;
}

-(void) next
{
	[crosshair next];
}

-(void) draw
{
	Draw_Fill (xabs, yabs, xlen, ylen, 0);
	Draw_Crosshair ([crosshair crosshair], xabs + xlen / 2, yabs + ylen / 2);
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	switch (key) {
		case QFK_RETURN:
		case QFM_BUTTON1:
			[self next];
			return 1;
		default:
			return 0;
	}
}
@end
void traceon () = #0;
@implementation ProxyView

-(id)initWithBounds:(Rect)aRect title:(View)aTitle view:(View)aView
{
	self = [super initWithBounds:aRect];
	if (!self)
		return self;

	title = aTitle;
	view = aView;
	return self;
}

- (integer) keyEvent:(integer)key unicode:(integer)unicode down:(integer)down
{
	return [view keyEvent:key unicode:unicode down:down];
}

- (void) draw
{
	[title draw];
	[view draw];
}

- (void) setBasePos: (Point) pos
{
    [super setBasePos:pos];
	local Point point = [[Point alloc] initWithComponents:xabs :yabs];
	[title setBasePos:point];
	[view setBasePos:point];
	[point release];
}

@end

/*
	opt_cursor

	function for drawing the cursor
*/
void (integer x, integer y)
opt_cursor =
{
	// use time becaus we want a nice rotaing cursor
	Draw_Character (x, y, 12 + (integer) (time * 4) & 1);
};

/*
	draw_item
 
	Draws a item with a specific spacing between label and value to
	position x, y.
	Used as helper function for draw_val_item.
*/
void (integer x, integer y, integer spacing, string spacechar,
	  string label, string valstr)
draw_item =
{
	local integer	i;

	Draw_String	(x,  y,	label); 
	for (i = x + (integer) strlen (label) * 8; i < (x+spacing); i += 8) {
		Draw_String (i, y, spacechar);
	}
	Draw_String (x + spacing,  y, valstr);
};

/* 
	draw_val_item

	Draws a nice menu item. 
	Use this function for a consistent look of menu items!
	Example:
		<Label>.....:<valstr>
			spacing are the number of the points to put
*/
void (integer x, integer y, integer spacing, string label, string valstr) 
draw_val_item =
{
	draw_item (x, y, spacing, ".", label, ":" + valstr);
};

/*
	to_percentage

	Calculates the percentage of a value relative
	to a min and max value.
*/
integer (float min, float max, float val) 
to_percentage =
{
	local float		max_v = (max - min);
	local integer	perc;

	val -= min;

	if (val > max_v) {
		val = max_v;
	}
	if (val < 0) {
		val = 0;
	}
	perc = (integer) ((val / max_v) * 100);

	return perc;
};

/*
	min_max_cnt

	Increases or decreases a value by take care of the bordervalues.
	min, max are the borders.
	step is the step by in-/de-creasing.
	cntflag should be true for increasing and false for decreasing
*/
float (float min, float max, float step, float val, integer cntflag)
min_max_cnt =
{
	if (cntflag) {
		val += step;
	} else {
		val -= step;
	}

	if (val > max) {
		val = max;
	} else if (val < min) {
		val = min;
	}
	return val;
};