/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#include "v/VMenu.h"
#include "v/VWindow.h"
#include "machine/lc_util.h"
extern "C" {
#include "memory_debug.h"
bool vobject_quit();
}

// ===================================================================
//    Implementation of Machine-Common Menu Routine
// ===================================================================

VMenuItem::VMenuItem(
	short type,
	short flag,
	char *name,
	LC_WRITING_STYLE *ws,
	short modifier,
	char shortcut_key,
	char access_key,
	V_CALLBACK(func))
{
	static int next_id = 1000;
	int id;
	switch ( type ) {
	  case VMT_OTHER:
		id = next_id++;
		break;
	  default:
		id = type;
	}
	
	set_member_vars(id, type, flag, name, ws, 
			modifier, shortcut_key, access_key, func, 0);
}

void
VMenuBar::init()
{
}


void
VCustomizedMenuBar::make_customized_menu(VMenuBar *bar)
{
	items = new VMenuItem(*bar->get_items(), this);
}



// ===================================================================
//    Windows Menu Bar Management
// ===================================================================

static void
v_set_menu_flag_do(HMENU hmenu, UINT id, short flag)
{
	MENUITEMINFO mii;
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE;
	
	if ( flag&VMF_ENABLED )
		mii.fState = MFS_ENABLED;
	else
		mii.fState = MFS_DISABLED;

	if ( flag&(VMF_CHECKED|VMF_FLAGGED) )
		mii.fState |= MFS_CHECKED;
	else
		mii.fState |= MFS_UNCHECKED;
	
	SetMenuItemInfo(hmenu, id, FALSE, &mii);
}

void
VCustomizedMenuBar::set_menu_flag_do(VMenuItem *item, short flag)
{
	v_serialized_exec_sub(v_set_menu_flag_do, item->info, item->id, flag);
}


static void
v_set_menu_name_do(HMENU hmenu, UINT id, const L_CHAR *name,
				   short modifier, char shortcut_key, char access_key)
{
	L_CHAR *lname;
	char *cstr=0;
	wchar_t *wstr=0;
	int len = name ? l_strlen((L_CHAR*)name) : 0;

	if ( access_key ) {
		int amp = 0;
		lname = (L_CHAR*)d_alloc((len+5)*sizeof(L_CHAR));
		for ( int i = 0 ; i < len ; i++ ) {
			if ( !amp && tolower(name[i]) == tolower(access_key) ) {
				lname[i] = '&';
				amp = 1;
			}
			lname[i+amp] = name[i];
		}
		if ( !amp ) {
			lname[len++] = '(';
			lname[len++] = '&';
			lname[len++] = access_key;
			lname[len++] = ')';
		}
		len = len+amp;
		lname[len] = 0;
	}
	else
		lname = ll_copy_str(const_cast<L_CHAR*>(name));

	if ( shortcut_key ) {
		if ( access_key ) 
			lname = (L_CHAR*)d_re_alloc(lname, (len+32)*sizeof(L_CHAR));
		else {
			L_CHAR *lname2 = (L_CHAR*)d_alloc((len+32)*sizeof(L_CHAR));
			memcpy(lname2, lname, len*sizeof(L_CHAR));
			d_f_ree(lname);
			lname = lname2;
		}
		lname[len++] = '\t';
		if ( modifier & V_MODKEY_ALT ) {
			lname[len++] = 'A';
			lname[len++] = 'l';
			lname[len++] = 't';
			lname[len++] = '+';
		}
		if ( modifier & V_MODKEY_CTRL ) {
			lname[len++] = 'C';
			lname[len++] = 't';
			lname[len++] = 'r';
			lname[len++] = 'l';
			lname[len++] = '+';
		}
		if ( modifier & V_MODKEY_SHIFT ) {
			lname[len++] = 'S';
			lname[len++] = 'h';
			lname[len++] = 'i';
			lname[len++] = 'f';
			lname[len++] = 't';
			lname[len++] = '+';
		}
		lname[len++] = toupper(shortcut_key);
		lname[len] = 0;
	}

	l2native(&cstr, &wstr, lname);
	if ( cstr ) {
		MENUITEMINFOA mii;
		mii.cbSize = sizeof(mii);
		mii.fMask = MIIM_TYPE;
		mii.fType = MFT_STRING;
		mii.dwTypeData = cstr;
		mii.cch = -1;
		SetMenuItemInfoA(hmenu, id, FALSE, &mii);
	}
	else if ( wstr ) {
		MENUITEMINFOW mii;
		mii.cbSize = sizeof(mii);
		mii.fMask = MIIM_TYPE;
		mii.fType = MFT_STRING;
		mii.dwTypeData = wstr;
		mii.cch = -1;
		SetMenuItemInfoW(hmenu, id, FALSE, &mii);
	}
	if ( cstr )
		d_f_ree(cstr);
	if ( wstr )
		d_f_ree(wstr);

	if ( access_key )
		d_f_ree(lname);
}

