#include "win32\win32guts.h"
#include <commdlg.h>
#ifndef _APRICOT_H_
#include "apricot.h"
#endif
#include "guts.h"
#include "File.h"
#include "Menu.h"
#include "Icon.h"
#include "Widget.h"
#include "Window.h"

#ifdef __cplusplus
extern "C" {
#endif


#define  sys (( PDrawableData)(( PComponent) self)-> sysData)->
#define  dsys( view) (( PDrawableData)(( PComponent) view)-> sysData)->
#define var (( PWidget) self)->
#define HANDLE sys handle
#define DHANDLE(x) dsys(x) handle

static void
map_tildas( WCHAR * buf, int len)
{
	int j;
	for ( j = 0; j < len; j++) {
		if ( buf[ j] == '~') {
			if ( buf[ j + 1] == '~')
				j++;
			else if ( buf[ j + 1])
				buf[ j] = '&';
			continue;
		} else if ( buf[ j] == '&') {
			memmove( buf + j + 1, buf + j, (len - j + 1) * sizeof( WCHAR));
			j++;
			len++;
			continue;
		}
	}
}


static WCHAR *
map_text_accel( PMenuItemReg i)
{
	char * c;
	int l1, l2 = 0, amps = 0;
	WCHAR *buf, *text, *accel = NULL;

	c = i-> text;
	while (*c++) if ( *c == '&') amps++;
	if ( i-> flags. utf8_text ) {
		text = alloc_utf8_to_wchar( i-> text, prima_utf8_length( i-> text, -1), &l1);
	} else {
		l1 = strlen( i-> text);
		text = alloc_ascii_to_wchar( i-> text, &l1);
	}

	if ( i-> accel ) {
		c = i-> accel;
		while (*c++) if ( *c == '&') amps++;
		if ( i-> flags. utf8_accel ) {
			accel = alloc_utf8_to_wchar( i-> accel, prima_utf8_length( i-> accel, -1), &l2);
		} else {
			l2 = strlen( i-> accel);
			accel = alloc_ascii_to_wchar( i-> accel, &l2);
		}
	}

	buf = malloc(sizeof(WCHAR) * (l1 + l2 + amps + 2));

	memcpy( buf, text, l1 * sizeof(WCHAR));
	free(text);

	if ( i->accel ) {
		buf[l1] = '\t';
		memcpy( buf + l1 + 1, accel, l2 * sizeof(WCHAR));
		free(accel);
		l2++;
	}

	buf[l1 + l2] = 0;
	map_tildas( buf, l1 + l2);

	return buf;
}

/* Convert 1-bit icons to bitmaps, icons to argb, others to pixmaps */
static HBITMAP
create_menu_bitmap( Handle bitmap )
{
	HBITMAP ret = NULL;
	PIcon i = (PIcon) bitmap;

	if ( i == NULL || i-> stage >= csDead )
		return NULL;

	if (kind_of(bitmap, CIcon)) {
		if (( i-> type & imBPP) == 1) {
			ret = image_create_bitmap( bitmap);
		} else {
			Handle dup = CImage(bitmap)->dup(bitmap);
			CIcon(dup)->set_autoMasking(dup, amNone);
			if ( i-> type != imRGB )
				CIcon(dup)->set_type(dup, imRGB);
			if ( i-> maskType != 8 )
				CIcon(dup)->set_maskType(dup, 8);
			CIcon(dup)-> premultiply_alpha(dup, NULL);
			ret = image_create_bitmap( dup);
			Object_destroy(dup);
		}
	} else {
		if (( i-> type & imBPP) == 1) {
			Handle dup = CImage(bitmap)->dup(bitmap);
			CIcon(dup)->set_type(dup, 4);
			ret = image_create_bitmap( bitmap);
			Object_destroy(dup);
		}
	}

	if ( ret == NULL )
		ret = image_create_bitmap( bitmap);

	return ret;
}

typedef struct {
	HMENU menu;
	int   id;
} BitmapKey;

static void
build_bitmap_key( HMENU menu, PMenuItemReg m, BitmapKey * key)
{
	memset(key, 0, sizeof(BitmapKey));
	key->menu = menu;
	key->id   = m->id;
}

static HBITMAP
get_unchecked_bitmap(void)
{
	int cx, x1, y1, x2, y2, i, sz;
	HDC dc;
	uint32_t * ptr;
	HPEN stock_pen;
	HPEN stock_brush;
	HBITMAP stock_bm;

	if ( std_unchecked_bitmap == (HBITMAP)(-1)) return NULL;
	if ( std_unchecked_bitmap != NULL) return std_unchecked_bitmap;

	std_unchecked_bitmap = (HBITMAP) -1;

	cx = GetSystemMetrics( SM_CXMENUCHECK );
	dc = GetDC(NULL);
	if ( !( std_unchecked_bitmap = image_create_argb_dib_section( dc, cx, cx, &ptr))) {
		ReleaseDC(NULL, dc);
		return NULL;
	}
	ReleaseDC(NULL, dc);

	dc = CreateCompatibleDC(NULL);
	stock_bm = SelectObject( dc, std_unchecked_bitmap);

	sz = cx * cx;
	if (
		(LOBYTE(LOWORD(guts.version)) < 6) || (
			(LOBYTE(LOWORD(guts.version)) == 6) &&
			(HIBYTE(LOWORD(guts.version)) <  2)
		)
	) {
		DWORD color = GetSysColor( COLOR_MENU ) | 0xff000000;
		for ( i = 0; i < sz; i++, ptr++)
			*ptr = color;
	} else
		bzero(ptr, sz * 4);
	x1 = y1 = (cx > 10) ? cx / 4 : 0;
	x2 = y2 = cx - x1;

	stock_brush = SelectObject( dc, std_hollow_brush);

	stock_pen = SelectObject( dc, CreatePen( PS_SOLID, 0, 0x404040));
	MoveToEx( dc, x1, y2, NULL);
	LineTo( dc, x2, y2);
	LineTo( dc, x2, y1);

	DeleteObject( SelectObject( dc, CreatePen( PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW))));
	MoveToEx( dc, x2, y1, NULL);
	LineTo( dc, x1, y1);
	LineTo( dc, x1, y2 + 1);

	x1++; y1++; x2--; y2--;
	MoveToEx( dc, x1, y2, NULL);
	LineTo( dc, x2, y2);
	LineTo( dc, x2, y1);

	DeleteObject( SelectObject( dc, CreatePen( PS_SOLID, 0, GetSysColor(COLOR_BTNHIGHLIGHT))));
	MoveToEx( dc, x2, y1, NULL);
	LineTo( dc, x1, y1);
	LineTo( dc, x1, y2 + 1);

	for ( i = 0; i < sz; i++, ptr++)
		if ( *ptr )
			*ptr |= 0xff000000;

	DeleteObject(SelectObject( dc, stock_brush ));
	SelectObject( dc, stock_pen );
	SelectObject(dc, stock_bm);
	DeleteDC(dc);

	return std_unchecked_bitmap;
}

