#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <commdlg.h>
#include <math.h>
#include "i18n.h"
#include "mswint.h"

/*
 *****************************************************************************
 *
 * Choice Boxes
 *
 *****************************************************************************
 */

#define CHOICE_HEIGHT (17)
#define CHOICE_MIN_WIDTH (25)

static XWNDPROC oldChoiceItemProc = NULL;
static XWNDPROC newChoiceItemProc;

typedef struct {
	WOBJ_COMMON
	wChoice_p owner;
} wChoiceItem_t, * wChoiceItem_p;

struct wChoice_t {
	WOBJ_COMMON
	const char * const * labels;
	wChoiceItem_p *buttList;
	long *valueP;
	long oldVal;
	wChoiceCallBack_p action;
	HWND hBorder;
};

static FARPROC oldChoiceProc;

void wRadioSetValue(
        wChoice_p bc,
        long val )
{
	const char * const * labels;
	long cnt;
	wChoiceItem_p * butts;

	butts = (wChoiceItem_p*)bc->buttList;
	for (labels = bc->labels, cnt=0; *labels; labels++, cnt++, butts++ )
		SendMessage( (*butts)->hWnd, BM_SETCHECK,
		             (WPARAM)((val==cnt)?1:0), (LPARAM)0 );
	bc->oldVal = val;
	if (bc->valueP) {
		*bc->valueP = val;
	}
}

long wRadioGetValue(
        wChoice_p bc )
{
	return bc->oldVal;
}



void wToggleSetValue(
        wChoice_p bc,
        long val )
{
	const char * const * labels;
	long cnt;
	wChoiceItem_p * butts;

	butts = (wChoiceItem_p*)bc->buttList;
	for (labels = bc->labels, cnt=0; *labels; labels++, cnt++, butts++ )
		SendMessage( (*butts)->hWnd, BM_SETCHECK,
		             (WPARAM)((val & (1L<<cnt)) != 0), (LPARAM)0 );
	bc->oldVal = val;
	if (bc->valueP) {
		*bc->valueP = val;
	}
}


long wToggleGetValue(
        wChoice_p bc )
{
	return bc->oldVal;
}


static void choiceSetBusy(
        wControl_p b,
        BOOL_T busy)
{
	wChoiceItem_p * butts;
	wChoice_p bc = (wChoice_p)b;

	for (butts = (wChoiceItem_p*)bc->buttList; *butts; butts++ ) {
		EnableWindow( (*butts)->hWnd, !(BOOL)busy );
	}
}

static void choiceShow(
        wControl_p b,
        BOOL_T show)
{
	wChoice_p bc = (wChoice_p)b;
	wChoiceItem_p * butts;

	if ((bc->option & BC_NOBORDER)==0) {
		ShowWindow( bc->hBorder, show?SW_SHOW:SW_HIDE );
	}

	for (butts = (wChoiceItem_p*)bc->buttList; *butts; butts++ ) {
		ShowWindow( (*butts)->hWnd, show?SW_SHOW:SW_HIDE );
	}
}

static void choiceSetPos(
        wControl_p b,
        wWinPix_t x,
        wWinPix_t y )
{
	wChoice_p bc = (wChoice_p)b;
	wChoiceItem_p * butts;
	wWinPix_t dx, dy;

	dx = x - bc->x;
	dy = y - bc->y;
	if ((bc->option & BC_NOBORDER)==0)
		SetWindowPos( bc->hBorder, HWND_TOP, x, y, CW_USEDEFAULT, CW_USEDEFAULT,
		              SWP_NOSIZE|SWP_NOZORDER );

	for (butts = (wChoiceItem_p*)bc->buttList; *butts; butts++ ) {
		(*butts)->x += dx;
		(*butts)->y += dy;
		SetWindowPos( (*butts)->hWnd, HWND_TOP,
		              (*butts)->x, (*butts)->y,
		              CW_USEDEFAULT, CW_USEDEFAULT,
		              SWP_NOSIZE|SWP_NOZORDER );
	}
	bc->x = x;
	bc->y = y;
}

LRESULT FAR PASCAL _export pushChoiceItem(
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam )
{
	/* Catch <Return> and cause focus to leave control */
	wIndex_t inx = (wIndex_t)GetWindowLongPtr( hWnd, GWL_ID );
	wControl_p b = mswMapIndex( inx );

	switch (message) {
	case WM_CHAR:
		if ( b != NULL) {
			switch( wParam ) {
			case 0x0D:
			case 0x1B:
			case 0x09:
				SetFocus( ((wControl_p)(b->parent))->hWnd );
				SendMessage( ((wControl_p)(b->parent))->hWnd, WM_CHAR,
				             wParam, lParam );
				/*SendMessage( ((wControl_p)(b->parent))->hWnd, WM_COMMAND,
						inx, MAKELONG( hWnd, EN_KILLFOCUS ) );*/
				return (LRESULT)0;
			}
		}
		break;
	}
	return CallWindowProc( oldChoiceItemProc, hWnd, message, wParam, lParam );
}