void
VCustomizedMenuBar::set_menu_name_do(VMenuItem *item, const L_CHAR *name)
{
	v_serialized_exec_sub(v_set_menu_name_do, item->info, item->id, name,
					item->modifier, item->shortcut_key, item->access_key);
}


// --------------- Edit Menu Update Implementaion ---------------

void
VCustomizedMenuBarImp::setup_edit_menu_contextual(VMenuItem *item)
{
	((VCustomizedMenuBar*)(item->menu_bar))->update(item->submenu);
}

// --------------- Window Menu Implementaion ---------------

void
VCustomizedMenuBarImp::setup_window_menu(VMenuItem *item)
{
	MENUITEMINFO mii;
	mii.cbSize = sizeof(mii);

	HMENU hmenu;
	if ( item->submenu )
		hmenu = item->submenu->info;
	else {
		hmenu = CreateMenu();
		mii.fMask = MIIM_SUBMENU;
		GetMenuItemInfo(item->info, item->id, FALSE, &mii);
		hmenu = mii.hSubMenu;
		if ( hmenu == 0 )
			er_panic("Window menu must have submenu");
	}

	int cnt = 0;
	VMenuItem *last = 0;
	for ( VMenuItem *sub = item->submenu ; sub ; sub = sub->next ) {
		last = sub;
		cnt++;
	}
	int i = GetMenuItemCount(hmenu)-1;
	for ( ; i >= cnt ; i-- )
		DeleteMenu(hmenu, i, MF_BYPOSITION);

	if ( last && last->type != VMT_SEPARATOR ) {
		mii.fType = MFT_SEPARATOR;
		InsertMenuItem(hmenu, 0, FALSE, &mii);
	}
	
	cnt = 0;
	TCHAR text[256];
	HWND frontmost = GetForegroundWindow();
	mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA | MIIM_STATE;
	mii.fType = MFT_STRING;
	mii.dwTypeData = text;
	for ( VWindowsList *list = windows_list ; list ; list = list->next ) {
		GetWindowText(list->hwnd, text, sizeof(text));
		if ( list->hwnd == frontmost )
			mii.fState = MFS_CHECKED;
		else
			mii.fState = MFS_UNCHECKED;
		mii.dwItemData = (DWORD)list->hwnd;
		mii.wID = 0x8000 + cnt++;
		InsertMenuItem(hmenu, 0, TRUE, &mii);
	}
}


// --------------- Menu Interface Implementaion ---------------

bool
VCustomizedMenuBarImp::init_menu(HMENU hmenu, VMenuInfoM *info)
{
	if ( info->hmenu_edit )
		setup_edit_menu_contextual(info->item_edit);
	if ( info->hmenu_window )
		setup_window_menu(info->item_window);
	return false;
}


