/*
Copyright (C) 1997-2001 Id Software, Inc.

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 the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include <string.h>
#include <ctype.h>

#include "ui_local.h"

static void	 Field_Init( menucommon_t *f );
static void	 Action_DoEnter( menucommon_t *a );
static void	 Action_Draw( menucommon_t *a );
void  Menu_DrawStatusBar( char *string );
void Menu_AdjustRectangle ( int *mins, int *maxs );
static void	 Separator_Draw( menucommon_t *s );
static void	 Slider_DoSlide( menucommon_t *s, int dir );
static void	 Slider_Draw( menucommon_t *s );
//static void	 SpinControl_DoEnter( menulist_s *s );
static void	 SpinControl_Draw( menucommon_t *s );
static void	 SpinControl_DoSlide( menucommon_t *s, int dir );
static void  Scrollbar_DoSlide( menucommon_t *s, int dir );
static void	 Scrollbar_Draw( menucommon_t *s );

#define RCOLUMN_OFFSET  16
#define LCOLUMN_OFFSET -16

vec4_t colorWarsowOrange = { 0.95f, 0.33f, 0.1f, 1.0f };
vec4_t colorWarsowOrangeBright = { 1.0f, 0.6f, 0.1f, 1.0f };
vec4_t colorWarsowPurpleBright = { 0.66f, 0.66f, 1.0f, 1.0f };
vec4_t colorWarsowPurple = { 0.3843f, 0.2902f, 0.5843f, 1.0f };

/*
===============================================================================

STRINGS DRAWING

===============================================================================
*/

//=============
//UI_FillRect
//=============
void UI_FillRect( int x, int y, int w, int h, vec4_t color ) {
	trap_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, color, uis.whiteShader );
}

//=============
//UI_StringWidth
//=============
int UI_StringWidth( char *s, struct mufont_s *font )
{
	if( !font )
		font = uis.fontSystemSmall;
	return trap_SCR_strWidth( s, font, 0 );
}

//=============
//UI_StringHeight
//=============
int UI_StringHeight( struct mufont_s *font )
{
	if( !font )
		font = uis.fontSystemSmall;
	return trap_SCR_strHeight( font );
}

//=============
//UISCR_HorizontalAlignOffset
//=============
int UISCR_HorizontalAlignOffset( int align, int width )
{
	int nx = 0;

	if( align % 3 == 0 ) // left
		nx = 0;
	if( align % 3 == 1 ) // center
		nx = -(width / 2);
	if( align % 3 == 2 ) // right
		nx = -width;

	return nx;
}

//=============
//UISCR_VerticalAlignOffset
//=============
int UISCR_VerticalAlignOffset( int align, int height )
{
	int ny = 0;

	if( align / 3 == 0 ) // top
		ny = 0;
	else if( align / 3 == 1 ) // middle
		ny = -(height / 2);
	else if( align / 3 == 2 ) // bottom
		ny = -height;

	return ny;
}

//=============
//UI_DrawStringHigh
//This string is highlighted by the mouse
//=============
void UI_DrawStringHigh( int x, int y, int align, char *str, int maxlen, struct mufont_s *font, vec4_t color )
{
	int shadowoffset = 1 + (trap_SCR_strHeight(font) >= trap_SCR_strHeight(uis.fontSystemBig));
	if( !font )
		font = uis.fontSystemSmall;

	if( maxlen > 0 ) {
		trap_SCR_DrawStringLen( x+shadowoffset, y+shadowoffset, align, COM_RemoveColorTokens(str), maxlen, font, colorBlack );
		trap_SCR_DrawStringLen( x, y, align, COM_RemoveColorTokens(str), maxlen, font, UI_COLOR_HIGHLIGHT );
	} else {
		trap_SCR_DrawString( x+shadowoffset, y+shadowoffset, align, str, font, colorBlack );
		trap_SCR_DrawString( x, y, align, str, font, UI_COLOR_HIGHLIGHT );
	}
}

//=============
//UI_DrawString
//=============
void UI_DrawString( int x, int y, int align, char *str, int maxlen, struct mufont_s *font, vec4_t color )
{
	if( !font )
		font = uis.fontSystemSmall;

	if( maxlen > 0 )
		trap_SCR_DrawStringLen( x, y, align, str, maxlen, font, color );
	else
		trap_SCR_DrawString( x, y, align, str, font, color );
}



//=================================================================
//
//		MENUITEMS
//
//=================================================================

//=================================================================
// ACTION
//=================================================================

static void	Action_UpdateBox( menucommon_t *a )
{
	a->mins[0] = a->x + a->parent->x + UISCR_HorizontalAlignOffset( a->align, UI_StringWidth( a->title, a->font ) );
	if( a->width > 0 ) {
		a->maxs[0] = a->mins[0] + a->width;
	} else {
		a->maxs[0] = a->mins[0] + UI_StringWidth( a->title, a->font );
	}

	a->mins[1] = a->y + a->parent->y + UISCR_VerticalAlignOffset( a->align, UI_StringHeight( a->font ) );
	if( a->height > 0 ) {
		a->maxs[1] = a->mins[1] + a->height;
	} else {
		a->maxs[1] = a->mins[1] + UI_StringHeight( a->font );
	}
}