LRESULT choiceItemProc(
        wControl_p b,
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam )
{
	wChoiceItem_p me = (wChoiceItem_p)b, *rest;
	wChoice_p bc;
	int num;

	switch( message ) {

	case WM_COMMAND:
		switch (WCMD_PARAM_NOTF) {
		case BN_CLICKED:
			bc = me->owner;
			num = -1;
			for (rest = (wChoiceItem_p*)bc->buttList; *rest; rest++ ) {
				switch (bc->type) {
				case B_TOGGLE:
					num = (int)(rest-(wChoiceItem_p*)bc->buttList);
					if (*rest == me) {
						bc->oldVal ^= (1L<<num);
					}
					SendMessage( (*rest)->hWnd, BM_SETCHECK,
					             (WPARAM)((bc->oldVal & (1L<<num)) != 0), (LPARAM)0 );
					break;

				case B_RADIO:
					if (*rest != me) {
						SendMessage( (*rest)->hWnd, BM_SETCHECK, (WPARAM)0, (LPARAM)0 );
					} else {
						bc->oldVal = (long)(rest-(wChoiceItem_p*)bc->buttList);
						SendMessage( (*rest)->hWnd, BM_SETCHECK, (WPARAM)1, (LPARAM)0 );
					}
					break;
				}
			}
			if (bc->valueP) {
				*bc->valueP = bc->oldVal;
			}
			if (bc->action) {
				bc->action( bc->oldVal, bc->data );
			}
			break;

		}
		break;
	}

	return DefWindowProc( hWnd, message, wParam, lParam );
}


static callBacks_t choiceCallBacks = {
	mswRepaintLabel,
	NULL,
	NULL,
	choiceSetBusy,
	choiceShow,
	choiceSetPos
};

static callBacks_t choiceItemCallBacks = {
	NULL,
	NULL,
	choiceItemProc
};

/**
 * Creates choice buttons. This function is used to create a group of
 * radio buttons and checkboxes.
 *
 * \param type IN type of button
 * \param parent IN parent window
 * \param x, y IN position of group
 * \param helpStr IN index string to find help
 * \param labelStr IN label for group
 * \param option IN ?
 * \param labels IN labels for individual choices
 * \param valueP OUT pointer for return value
 * \param action IN ?
 * \param data IN ?
 * \return    created choice button group
 */