void
VCustomizedMenuBarImp::menu_choosed(VWindow *vwin, UINT id)
{
	if ( id == VMT_CLOSE )
		vwin->attempt_close();
	else if ( id == VMT_QUIT )
		vobject_quit();
	else if ( id & 0x8000 ) {
		VMenuItem *item = vwin->get_menu_bar()->search(VMT_WINDOW);
		if  ( item == 0 )
			er_panic("id < 0x8000 is reserved for menu");
		MENUITEMINFO mii;
		mii.cbSize = sizeof(mii);
		mii.fMask = MIIM_SUBMENU;
		GetMenuItemInfo(item->info, item->id, FALSE, &mii);
		if ( mii.hSubMenu == 0 )
			er_panic("Window menu must have submenu");
		TCHAR str[256];
		mii.dwTypeData = str;
		mii.fMask = MIIM_DATA | MIIM_TYPE;
		GetMenuItemInfo(mii.hSubMenu, id, FALSE, &mii);
		SetForegroundWindow((HWND)mii.dwItemData);
	}
	else {
		VMenuItem *item = vwin->get_menu_bar()->search(id);
		((VCustomizedMenuBar*)(item->menu_bar))->menu_choosed(item);
	}
}


HMENU
VCustomizedMenuBarImp::make_menu_items(VMenuItem *items, VMenuInfoM *info, int depth)
{
	MENUITEMINFO mii;
	mii.cbSize = sizeof(mii);
	HMENU hmenu = CreateMenu();

	const int max_accel = 1024;
	static ACCEL accel[max_accel];
	static accel_cnt = 0;
	
	for ( ; items ; items = items->next ) {
		
		mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;

		items->info = hmenu;
		mii.wID = items->id;
		mii.dwItemData = (DWORD)items;

		if ( items->submenu ) {
			mii.fMask |= MIIM_SUBMENU;
			mii.hSubMenu = make_menu_items(items->submenu, info, depth+1);
		}
		else if ( items->type == VMT_WINDOW ) {
			// window menu must have submenu
			mii.fMask |= MIIM_SUBMENU;
			mii.hSubMenu = CreateMenu();
		}

		if ( items->shortcut_key ) {
			if ( accel_cnt >= max_accel )
				er_panic("too many accelarators");
			accel[accel_cnt].fVirt = FVIRTKEY |
				((items->modifier&V_MODKEY_SHIFT) ? FSHIFT  : 0) |
				((items->modifier&V_MODKEY_CTRL)  ? FCONTROL: 0) |
				((items->modifier&V_MODKEY_ALT)   ? FALT    : 0);
			accel[accel_cnt].key = toupper(items->shortcut_key);
			accel[accel_cnt].cmd = items->id;
			accel_cnt++;
		}

		if ( items->type == VMT_SEPARATOR ) {
			mii.fType = MFT_SEPARATOR;
			InsertMenuItem(hmenu, 0, FALSE, &mii);
		}
		else {
			mii.fType = MFT_STRING;
			mii.dwTypeData = "";
			InsertMenuItem(hmenu, 0, FALSE, &mii);
			v_set_menu_name_do(hmenu, items->id, items->name,
					items->modifier, items->shortcut_key, items->access_key);
		}

		switch ( items->type ) {
		  case VMT_OTHER:
		  case VMT_SETUP:
		  case VMT_ABOUT:
		  case VMT_QUIT:
			v_set_menu_flag_do(hmenu, items->id, items->flag);
			break;
		  case VMT_EDIT:
			info->hmenu_edit = items->submenu ? items->submenu->info : 0;
			info->item_edit = items;
			break;
		  case VMT_WINDOW:
			info->hmenu_window = mii.hSubMenu;
			info->item_window = items;
			break;
		}
	}

	if ( depth == 0 && accel_cnt ) {
		info->haccel = CreateAcceleratorTable(accel, accel_cnt);
		accel_cnt = 0;
	}
	return hmenu;
}