static HMENU
add_item( Bool menuType, Handle menu, PMenuItemReg i)
{
	MENUITEMINFOW mii;
	HMENU m;
	PMenuWndData mwd;
	PMenuItemReg first;

	if ( i == NULL) return NULL;

	if ( menuType)
		m = CreateMenu();
	else
		m = CreatePopupMenu();

	if ( !m) {
		apiErr;
		return NULL;
	}
	mwd = ( PMenuWndData) malloc( sizeof( MenuWndData));
	if ( !mwd) {
		DestroyMenu( m);
		return NULL;
	}
	mwd-> menu = menu;
	first      = i;
	hash_store( mgr_menu, &m, sizeof(HMENU), mwd);

	while ( i != NULL)
	{
		bzero( &mii, sizeof(mii));
		mii.cbSize   = sizeof(mii);
		mii.fMask    = MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE | MIIM_ID | MIIM_DATA;

		mii.fType    = 0;
		mii.fType   |= ( i-> flags. divider    ) ? MFT_SEPARATOR    : 0;
		mii.fType   |= ( i-> bitmap            ) ? MFT_BITMAP       : 0;
		mii.fType   |= ( i-> text              ) ? MFT_STRING       : 0;
		mii.fType   |= ( i-> flags. rightAdjust) ? MFT_RIGHTJUSTIFY : 0;
		mii.fType   |= ( i-> flags. custom_draw) ? MFT_OWNERDRAW    : 0;

		mii.fState   = 0;
		mii.fState  |= ( i-> flags. checked    ) ? MFS_CHECKED      : 0;
		mii.fState  |= ( i-> flags. disabled   ) ? MFS_GRAYED       : 0;

		mii.wID      = i-> id + MENU_ID_AUTOSTART;
		mii.hSubMenu = add_item( menuType, menu, i-> down);
		mii.dwItemData = menu;

		if (!( i-> flags. divider && i-> flags. rightAdjust)) {
			if ( i-> text)
				mii. dwTypeData = map_text_accel( i);
			else if ( i-> bitmap )
				mii. dwTypeData = (WCHAR*) create_menu_bitmap( i-> bitmap);
			if ( i-> icon ) {
				BitmapKey key;
				HBITMAP bitmap = create_menu_bitmap( i-> icon);
				build_bitmap_key(m, i, &key);
				hash_store( mgr_menu_bitmaps, &key, sizeof(key), (void*) bitmap);
				mii. fMask |= MIIM_CHECKMARKS;
				mii. hbmpChecked = mii. hbmpUnchecked = bitmap;
			} else if ( i-> flags. autotoggle ) {
				mii. fMask |= MIIM_CHECKMARKS;
				mii. hbmpUnchecked = get_unchecked_bitmap();
			}
			InsertMenuItemW( m, -1, true, &mii);
			if ( i-> text && mii.dwTypeData)
				free( mii. dwTypeData);
		}

		i = i-> next;
	}

	mwd-> id = first-> id;
	return m;
}