static void	Action_Init( menucommon_t *a )
{
	Action_UpdateBox( a );
}

void Action_DoEnter( menucommon_t *menuitem )
{
	if( menuitem->callback )
		menuitem->callback( menuitem );
}

void Action_Draw( menucommon_t *menuitem )
{
	int x, y;
	int maxlen = 0;

	x = menuitem->x + menuitem->parent->x;
	y = menuitem->y + menuitem->parent->y;

	// update box for string size
	Action_UpdateBox( menuitem );

	if( menuitem->ownerdraw )
		menuitem->ownerdraw( menuitem );

	if( menuitem->width > 0 ) {
		maxlen = trap_SCR_StrlenForWidth( COM_RemoveColorTokens(menuitem->title), menuitem->font, menuitem->width );
	}

	if( Menu_ItemAtCursor( menuitem->parent ) == menuitem ) {
		UI_DrawStringHigh( x, y, menuitem->align, menuitem->title, maxlen, menuitem->font, colorWhite );
	} else {
		UI_DrawString( x, y, menuitem->align, menuitem->title, maxlen, menuitem->font, UI_COLOR_LIVETEXT );
	}
}

//=================================================================
// FIELD
//=================================================================
static void Field_ResetCursor( menucommon_t *f ) 
{
	menufield_t *itemlocal;
	itemlocal = (menufield_t*)f->itemlocal;

	itemlocal->cursor = strlen(itemlocal->buffer);
	if( itemlocal->cursor > itemlocal->length )
		itemlocal->cursor = itemlocal->length;
}

static void Field_SetupBox( menucommon_t *f ) {
	menufield_t *itemlocal;
	itemlocal = (menufield_t*)f->itemlocal;

	f->mins[0] = f->x + f->parent->x + 16;
	f->maxs[0] = f->mins[0] + itemlocal->width;

	f->mins[1] = f->y + f->parent->y;
	f->maxs[1] = f->mins[1] + UI_StringHeight( f->font );
}

static void Field_Init( menucommon_t *f )
{
	Field_SetupBox( f );
}

qboolean Field_DoEnter( menucommon_t *f )
{
	if( f->callback )
	{
		f->callback( f );
		return qtrue;
	}
	return qfalse;
}

void Field_Draw( menucommon_t *f )
{
	int x, y;
	char tempbuffer[128] = "";
	menufield_t	*itemlocal;

	itemlocal = (menufield_t*)f->itemlocal;
	if( !itemlocal )
		return;

	Field_SetupBox( f );  // update box

	x = f->x + f->parent->x + LCOLUMN_OFFSET;
	y = f->y + f->parent->y;

	if( f->title[0] ) {
		UI_DrawString( x, y, f->align, f->title, 0, f->font, UI_COLOR_DEADTEXT );
	}

	x = f->x + f->parent->x + 16;
	y = f->y + f->parent->y;

	{
		float color[4] = { 0.5, 0.5, 0.5, 0.5 };
		UI_FillRect( x, y, itemlocal->width, trap_SCR_strHeight(f->font), color );
	}

	if( Menu_ItemAtCursor( f->parent ) == f )
	{
		int offset, xcursor;
		char *str;

		Q_strncpyz( tempbuffer, itemlocal->buffer, sizeof(tempbuffer) );
		str = tempbuffer;
		while( *str && ((int)trap_SCR_strWidth(str, f->font, 0) > itemlocal->width - 16) ) {
			str++;
		}

		offset = str - tempbuffer;
		if( itemlocal->cursor < offset )
			Field_ResetCursor( f ); // force the cursor to be always at the end of the string

		UI_DrawString( x, y, ALIGN_LEFT_TOP, str, 0, f->font, UI_COLOR_LIVETEXT );
		//draw cursor
		if( ( int ) ( uis.time / 250 ) & 1 ) {
			xcursor = trap_SCR_strWidth(str,f->font,itemlocal->cursor);
			UI_DrawString( x+xcursor, y, ALIGN_LEFT_TOP, "_", 0, f->font, UI_COLOR_LIVETEXT );
		}
	}
	else
	{
		x = f->x + f->parent->x + 16;
		y = f->y + f->parent->y;

		Q_strncpyz( tempbuffer, itemlocal->buffer, sizeof( tempbuffer ) );
		UI_DrawString( x, y, ALIGN_LEFT_TOP, tempbuffer, 0, f->font, colorLtGrey );
	}
}