static wChoice_p choiceCreate(
        wType_e type,
        wWin_p	parent,
        wWinPix_t	x,
        wWinPix_t	y,
        const char	* helpStr,
        const char	* labelStr,
        long	option,
        const char * const * labels,
        long	*valueP,
        wChoiceCallBack_p action,
        void	*data )
{
	wChoice_p b;
	const char * const * lp;
	int cnt;
	wChoiceItem_p * butts;
	wWinPix_t ppx, ppy;
	int bs;
	HDC hDc;
	HWND hButt;
	size_t lab_l;
	DWORD dw;
	int w, maxW;
	int pw, ph;
	int index;
	char * helpStrCopy;
	HFONT hFont;

	b = mswAlloc( parent, type, mswStrdup(labelStr), sizeof *b, data, &index );
	mswComputePos( (wControl_p)b, x, y );
	b->option = option;
	b->valueP = valueP;
	b->action = action;
	b->labels = labels;
	b->labelY += 6;

	ppx = b->x;
	ppy = b->y;

	switch (b->type) {
	case B_TOGGLE:
		bs = BS_CHECKBOX;
		break;
	case B_RADIO:
		bs = BS_RADIOBUTTON;
		break;
	}
	for (lp = b->labels,cnt=0; *lp; lp++,cnt++ );
	butts = (wChoiceItem_p*)malloc( (cnt+1) * sizeof *butts );
	b->buttList = butts;
	b->oldVal = (b->valueP?*b->valueP:0);
	ph = pw = 2;
	maxW = 0;
	if (helpStr) {
		helpStrCopy = mswStrdup( helpStr );
	}
	for (lp = b->labels, cnt=0; *lp; lp++, cnt++, butts++ ) {
		*butts = (wChoiceItem_p)mswAlloc( parent, B_CHOICEITEM,
		                                  mswStrdup(_((char *)*lp)), sizeof( wChoiceItem_t ), data, &index );
		(*butts)->owner = b;
		(*butts)->hWnd = hButt = CreateWindow( "BUTTON", (*butts)->labelStr,
		                                       bs | WS_CHILD | WS_VISIBLE | mswGetBaseStyle(parent), b->x+pw, b->y+ph,
		                                       80, CHOICE_HEIGHT,
		                                       ((wControl_p)parent)->hWnd, (HMENU)(UINT_PTR)index, mswHInst, NULL );
		if ( hButt == (HWND)0 ) {
			mswFail( "choiceCreate button" );
			return b;
		}
		(*butts)->x = b->x+pw;
		(*butts)->y = b->y+ph;
		if (b->hWnd == 0) {
			b->hWnd = (*butts)->hWnd;
		}
		(*butts)->helpStr = helpStrCopy;

		hDc = GetDC( hButt );
		lab_l = strlen((*butts)->labelStr);

		hFont = SelectObject( hDc, mswLabelFont );
		dw = GetTextExtent( hDc, (char *)((*butts)->labelStr), (UINT)lab_l );
		SelectObject( hDc, hFont );

		w = LOWORD(dw) + CHOICE_MIN_WIDTH;

		if (w > maxW) {
			maxW = w;
		}
		SetBkMode( hDc, TRANSPARENT );
		ReleaseDC( hButt, hDc );
		if (b->option & BC_HORZ) {
			pw += w;
		} else {
			ph += CHOICE_HEIGHT;
		}
		if (!SetWindowPos( hButt, HWND_TOP, 0, 0,
		                   w, CHOICE_HEIGHT, SWP_NOMOVE|SWP_NOZORDER)) {
			mswFail("Create CHOICE: SetWindowPos");
		}
		mswChainFocus( (wControl_p)*butts );
		newChoiceItemProc = MakeProcInstance( (XWNDPROC)pushChoiceItem, mswHInst );
		oldChoiceItemProc = (XWNDPROC)GetWindowLongPtr((*butts)->hWnd, GWLP_WNDPROC);
		SetWindowLongPtr((*butts)->hWnd, GWLP_WNDPROC, (LPARAM)newChoiceItemProc);
#ifdef _OLDCODE
		oldChoiceItemProc = (XWNDPROC)GetWindowLong((*butts)->hWnd, GWL_WNDPROC);
		SetWindowLong((*butts)->hWnd, GWL_WNDPROC, (LONG)newChoiceItemProc);
#endif
		SendMessage( (*butts)->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, (LPARAM)0 );
	}
	*butts = NULL;
	switch (b->type) {
	case B_TOGGLE:
		wToggleSetValue( b, (b->valueP?*b->valueP:0L) );
		break;
	case B_RADIO:
		wRadioSetValue( b, (b->valueP?*b->valueP:0L) );
		break;
	}
	if (b->option & BC_HORZ) {
		ph = CHOICE_HEIGHT;
	} else {
		pw = maxW;
	}
	pw += 4; ph += 4;
	b->w = pw;
	b->h = ph;

#define FRAME_STYLE		SS_ETCHEDFRAME

	if ((b->option & BC_NOBORDER)==0) {
		b->hBorder = CreateWindow( "STATIC", NULL, WS_CHILD | WS_VISIBLE | FRAME_STYLE,
		                           b->x, b->y, pw, ph, ((wControl_p)parent)->hWnd, 0, mswHInst, NULL );
	}
	mswAddButton( (wControl_p)b, TRUE, helpStr );
	mswCallBacks[ B_CHOICEITEM ] = &choiceItemCallBacks;
	mswCallBacks[ type ] = &choiceCallBacks;
	return b;
}


wChoice_p wRadioCreate(
        wWin_p	parent,
        wWinPix_t	x,
        wWinPix_t	y,
        const char	* helpStr,
        const char	* labelStr,
        long	option,
        const char * const *labels,
        long	*valueP,
        wChoiceCallBack_p action,
        void	*data )
{
	return choiceCreate( B_RADIO, parent, x, y, helpStr, labelStr,
	                     option, labels, valueP, action, data );
}

wChoice_p wToggleCreate(
        wWin_p	parent,
        wWinPix_t	x,
        wWinPix_t	y,
        const char	* helpStr,
        const char	* labelStr,
        long	option,
        const char * const *labels,
        long	*valueP,
        wChoiceCallBack_p action,
        void	*data )
{
	return choiceCreate( B_TOGGLE, parent, x, y, helpStr, labelStr,
	                     option, labels, valueP, action, data );
}