Bool
apc_menu_create( Handle self, Handle owner)
{
	objCheck false;
	dobjCheck( owner) false;
	sys class_name = WC_MENU;
	sys owner     = DHANDLE( owner);
	return true;
}

static Bool
clear_menus( void* item, int keyLen, void * key, void * params)
{
	if ( ((PMenuWndData)item)-> menu == ( Handle) params)
		hash_delete( mgr_menu, key, keyLen, true);
	return false;
}

static Bool
clear_bitmaps(void * bm, int keyLen, void * key, void * menu)
{
	if ( ((BitmapKey*)key)-> menu == ( HMENU) menu)
		hash_delete( mgr_menu_bitmaps, key, keyLen, true);
	return false;
}

Bool
apc_menu_destroy( Handle self)
{
	if ( var handle) {
		objCheck false;
		hash_first_that( mgr_menu, clear_menus, ( void *) self, NULL, NULL);
		hash_first_that( mgr_menu_bitmaps, clear_bitmaps, ( void *) var handle, NULL, NULL);
		if ( IsMenu(( HMENU) var handle) && !DestroyMenu(( HMENU) var handle)) apiErrRet;
		return true;
	}
	return false;
}

PFont
apc_menu_default_font( PFont copyTo)
{
	*copyTo = guts. menu_font;
	copyTo-> pitch = fpDefault;
	return copyTo;
}

Color
apc_menu_get_color( Handle self, int index)
{
	return remap_color( index | var widgetClass, true);
}

PFont
apc_menu_get_font( Handle self, PFont font)
{
	return apc_menu_default_font( font);
}

Bool
apc_menu_set_color( Handle self, Color color, int index)
{
	return true;
}

Bool
apc_menu_set_font( Handle self, PFont font)
{
	return true;
}

static void
free_bitmaps( BitmapKey *key, PMenuItemReg m)
{
	if ( m-> icon ) {
		key-> id = m-> id;
		hash_delete( mgr_menu_bitmaps, &key, sizeof(key), false);
	}
	if ( m-> next )
		free_bitmaps( key, m-> next);
	if ( m-> down )
		free_bitmaps( key, m-> down);
}

static void
free_submenus(HMENU menu)
{
	int i, n;
	hash_delete( mgr_menu, &menu, sizeof(HMENU), true);
	n = GetMenuItemCount(menu);
	for ( i = 0; i < n; i++) {
		HMENU submenu = GetSubMenu(menu, i);
		if ( submenu )
			free_submenus(submenu);
	}
}