qboolean Field_Key( menucommon_t *f, int key )
{
	menufield_t *itemlocal;
	itemlocal = (menufield_t*)f->itemlocal;
	if( !itemlocal )
		return qfalse;

	/*
	** support pasting from the clipboard
	*/
	if( ( toupper( key ) == 'V' && trap_Key_IsDown (K_CTRL) ) ||
		 ( ( ( key == K_INS ) || ( key == KP_INS ) ) && trap_Key_IsDown (K_SHIFT) ) )
	{
		char cbd[64];

		trap_CL_GetClipboardData ( cbd, sizeof(cbd) );

		if ( cbd[0] )
		{
			strtok( cbd, "\n\r\b" );

			Q_strncpyz( itemlocal->buffer, cbd, sizeof(itemlocal->buffer) );
			Field_ResetCursor( f );
			//Field_DoEnter( f ); // exec callback
		}
		return qtrue;
	}

	switch ( key )
	{
	case K_LEFTARROW:
	case K_BACKSPACE:
		if ( itemlocal->cursor > 0 )
		{
			memmove( &itemlocal->buffer[itemlocal->cursor-1], &itemlocal->buffer[itemlocal->cursor], strlen( &itemlocal->buffer[itemlocal->cursor] ) + 1 );
			Field_ResetCursor( f );
			//Field_DoEnter( f ); // exec callback
		}
		return qtrue;

	case KP_DEL:
	case K_DEL:
		{
			memmove( &itemlocal->buffer[itemlocal->cursor], &itemlocal->buffer[itemlocal->cursor+1], strlen( &itemlocal->buffer[itemlocal->cursor+1] ) + 1 );
			Field_ResetCursor( f );
			//Field_DoEnter( f ); // exec callback
		}
		return qtrue;

	case KP_HOME:
	case KP_UPARROW:
	case KP_PGUP:
	case KP_LEFTARROW:
	case KP_5:
	case KP_RIGHTARROW:
	case KP_END:
	case KP_DOWNARROW:
	case KP_PGDN:
		return qtrue; // ignore keypad stuff, so it can be used for writing numbers (little ugly)
	}

	return qfalse;
}

qboolean Field_CharEvent( menucommon_t *f, int key )
{
	menufield_t *itemlocal;
	itemlocal = (menufield_t*)f->itemlocal;
	if( !itemlocal )
		return qfalse;

	if (key < 32 || key > 126)
		return qfalse;	// non printable

	// jalfixme: reimplement numbers only option?
	//if ( !isdigit( key ) && ( f->flags & QMF_NUMBERSONLY ) )
	//	return qfalse;

	if( itemlocal->cursor < itemlocal->length )
	{
		itemlocal->buffer[itemlocal->cursor++] = key;
		itemlocal->buffer[itemlocal->cursor] = 0;
		Field_SetupBox( f );
		//Field_DoEnter( f ); // exec callback
	}

	return qtrue;
}

//=================================================================
// SEPARATOR
//=================================================================
static void Separator_Init( menucommon_t *s )
{
}

void Separator_Draw( menucommon_t *s )
{
	int x, y;
	int maxlen = 0;

	if( !s->title[0] ) {
		return;
	}

	x = s->x + s->parent->x;
	y = s->y + s->parent->y;

	if( s->width > 0 ) {
		maxlen = trap_SCR_StrlenForWidth( s->title, s->font, s->width );
	}
	UI_DrawString( x, y, s->align, s->title, maxlen, s->font, UI_COLOR_DEADTEXT );
}

//=================================================================
// SLIDER
//=================================================================
#define SCROLLBAR_PIC_SIZE 16

static void Slider_Init( menucommon_t *s )
{
	s->align = ALIGN_LEFT_TOP;

	s->mins[0] = s->x + s->parent->x;
	s->maxs[0] = s->mins[0] + s->width * SCROLLBAR_PIC_SIZE + SCROLLBAR_PIC_SIZE;

	s->mins[1] = s->y + s->parent->y;
	s->maxs[1] = s->mins[1] + SCROLLBAR_PIC_SIZE;
}