Bool
apc_menu_item_delete( Handle self, PMenuItemReg m)
{
	PWindow owner = NULL;
	Point size;
	Bool resize;
	BitmapKey key;
	MENUITEMINFO mii;

	objCheck false;
	dobjCheck( var owner) false;
	if (( resize = kind_of( var owner, CWindow) &&
		kind_of( self, CMenu) &&
		var stage <= csNormal &&
		((( PAbstractMenu) self)-> self)-> get_selected( self))) {
		owner = ( PWindow) var owner;
		size = owner-> self-> get_size( var owner);
	}

	build_bitmap_key((HMENU) var handle, m, &key);
	free_bitmaps(&key, m);

	bzero(&mii, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask  = MIIM_SUBMENU;
	GetMenuItemInfo(( HMENU) var handle, m->id + MENU_ID_AUTOSTART, false, &mii);
	if (mii.hSubMenu) free_submenus(mii.hSubMenu);

	DeleteMenu(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND);
	DrawMenuBar( DHANDLE( var owner));
	if ( resize && apc_window_get_window_state( var owner) == wsNormal)
		owner-> self-> set_size( var owner, size);
	return true;
}

Bool
apc_menu_item_set_accel( Handle self, PMenuItemReg m)
{
	UINT flags;
	WCHAR * buf;

	if ( !var handle) return false;
	objCheck false;
	flags = GetMenuState(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND);
	if ( flags == 0xFFFFFFFF) return false;

	if ( flags & MF_BITMAP)
		flags = ( flags & ~MF_BITMAP) | MF_STRING;

	apcErrClear;
	buf = map_text_accel( m);
	if ( !ModifyMenuW(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, flags,
						m-> id + MENU_ID_AUTOSTART, buf))
		apiErr;
	free( buf);
	return rc == errOk;
}

Bool
apc_menu_item_set_check( Handle self, PMenuItemReg m)
{
	DWORD res;
	if ( !var handle) return false;
	objCheck false;
	res = CheckMenuItem(( HMENU) var handle,
		m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND | ( m-> flags. checked ? MF_CHECKED : MF_UNCHECKED));
	return res != 0xFFFFFFFF;
}

static Bool
update_check_icons( Handle self, PMenuItemReg m)
{
	UINT flags;
	HBITMAP bitmap = NULL;
	BitmapKey key;
	Bool ret = true;
	MENUITEMINFO mii;

	if ( !var handle) return false;
	objCheck false;
	flags = GetMenuState(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND);
	if ( flags == 0xFFFFFFFF) return false;

	build_bitmap_key((HMENU) var handle, m, &key);
	hash_delete( mgr_menu_bitmaps, &key, sizeof(key), false);

	bzero(&mii, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask  = MIIM_CHECKMARKS;

	if ( m-> icon ) {
		if ( ( bitmap = create_menu_bitmap( m-> icon)) != NULL)
			hash_store( mgr_menu_bitmaps, &key, sizeof(key), (void*) bitmap);
		else {
			apiErr;
			ret = false;
		}
		mii.hbmpChecked = mii.hbmpUnchecked = bitmap;
	} else if ( m-> flags.autotoggle ) {
		mii. hbmpUnchecked = get_unchecked_bitmap();
	}

	SetMenuItemInfo(( HMENU ) var handle, m-> id + MENU_ID_AUTOSTART, false, &mii);

	return ret;
}

Bool
apc_menu_item_set_autotoggle( Handle self, PMenuItemReg m)
{
	return update_check_icons(self, m);
}

Bool
apc_menu_item_set_enabled( Handle self, PMenuItemReg m)
{
	DWORD res;
	if ( !var handle) return false;
	objCheck false;
	res = EnableMenuItem(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND | (
		m-> flags. disabled ? MF_GRAYED : MF_ENABLED
	));
	DrawMenuBar( DHANDLE( var owner));
	return res != 0xFFFFFFFF;
}

Bool
apc_menu_item_set_key( Handle self, PMenuItemReg m)
{
	return true;
}

Bool
apc_menu_item_set_text( Handle self, PMenuItemReg m)
{
	WCHAR * buf;
	UINT flags;

	if ( !var handle) return false;
	objCheck false;
	flags = GetMenuState(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND);
	if ( flags == 0xFFFFFFFF) return false;

	if ( flags & MF_BITMAP)
		flags = ( flags & ~MF_BITMAP) | MF_STRING;

	apcErrClear;
	buf = map_text_accel( m);
	if ( !ModifyMenuW(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, flags,
			m-> id + MENU_ID_AUTOSTART, (WCHAR*) buf)) apiErr;
	free( buf);
	return rc == errOk;
}

Bool
apc_menu_item_set_image( Handle self, PMenuItemReg m)
{
	UINT flags;

	if ( !var handle) return false;
	objCheck false;
	dobjCheck( m-> bitmap) false;
	flags = GetMenuState(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, MF_BYCOMMAND);
	if ( flags == 0xFFFFFFFF) return false;

	flags |= MF_BITMAP;

	if ( !ModifyMenuW(( HMENU) var handle, m-> id + MENU_ID_AUTOSTART, flags,
		m-> id + MENU_ID_AUTOSTART,
		( LPCWSTR) create_menu_bitmap( m-> bitmap))) apiErrRet;
	return true;
}

Bool
apc_menu_item_set_icon( Handle self, PMenuItemReg m)
{
	return update_check_icons(self, m);
}

ApiHandle
apc_menu_get_handle( Handle self)
{
	objCheck 0;
	return ( ApiHandle) var handle;
}

Bool
apc_menu_update( Handle self, PMenuItemReg oldBranch, PMenuItemReg newBranch)
{
	objCheck false;
	dobjCheck( var owner) false;

	if ( kind_of( var owner, CWindow) &&
		kind_of( self, CMenu) &&
		var stage <= csNormal &&
		((( PAbstractMenu) self)-> self)-> get_selected( self)) {
		HMENU h = GetMenu( DHANDLE( var owner));
		PWindow owner = ( PWindow) var owner;
		Point size = owner-> self-> get_size( var owner);
		if ( h) {
			hash_first_that( mgr_menu_bitmaps, clear_bitmaps, ( void *) h, NULL, NULL);
			DestroyMenu( h);
		}
		hash_first_that( mgr_menu, clear_menus, ( void *) self, NULL, NULL);
		var handle = ( Handle) add_item( kind_of( self, CMenu), self, (( PMenu) self)-> tree);
		SetMenu( DHANDLE( var owner), self ? ( HMENU) var handle : NULL);
		DrawMenuBar( DHANDLE( var owner));
		if ( apc_window_get_window_state( var owner) == wsNormal)
			owner-> self-> set_size( var owner, size);
	} else {
		if ( var handle) {
			hash_first_that( mgr_menu_bitmaps, clear_bitmaps, ( void *) var handle, NULL, NULL);
			DestroyMenu(( HMENU) var handle);
		}
		hash_first_that( mgr_menu, clear_menus, ( void *) self, NULL, NULL);
		var handle = ( Handle) add_item( kind_of( self, CMenu), self, (( PMenu) self)-> tree);
	}
	return true;
}

Bool
apc_popup_create( Handle self, Handle owner)
{
	objCheck false;
	dobjCheck( owner) false;
	sys owner = DHANDLE( owner);
	sys class_name = WC_MENU;
	return true;
}

PFont
apc_popup_default_font( PFont font)
{
	return apc_menu_default_font( font);
}

Bool
apc_popup( Handle self, int x, int y, Rect * anchor)
{
	TPMPARAMS tpm;
	HWND owner;
	Bool ret = true;
	objCheck false;

	if ( guts. popup_active) return false;

	y = dsys( var owner) last_size. y - y;
	owner = ( HWND)(( PComponent) var owner)-> handle;
	if ( var owner != prima_guts.application) {
		POINT pt;
		pt. x = x;
		pt. y = y;
		if ( !MapWindowPoints( owner, NULL, &pt, 1)) apiErr;
		x = pt.x;
		y = pt.y;
	}
	if ( anchor &&
		anchor-> left   &&
		anchor-> right  &&
		anchor-> top    &&
		anchor-> bottom
		)
	{
		RECT r;
		GetWindowRect( owner, &r);
		tpm. cbSize = sizeof( tpm);
		tpm. rcExclude. left   = anchor-> left;
		tpm. rcExclude. right  = anchor-> right;
		tpm. rcExclude. top    = r. bottom - r. top - anchor-> top;
		tpm. rcExclude. bottom = r. bottom - r. top - anchor-> bottom;
		if ( !MapWindowPoints( owner, NULL, ( PPOINT) &tpm. rcExclude, 2)) apiErr;
	} else
		anchor = NULL;

	guts. popup_active = 1;
	ret = TrackPopupMenuEx(
		( HMENU) var handle, TPM_LEFTBUTTON|TPM_LEFTALIGN|TPM_RIGHTBUTTON,
		x, y, owner, anchor ? &tpm : NULL);
	guts. popup_active = 0;
	return ret;
}

Bool
apc_menu_item_begin_paint( Handle self, PEvent event)
{
	objCheck false;
	apcErrClear;

	self = event->gen.H;
	sys transform2. x = 0;
	sys transform2. y = 0;
	apt_set( aptCompatiblePS);
	sys ps = (HDC) event->gen.p;
	sys last_size = event->gen.P;
	sys s.menuitem.saved_dc = SaveDC(sys ps);
	hwnd_enter_paint( self);
	return true;
}

Bool
apc_menu_item_end_paint( Handle self, PEvent event)
{
	objCheck false;
	self = event->gen.H;
	hwnd_leave_paint( self);
	RestoreDC(sys ps, sys s.menuitem.saved_dc);
	sys ps = NULL;
	apt_clear( aptCompatiblePS);
	return true;
}


#ifdef __cplusplus
}
#endif