void Slider_Draw( menucommon_t *s )
{
	int	i;
	int x, y;
	vec4_t	buttoncolor;
	int cursorxpos;

	Slider_Init( s ); //update box size

	x = s->x + s->parent->x;
	y = s->y + s->parent->y;

	//draw slider tittle, if any
	if( s->title[0] ) {
		UI_DrawString( x + LCOLUMN_OFFSET, y, ALIGN_RIGHT_TOP, s->title, 0, s->font, UI_COLOR_DEADTEXT );
	}

	if( s->maxvalue > s->minvalue )
	{
		s->range = (float)( s->curvalue - s->minvalue ); // reserve space for the buttons
		s->range /= (float)( s->maxvalue - s->minvalue );
		clamp( s->range, 0, 1 );
	}
	else
		s->range = 0;

	trap_R_DrawStretchPic( x, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_1" ) );

	for( i = 1; i < s->width-1 ; i++ )
		trap_R_DrawStretchPic( x + i*SCROLLBAR_PIC_SIZE, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_2" ) );

	trap_R_DrawStretchPic( x + s->width*SCROLLBAR_PIC_SIZE - SCROLLBAR_PIC_SIZE, y , SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_3" ) );

	if( Menu_ItemAtCursor( s->parent ) == s ) {
		Vector4Copy( UI_COLOR_HIGHLIGHT, buttoncolor );
	} else {
		Vector4Copy( colorWhite, buttoncolor );
	}
	cursorxpos = x + SCROLLBAR_PIC_SIZE // start with offset for the button
		+ ((s->width-3) * SCROLLBAR_PIC_SIZE * s->range);

	trap_R_DrawStretchPic( cursorxpos, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		buttoncolor, trap_R_RegisterPic( "gfx/ui/slidebar_4" ) );
}

void Slider_DoSlide( menucommon_t *s, int dir )
{
	int min, max;
	float newrange;
	float value;

	//empty or erroneous
	if( s->width <= 0 )
		return;

	min = s->x + s->parent->x + SCROLLBAR_PIC_SIZE; // extra pics reserved for the buttons
	max = min + ((s->width-1) * SCROLLBAR_PIC_SIZE) - SCROLLBAR_PIC_SIZE;
	//empty or erroneous
	if( max < min )
		UI_Error( "Invalid slidebar range: 'min < max'" );

	// see if the mouse is touching the step buttons
	if( uis.cursorX < min ) {
		s->curvalue--;
	} else if( uis.cursorX > max ) {
		s->curvalue++;
	} else {
		newrange = uis.cursorX - min;
		newrange /= (float)(max - min);

		clamp( newrange, 0.0, 1.0 );

		value = newrange * (float)(s->maxvalue - s->minvalue) + s->minvalue;
		if( value - (int)value > 0.5 )
			value = (int)value + 1;
		else
			value = (int)value;

		s->curvalue = (int)value;
	}

	if( s->curvalue > s->maxvalue )
		s->curvalue = s->maxvalue;
	else if( s->curvalue < s->minvalue )
		s->curvalue = s->minvalue;

	if( s->callback )
		s->callback( s );
}

//=================================================================
// SCROLLBAR
//=================================================================

static void Scrollbar_Init( menucommon_t *s )
{
	s->align = ALIGN_LEFT_TOP;

	s->mins[0] = s->x + s->parent->x;
	s->maxs[0] = s->mins[0] + SCROLLBAR_PIC_SIZE;

	s->mins[1] = s->y + s->parent->y;
	s->maxs[1] = s->mins[1] + s->height * SCROLLBAR_PIC_SIZE + SCROLLBAR_PIC_SIZE;
}

void Scrollbar_DoSlide( menucommon_t *s, int dir )
{
	int min, max;
	float newrange;
	float value;

	//empty or erroneous
	if( s->height <= 0 )
		return;

	min = s->y + s->parent->y + SCROLLBAR_PIC_SIZE; // extra pics reserved for the buttons
	max = min + ((s->height-1) * SCROLLBAR_PIC_SIZE) - SCROLLBAR_PIC_SIZE;
	//empty or erroneous
	if( max < min )
		UI_Error( "Invalid scrollbar range: 'min < max'" );

	// see if the mouse is touching the step buttons
	if( uis.cursorY < min ) {
		s->curvalue--;
	} else if( uis.cursorY > max ) {
		s->curvalue++;
	} else {
		newrange = uis.cursorY - min;
		newrange /= (float)(max - min);

		clamp( newrange, 0.0, 1.0 );

		value = newrange * (float)(s->maxvalue - s->minvalue) + s->minvalue;
		if( value - (int)value > 0.5 )
			value = (int)value + 1;
		else
			value = (int)value;

		s->curvalue = (int)value;
	}

	if( s->curvalue > s->maxvalue )
		s->curvalue = s->maxvalue;
	else if( s->curvalue < s->minvalue )
		s->curvalue = s->minvalue;

	if( s->callback )
		s->callback( s );
}

void Scrollbar_Draw( menucommon_t *s )
{
	int	i;
	int x, y;
	vec4_t	buttoncolor;
	int cursorypos;

	Scrollbar_Init( s ); //update box size

	x = s->x + s->parent->x;
	y = s->y + s->parent->y;

	//draw scrollbar tittle, if any
	if( s->title[0] ) {
		UI_DrawString( x, y-trap_SCR_strHeight(s->font), s->align, s->title, 0, s->font, UI_COLOR_DEADTEXT );
	}

	if( s->maxvalue > s->minvalue )
	{
		s->range = (float)( s->curvalue - s->minvalue ); // reserve space for the buttons
		s->range /= (float)( s->maxvalue - s->minvalue );
		clamp( s->range, 0, 1 );
	}
	else
		s->range = 0;

	trap_R_DrawStretchPic( x, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_1" ) );

	for( i = 1; i < s->height-1 ; i++ )
		trap_R_DrawStretchPic( x, y + i*SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_2" ) );

	trap_R_DrawStretchPic( x, y + s->height*SCROLLBAR_PIC_SIZE - SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_3" ) );

	if( Menu_ItemAtCursor( s->parent ) == s ) {
		Vector4Copy( UI_COLOR_HIGHLIGHT, buttoncolor );
	} else {
		Vector4Copy( colorWhite, buttoncolor );
	}
	cursorypos = y + SCROLLBAR_PIC_SIZE // start with offset for the button
		+ ((s->height-3) * SCROLLBAR_PIC_SIZE * s->range);

	trap_R_DrawStretchPic( x, cursorypos, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1,
		buttoncolor, trap_R_RegisterPic( "gfx/ui/scrollbar_4" ) );
}

//=================================================================
// SPINCONTROL
//=================================================================
static void SpinControl_Init( menucommon_t *s )
{
	char buffer[100] = { 0 };
	int ysize, xsize, spacing, len;
	char **n;

	n = s->itemnames;

	if ( !n )
		return;

	s->mins[0] = s->x + s->parent->x + RCOLUMN_OFFSET;
	s->mins[1] = s->y + s->parent->y;

	ysize = UI_StringHeight( s->font );
	spacing = UI_StringHeight( s->font );

	xsize = 0;

	while( *n )
	{
		if( !strchr( *n, '\n' ) )
		{
			len = UI_StringWidth( *n, s->font );
			xsize = max( xsize, len );
		}
		else
		{

			Q_strncpyz( buffer, *n, sizeof(buffer) );
			*strchr( buffer, '\n' ) = 0;
			len = UI_StringWidth ( buffer, s->font );
			xsize = max ( xsize, len );

			ysize = ysize + spacing;
			Q_strncpyz( buffer, strchr( *n, '\n' ) + 1, sizeof(buffer) );
			len = UI_StringWidth( buffer, s->font );
			xsize = max( xsize, len );
		}

		*n++;
	}

	if( s->align == ALIGN_CENTER_BOTTOM || s->align == ALIGN_CENTER_TOP || s->align == ALIGN_CENTER_MIDDLE ) {
		s->mins[0] -= xsize / 2;
	}

	s->maxs[0] = s->mins[0] + xsize;
	s->maxs[1] = s->mins[1] + ysize;
}

/*
void SpinControl_DoEnter( menulist_s *s )
{
s->curvalue++;
if ( s->itemnames[s->curvalue] == 0 )
s->curvalue = 0;

if ( s->generic.callback )
s->generic.callback( s );
}
*/

void SpinControl_DoSlide( menucommon_t *s, int dir )
{
	s->curvalue += dir;

	if( s->curvalue < 0 )
		s->curvalue = s->maxvalue;
	else if( s->itemnames[s->curvalue] == 0 )
		s->curvalue=0;

	if( s->callback )
		s->callback( s );
	return;
}

void SpinControl_Draw( menucommon_t *s )
{
	char buffer[100];
	int x, y;
	qboolean font_highlighted = qfalse;

	SpinControl_Init( s ); //update box size

	x = s->x + s->parent->x + LCOLUMN_OFFSET;
	y = s->y + s->parent->y;

	if( s->title[0] ) {
		UI_DrawString( x, y, s->align, s->title, 0, s->font, UI_COLOR_DEADTEXT );
	}

	if( Menu_ItemAtCursor( s->parent ) == s )
	{
		font_highlighted = qtrue;
	}

	x = s->x + s->parent->x + RCOLUMN_OFFSET;
	y = s->y + s->parent->y;

	if( !strchr( s->itemnames[s->curvalue], '\n' ) )
	{
		if( font_highlighted )
			UI_DrawStringHigh( x, y, ALIGN_LEFT_TOP, ( char *)s->itemnames[s->curvalue], 0, s->font, colorWhite );
		else
			UI_DrawString( x, y, ALIGN_LEFT_TOP, ( char *)s->itemnames[s->curvalue], 0, s->font, UI_COLOR_LIVETEXT );
	}
	else
	{
		Q_strncpyz( buffer, s->itemnames[s->curvalue], sizeof(buffer) );
		*strchr( buffer, '\n' ) = 0;
		if( font_highlighted )
			UI_DrawStringHigh( x, y, s->align, buffer, 0, s->font, colorWhite );
		else
			UI_DrawString( x, y, s->align, buffer, 0, s->font, UI_COLOR_LIVETEXT );

		Q_strncpyz( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1, sizeof(buffer) );
		if( font_highlighted )
			UI_DrawStringHigh( x, y + UI_StringHeight(s->font), ALIGN_LEFT_TOP, buffer, 0, s->font, colorWhite );
		else
			UI_DrawString( x, y + UI_StringHeight(s->font), ALIGN_LEFT_TOP, buffer, 0, s->font, UI_COLOR_LIVETEXT );
	}
}

//=================================================================
//
// MENUITEM ASSOCIATED ELEMENTS DRAWING (Generic to all types)
//
//=================================================================
void MenuItem_DrawPict( menucommon_t *menuitem )
{
	int x, y;
	x = menuitem->x + menuitem->parent->x;
	y = menuitem->y + menuitem->parent->y;

	x += menuitem->pict.xoffset;
	y += menuitem->pict.yoffset;

	if( Menu_ItemAtCursor( menuitem->parent ) == menuitem ) {
		if( menuitem->pict.shaderHigh ) {
			trap_R_DrawStretchPic( x, y, menuitem->pict.width, menuitem->pict.height, 0, 0, 1, 1,
				menuitem->pict.colorHigh, menuitem->pict.shaderHigh );

			return;
		}
	}

	if( menuitem->pict.shader ) {
		trap_R_DrawStretchPic( x, y, menuitem->pict.width, menuitem->pict.height, 0, 0, 1, 1,
			menuitem->pict.color, menuitem->pict.shader );
	}
}

void Menu_DrawWindowedBackground( menuframework_s *menu ) {
// it works, but needs to be improved
	int i;
	vec2_t mins, maxs;
	static vec4_t colorback = { .0f, .0f, .0f, 0.6f };

	mins[0] = uis.vidWidth;
	mins[1] = uis.vidHeight;
	maxs[0] = maxs[1] = 0;
	for( i = 0; i<menu->nitems; i++ ) {
		if( menu->items[i]->mins[0] && menu->items[i]->mins[0] < mins[0] )
			mins[0] = menu->items[i]->mins[0];
		if( menu->items[i]->mins[1] && menu->items[i]->mins[1] < mins[1] )
			mins[1] = menu->items[i]->mins[1];
		if( menu->items[i]->maxs[0] && menu->items[i]->maxs[0] > maxs[0] )
			maxs[0] = menu->items[i]->maxs[0];
		if( menu->items[i]->maxs[1] && menu->items[i]->maxs[1] > maxs[1] )
			maxs[1] = menu->items[i]->maxs[1];
	}
	//extend it
	mins[0] -= 16;
	mins[1] -= 16;
	maxs[0] += 16;
	maxs[1] += 16;

	// add a background to the menu
	//trap_R_DrawStretchPic( mins[0], mins[1], maxs[0]-mins[0], maxs[1]-mins[1], 
	//	0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/novideoback") );

	trap_R_DrawStretchPic( 0, mins[1], uis.vidWidth, maxs[1]-mins[1], 
		0, 0, 1, 1, colorback, uis.whiteShader );
}

//=================================================================
//
//		MENU
//
//=================================================================

void Menu_AddItem( menuframework_s *menu, void *item )
{
	int i;
	qboolean found = qfalse;

	if( menu->nitems == 0 )
		menu->nslots = 0;

	for( i = 0; i < menu->nitems; i++ ) {
		if( menu->items[i] == item ) {
			found = qtrue;
			break;
		}
	}

	if( !found && menu->nitems < MAXMENUITEMS )
	{
		menu->items[menu->nitems] = item;
		menu->items[menu->nitems]->parent = menu;
		menu->nitems++;
	}

	menu->nslots = Menu_TallySlots( menu );
}

/*
** Menu_AdjustCursor
**
** This function takes the given menu, the direction, and attempts
** to adjust the menu's cursor so that it's at the next available
** slot.
*/
void Menu_AdjustCursor( menuframework_s *m, int dir )
{
	menucommon_t *citem;

	// see if it's in a valid spot
	if( m->cursor >= 0 && m->cursor < m->nitems )
	{
		if( ( citem = Menu_ItemAtCursor( m ) ) != 0 )
		{
			if( citem->type != MTYPE_SEPARATOR )
				return;
		}
	}

	// it's not in a valid spot, so crawl in the direction indicated until we
	// find a valid spot
	if( dir == 1 )
	{
		while( 1 )
		{
			citem = Menu_ItemAtCursor( m );
			if( citem )
				if( citem->type != MTYPE_SEPARATOR )
					break;
			m->cursor += dir;
			if( m->cursor >= m->nitems )
				m->cursor = 0;
		}
	}
	else
	{
		while( 1 )
		{
			citem = Menu_ItemAtCursor( m );
			if( citem )
				if( citem->type != MTYPE_SEPARATOR )
					break;
			m->cursor += dir;
			if( m->cursor < 0 )
				m->cursor = m->nitems - 1;
		}
	}
}

void Menu_Center( menuframework_s *menu )
{
	int height = 0;
	int i;
	int width = 0;

	// x center
	width = menu->items[menu->nitems-1]->x;
	for( i = 0; i<menu->nitems; i++ ) {
		if( menu->items[i]->x > width )
			width = menu->items[i]->x;
	}
	width += 10;

	menu->x = ( uis.vidWidth - width ) / 2;

	// y center
	height = menu->items[menu->nitems-1]->y;
	for( i = 0; i<menu->nitems; i++ ) {
		if( menu->items[i]->y > height )
			height = menu->items[i]->y;
	}
	height += 10;

	menu->y = ( uis.vidHeight - height ) / 2;
}

void Menu_AdjustRectangle( int *mins, int *maxs )
{
	mins[0] *= UI_WIDTHSCALE;
	maxs[0] *= UI_HEIGHTSCALE;

	mins[1] *= UI_WIDTHSCALE;
	maxs[1] *= UI_HEIGHTSCALE;
}

void Menu_Init( menuframework_s *menu )
{
	int i;

	// init items
	for( i = 0; i < menu->nitems; i++ )
	{
		switch( menu->items[i]->type )
		{
		case MTYPE_FIELD:
			Field_Init( menu->items[i] );
			break;
		case MTYPE_SLIDER:
			Slider_Init( menu->items[i] );
			break;
		case MTYPE_SPINCONTROL:
			SpinControl_Init( menu->items[i] );
			break;
		case MTYPE_ACTION:
			Action_Init( menu->items[i] );
			break;
		case MTYPE_SEPARATOR:
			Separator_Init( menu->items[i] );
			break;
		case MTYPE_SCROLLBAR:
			Scrollbar_Init( menu->items[i] );
			break;
		}

		Menu_AdjustRectangle( menu->items[i]->mins, menu->items[i]->maxs );
	}
}

void Menu_Draw( menuframework_s *menu )
{
	int i;
	menucommon_t *item;

	if( !uis.backGround )
		Menu_DrawWindowedBackground( menu );

	// draw contents
	for( i = 0; i < menu->nitems; i++ )
	{
		//draw associated picture
		MenuItem_DrawPict(  menu->items[i] );

		switch( menu->items[i]->type )
		{
		case MTYPE_FIELD:
			Field_Draw( menu->items[i] );
			break;
		case MTYPE_SLIDER:
			Slider_Draw( menu->items[i] );
			break;
		case MTYPE_SPINCONTROL:
			SpinControl_Draw( menu->items[i] );
			break;
		case MTYPE_ACTION:
			Action_Draw( menu->items[i] );
			break;
		case MTYPE_SEPARATOR:
			Separator_Draw( menu->items[i] );
			break;
		case MTYPE_SCROLLBAR:
			Scrollbar_Draw( menu->items[i] );
			break;
		}
	}

	item = Menu_ItemAtCursor( menu );

	if( item && item->cursordraw )	{
		item->cursordraw( item );
	} else if( menu->cursordraw ) {
		menu->cursordraw( menu );
	}

	if( item ) {
		if( item->statusbarfunc )
			item->statusbarfunc( item );
		else if( item->statusbar )
			Menu_DrawStatusBar( item->statusbar );
		else if( menu->statusbar )
			Menu_DrawStatusBar( menu->statusbar );
	} else if( menu->statusbar )  {
		Menu_DrawStatusBar( menu->statusbar );
	}
}

void Menu_DrawStatusBar( char *string ) {
	int len;
	struct mufont_s *font = uis.fontSystemSmall;

	UI_FillRect( 0, uis.vidHeight-trap_SCR_strHeight(font), uis.vidWidth, trap_SCR_strHeight(font), colorDkGrey );
	len = trap_SCR_StrlenForWidth( string, font, uis.vidWidth );
	trap_SCR_DrawStringLen( uis.vidWidth/2, uis.vidHeight, ALIGN_CENTER_BOTTOM, string, len, font, colorWhite );
}

menucommon_t *Menu_ItemAtCursor( menuframework_s *m )
{
	if( m->cursor < 0 || m->cursor >= m->nitems )
		return NULL;

	return m->items[m->cursor];
}

qboolean Menu_SelectItem( menuframework_s *s )
{
	menucommon_t *item = Menu_ItemAtCursor( s );

	if( item )
	{
		switch( item->type )
		{
		case MTYPE_FIELD:
			return Field_DoEnter( item ) ;
		case MTYPE_ACTION:
			Action_DoEnter( item );
			return qtrue;
		case MTYPE_SPINCONTROL:
//			SpinControl_DoEnter( item );
			return qfalse;
		}
	}
	return qfalse;
}

void Menu_SetStatusBar( menuframework_s *m, char *string )
{
	m->statusbar = string;
}

qboolean Menu_SlideItem( menuframework_s *s, int dir, int key )
{
	menucommon_t *item = Menu_ItemAtCursor( s );

	if( item )
	{
		switch( item->type )
		{
		case MTYPE_SLIDER:
			if( key == K_MOUSE1 || key == K_MOUSE2 )
				dir = 0;
			Slider_DoSlide( item, dir );
			return qtrue;
		case MTYPE_SPINCONTROL:
			SpinControl_DoSlide( item, dir );
			return qtrue;
		case MTYPE_SCROLLBAR:
			Scrollbar_DoSlide( item, dir );
			return qtrue;
		}
	}

	return qfalse;
}

int Menu_TallySlots( menuframework_s *menu )
{
	int i;
	int total = 0;

	for( i = 0; i < menu->nitems; i++ ) {
		total++;
	}

	return total;
}

//=======================================================
// scroll lists management
//=======================================================

//==================
//UI_FreeScrollItemList
//==================
void UI_FreeScrollItemList( m_itemslisthead_t *itemlist )
{
	m_listitem_t *ptr;

	while( itemlist->headNode )
	{
		ptr = itemlist->headNode;
		itemlist->headNode = ptr->pnext;
		UI_Free(ptr);
	}

	itemlist->headNode = NULL;
	itemlist->numItems = 0;
}

//==================
//UI_FindItemInScrollListWithId
//==================
m_listitem_t *UI_FindItemInScrollListWithId( m_itemslisthead_t *itemlist, int itemid ) {
	m_listitem_t *item;

	if( !itemlist->headNode )
		return NULL;

	item = itemlist->headNode;
	while( item ) {
		if( item->id == itemid )
			return item;
		item = item->pnext;
	}

	return NULL;
}

//==================
//UI_AddItemToScrollList
//==================
void UI_AddItemToScrollList( m_itemslisthead_t *itemlist, char *name, void *data )
{
	m_listitem_t *newitem, *checkitem;

	//check for the address being already in the list
	checkitem = itemlist->headNode;
	while( checkitem ) 
	{
		if( !Q_stricmp( name, checkitem->name ) )
			return;
		checkitem = checkitem->pnext;
	}

	newitem = (m_listitem_t*)UI_Malloc( sizeof(m_listitem_t) );
	Q_strncpyz( newitem->name, name, sizeof(newitem->name) );
	newitem->pnext = itemlist->headNode;
	itemlist->headNode = newitem;
	newitem->id = itemlist->numItems;
	newitem->data = data;

	// update item names array
	itemlist->item_names[itemlist->numItems] = UI_CopyString( newitem->name );
	itemlist->item_names[itemlist->numItems+1] = NULL;

	itemlist->numItems++;
}

//=======================================================
// menu actions registration
//=======================================================
menucommon_t *ui_menuitems_headnode;

menucommon_t *UI_MenuItemByName( char *name ) {
	menucommon_t	*menuitem;

	if( !name )
		return NULL;

	for( menuitem = ui_menuitems_headnode; menuitem; menuitem = menuitem->next ) {
		if( !Q_stricmp( menuitem->name, name ) )
			return menuitem;
	}

	return NULL;
}

char *UI_GetMenuitemFieldBuffer( menucommon_t *menuitem ) {
	menufield_t *field;

	if( !menuitem || menuitem->type != MTYPE_FIELD )
		return NULL;
	if( !menuitem->itemlocal )
		return NULL;

	field = (menufield_t*)menuitem->itemlocal;
	return field->buffer;
}

menucommon_t *UI_RegisterMenuItem( const char *name, int type )
{
	menucommon_t	*menuitem;
	size_t			size;

	if( !name )
		return NULL;

	for( menuitem = ui_menuitems_headnode; menuitem; menuitem = menuitem->next ) {
		if( !Q_stricmp( menuitem->name, name ) )
			return menuitem;
	}

	switch( type ) {
		case MTYPE_SLIDER:
			size = 0;
			break;
		case MTYPE_ACTION:
			size = 0;
			break;
		case MTYPE_SPINCONTROL:
			size = 0;
			break;
		case MTYPE_SEPARATOR:
			size = 0;
			break;
		case MTYPE_FIELD:
			size = sizeof( menufield_t );
			break;
		case MTYPE_SCROLLBAR:
			size = 0;
			break;
		default:
			size = 0;
			break;
	}

	// allocate one huge array to hold our data
	menuitem = UI_Malloc( sizeof( menucommon_t ) );
	if( size )
		menuitem->itemlocal = UI_Malloc( size );
	menuitem->name = UI_CopyString( name );
	menuitem->next = ui_menuitems_headnode;
	ui_menuitems_headnode = menuitem;

	return menuitem;
}

menucommon_t *UI_InitMenuItem( const char *name, char *tittle, int x, int y, int type, int align, struct mufont_s *font, void *callback )
{
	menucommon_t *menuitem;

	if( !name )
		return NULL;

	menuitem = UI_RegisterMenuItem( name, type );
	if( !menuitem )
		return NULL;

	menuitem->type = type;
	menuitem->align = align;
	menuitem->x = x;
	menuitem->y = y;
	menuitem->font = font;
	menuitem->callback = callback;
	if( tittle )
		Q_strncpyz( menuitem->title, tittle, MAX_STRING_CHARS );
	else
		menuitem->title[0] = 0;

	// clear up associated picture trash
	menuitem->pict.shader = NULL;
	menuitem->pict.shaderHigh = NULL;
	Vector4Copy( colorWhite, menuitem->pict.color );
	Vector4Copy( colorWhite, menuitem->pict.colorHigh );

	return menuitem;
}

menucommon_t *UI_SetupSlider( menucommon_t *menuitem, int width, int curvalue, int minvalue, int maxvalue )
{
	if( !menuitem  ) 
		return NULL;

	menuitem->curvalue = curvalue;
	menuitem->minvalue = minvalue;
	menuitem->maxvalue = maxvalue;
	clamp( menuitem->curvalue, minvalue, maxvalue );
	menuitem->width = width;
	if( menuitem->width < 3 )
		menuitem->width = 3;

	return menuitem;
}

menucommon_t *UI_SetupScrollbar( menucommon_t *menuitem, int height, int curvalue, int minvalue, int maxvalue )
{
	if( !menuitem )
		return NULL;

	menuitem->minvalue = minvalue;
	menuitem->maxvalue = maxvalue;
	menuitem->curvalue = curvalue;
	clamp( menuitem->curvalue, minvalue, maxvalue );
	menuitem->height = height;
	if( menuitem->height < 3 )
		menuitem->height = 3;
	return menuitem;
}

menucommon_t *UI_SetupSpinControl( menucommon_t *menuitem, char **item_names, int curvalue )
{
	int	numitems;

	if( !menuitem || !item_names ) 
		return NULL;

	//count item names
	numitems = 0;
	while( item_names[numitems] )
		numitems++;

	menuitem->itemnames = item_names;
	menuitem->curvalue = curvalue;
	menuitem->minvalue = 0;
	menuitem->maxvalue = numitems-1;
	clamp( menuitem->curvalue, menuitem->minvalue, menuitem->maxvalue );

	return menuitem;
}

menufield_t *UI_SetupField( menucommon_t *menuitem, char *string, size_t length, int width )
{
	menufield_t *field;

	if( !menuitem ) 
		return NULL;

	field = menuitem->itemlocal;

	field->length = length;
	if( length < 1 )
		length = 1;

	if( width >= ( 2*(int)trap_SCR_strWidth( "_", menuitem->font, 0) ) )
		field->width = width;
	else
		field->width = (length+1) * trap_SCR_strWidth( "_", menuitem->font, 0);

	if( string ) {
		Q_strncpyz( field->buffer, string, sizeof(field->buffer) );
		field->cursor = strlen( field->buffer );
	} else {
		memset( field->buffer, 0, sizeof( field->buffer ) );
		field->cursor = 0;
	}

	return field;
}



