/**********************************************************************
 
	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 "CWindowMenuAttachment.h"
#include "v/VMenu.h"

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

VCustomizedMenuBar	*menu_no_window = 0;

void v_setup_menu_choosed();
void v_about_menu_choosed();


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;
	  case VMT_SETUP:
		id = cmd_Preferences;
		break;
	  case VMT_ABOUT:
		id = cmd_About;
		break;
	  case VMT_CLOSE:
		id = cmd_Close;
		break;
	  case VMT_QUIT:
		id = cmd_Quit;
		break;
	  case VMT_UNDO:
		id = cmd_Undo;
		break;
	  case VMT_REDO:
		id = cmd_Redo;
		break;
	  case VMT_CUT:
		id = cmd_Cut;
		break;
	  case VMT_COPY:
		id = cmd_Copy;
		break;
	  case VMT_PASTE:
		id = cmd_Paste;
		break;
	  case VMT_CLEAR:
		id = cmd_Clear;
		break;
	  case VMT_SEL_ALL:
		id = cmd_SelectAll;
		break;
	
	  default:
		id = type;
	}
	
	set_member_vars(id, type, flag, name, ws, 
			modifier, shortcut_key, access_key, func, 0);
}


void
VMenuBar::init()
{
	menu_no_window = VCustomizedMenuBar::create(0,0);
	VCustomizedMenuBarImp::switch_to_menu(menu_no_window);
}


void
VCustomizedMenuBar::make_customized_menu(VMenuBar *bar)
{
	VMenuItem  *ret, *add, *m, *sub, *n, *next, *next_sub;

	VMenuItem *m_appl, *m_file, *m_edit, *m_view, *m_wind, *m_help;
	VMenuItem *m_setup, *m_about, *m_close, *m_quit, *m_insert_p;

	if ( base )
		ret = new VMenuItem(*bar->get_items(), this);
	else {
		ret = new VMenuItem(*get_menu_bar(0)->get_items(), this);
		add = new VMenuItem(*bar->get_items(), this);
		add_flag_hierarchic(ret, VMF_MERGED);
	}
	
	m_appl = m_file = m_edit = m_view = m_wind = m_help = 0;
	m_setup = m_about = m_close = m_quit = m_insert_p = 0;
	
	for ( m = ret ; m ; m = next ) {
		next = m->next;
		if ( ! base && (m->flag & VMF_B_HIDE) ) {
			m->remove_from_list(m, &ret);
			delete m;
			continue;
		}
		switch ( m->type ) {
		  case VMT_APPLICATION:
			m_appl = m_insert_p = m;
			break;
		  case VMT_FILE:
			m_file = m_insert_p = m;
			break;
		  case VMT_EDIT:
			m_edit = m_insert_p = m;
			break;
		  case VMT_VIEW:
			m_view = m_insert_p = m;
			break;
		  case VMT_WINDOW:
			m_wind = m;
			break;
		  case VMT_HELP:
			m_help = m;
			break;
		  default:
		  	if ( m_insert_p == 0 )
				m_insert_p = m;
		}
	}
	
	if ( ! base ) {
		for ( m = add ; m ; m = next ) {
			next = m->next;
			switch ( m->type ) {
			  case VMT_APPLICATION:
			  	if ( m_appl ) {
			  		m_appl->merge(m);
			  		m->next = 0;
			  		delete m;
			  	}
			  	else {
			  		m_appl = m;
			  		m->next = ret;
					ret = m;
			  	}
				break;
			  case VMT_FILE:
			  	if ( m_file ) {
			  		m_file->merge(m);
			  		m->next = 0;
			  		delete m;
			  	}
			  	else {
			  		m_file = m;
			  		if ( m_appl ) {
			  			m->next = m_appl->next;
			  			m_appl->next = m;
			  		}
			  		else {
			  			m->next = ret;
			  			ret = m;
			  		}
			  	}
				break;
			  case VMT_EDIT:
			  	if ( m_edit ) {
			  		m->next = m_edit->next;
			  		m_edit->next = m;
			  		m_edit->remove_from_list(m_edit, &ret);
			  		delete m_edit;
			  	}
				m_edit = m;
				break;
			  case VMT_VIEW:
			  	if ( m_view ) {
			  		m->next = m_view->next;
			  		m_view->next = m;
			  		m_view->remove_from_list(m_view, &ret);
			  		delete m_view;
			  	}
				m_view = m;
				break;
			  case VMT_WINDOW:
			  	if ( m_wind ) {
			  		m->next = m_wind->next;
			  		m_wind->next = m;
			  		m_wind->remove_from_list(m_wind, &ret);
			  		delete m_wind;
			  	}
				m_wind = m;
				break;
			  case VMT_HELP:
			  	if ( m_help ) {
			  		m->next = m_help->next;
			  		m_help->next = m;
			  		m_help->remove_from_list(m_help, &ret);
			  		delete m_help;
			  	}
				m_help = m;
				break;
			  default:
				m->next = m_insert_p->next;
				m_insert_p->next = m;
				m_insert_p = m;
			}
		}
	}
	
	
	if ( m_appl == 0 ) {
		m_appl = new VMenuItem(VMT_APPLICATION, 0, 0, 0, 0, 0, 0, 0);
		m_appl->next = ret;
		ret = m_appl;
	}
	if ( m_file == 0 ) {
		m_file = new VMenuItem(VMT_FILE, 0, 0, 0, 0, 0, 0, 0);
		m_file->next = m_appl->next;
		m_appl->next = m_file;
	}
	
	for ( m = ret ; m ; m = next ) {
		next = m->next;
		int cnt = 0;
		for ( sub = m->submenu ; sub ; sub = next_sub ) {
			next_sub = sub->next;
			cnt++;
			switch ( sub->type ) {
			  case VMT_SETUP:
				m_setup = sub;
				if ( UEnvironment::IsRunningOSX() ) {
					if ( m->type != VMT_APPLICATION ) {
						m->remove(sub);
						m_appl->append(sub, m_about?3:1);
						if ( sub->next == 0 ||
								sub->next->type != VMT_SEPARATOR &&
								sub->next->type != VMT_SETUP ) {
							n = new VMenuItem(VMT_SEPARATOR,0,0,0,0,0,0,0);
							n->next = sub->next;
							sub->next = n;
						}
					}
				}
				break;
			  case VMT_ABOUT:
				m_about = sub;
				if ( UEnvironment::IsRunningOSX() ) {
					if ( m->type != VMT_APPLICATION ) {
						m->remove(sub);
						m_appl->append(sub, 1);
						if ( sub->next == 0 ||
								sub->next->type != VMT_SEPARATOR &&
								sub->next->type != VMT_ABOUT ) {
							n = new VMenuItem(VMT_SEPARATOR,0,0,0,0,0,0,0);
							n->next = sub->next;
							sub->next = n;
						}
					}
				}
				break;
			  case VMT_QUIT:
				m_quit = sub;
				if ( UEnvironment::IsRunningOSX() ) {
					if ( m->type != VMT_APPLICATION ) {
						m->remove(sub);
						m_appl->append(sub);
					}
				}
				else {
					if ( sub->next ) { // move to last
						m->remove(sub);
						for ( VMenuItem *k = m->submenu ; k ; k = k->next ) {
							if ( k->next == 0 ) {
								if ( k->type != VMT_SEPARATOR ) {
									k->next = new VMenuItem(VMT_SEPARATOR,0,0,0,0,0,0,0);
									k = k->next;
								}
								k->next = sub;
								sub->next = 0;
								break;
							}
						}
					}
				}
				break;
			  case VMT_REDO:
				m->remove(sub); // 'undo' will be changed into 'redo'
				delete sub;
				break;
			}
		}
	}
	for ( m = ret ; m ; m = next ) {
		next = m->next;
		int cnt = 0;
		for ( sub = m->submenu ; sub ; sub = next_sub ) {
			next_sub = sub->next;
			if ( sub->type == VMT_SEPARATOR ) {
				if ( sub->next == 0 || sub->next->type == VMT_SEPARATOR ) {
					m->remove(sub);
					delete sub;
					continue;
				}
			}
			cnt++;
		}
		if ( cnt == 0 && m->type != VMT_WINDOW ) {
			VMenuItem::remove_from_list(m, &ret);
		}
	}
	items = ret;
	items->debug_print(0);
}

VMenuBar::edit_id_2_item_type(int id)
{
	switch (id) {
	  case cmd_Undo:
		return VMT_UNDO;
	  case cmd_Cut:
		return VMT_CUT;
	  case cmd_Copy:
		return VMT_COPY;
	  case cmd_Paste:
		return VMT_PASTE;
	  case cmd_Clear:
		return VMT_CLEAR;
	  case cmd_SelectAll:
		return VMT_SEL_ALL;
	}
	return 0;
}


// ================================================================================
//    Macintosh Menu Bar Management
// ================================================================================

#include <LMenuBar.h>

struct LMenuList {
	LMenu *menu;
	bool in_menu_bar;
	struct LMenuList *next;
} *currentMenuList = 0;
VMenuItem *currentMenuItems = 0;
VCustomizedMenuBar *currentCustomizedMenuBar = 0;
bool MenuFlag_about = false;
bool MenuFlag_setup = false;

void
VCustomizedMenuBar::set_menu_flag_do(VMenuItem *item, short flag)
{
	if ( item->info.lmenu ) {
		MenuRef macMenuH = item->info.lmenu->GetMacMenuH();
		MenuItemIndex menuItem = item->info.where;
		if ( flag & VMF_MERGED ) {
			switch ( flag & VMF_BEHAVIOR_MASK ) {
			  case VMF_B_AUTOMATIC:
			  	if ( (flag & VMF_ENABLED) && item->func )
					EnableMenuItem(macMenuH, menuItem);
				else
					DisableMenuItem(macMenuH, menuItem);
				break;
			  case VMF_B_DISABLE:
				DisableMenuItem(macMenuH, menuItem);
				break;
			}
		}
		else {
			if ( flag & VMF_ENABLED )
				EnableMenuItem(macMenuH, menuItem);
			else
				DisableMenuItem(macMenuH, menuItem);
		}
		if ( flag & VMF_CHECKED )
			SetItemMark(macMenuH, menuItem, checkMark);
		else if ( flag & VMF_FLAGGED )
			SetItemMark(macMenuH, menuItem, diamondMark);
		else
			SetItemMark(macMenuH, menuItem, noMark);
	}
}

void
VCustomizedMenuBar::set_menu_name_do(VMenuItem *item, const L_CHAR *name)
{
	if ( item->info.lmenu ) {
		MenuRef macMenuH = item->info.lmenu->GetMacMenuH();
		MenuItemIndex menuItem = item->info.where;
		Str255 pname;
		c2pstrcpy(pname, name ? n_string(std_cm, const_cast<L_CHAR*>(name)) : "-");
		SetMenuItemText(macMenuH, menuItem, pname);
	}
}

void
VCustomizedMenuBarImp::install_menu_items_sub(VMenuItem *items, LMenu *lm)
{
	int where = 0;
	Str255 name;
	MenuRef macmenu = lm->GetMacMenuH();
	for ( ; items ; items = items->next ) {
		c2pstrcpy(name, (items->type!=VMT_SEPARATOR && items->name)
							? n_string(std_cm, items->name) : "-");
		lm->InsertCommand(name, items->id, ++where);
		SetItemCmd(macmenu, where, toupper(items->shortcut_key));
		SetMenuItemModifiers(macmenu, where,
			((items->modifier&V_MODKEY_SHIFT) ? kMenuShiftModifier : 0) |
			((items->modifier&V_MODKEY_ALT) ? kMenuOptionModifier : 0) |
			((items->modifier&V_MODKEY_CTRL) ? 0 : kMenuNoCommandModifier) |
			((items->modifier&V_MODKEY_META) ? kMenuControlModifier : 0));
		if ( items->submenu ) {
			LMenu *lsub = new LMenu(items->id, "\p");
			install_menu_items_sub(items->submenu, lsub);
			SetMenuItemHierarchicalMenu(macmenu, where, lsub->GetMacMenuH());
			currentMenuList = new LMenuList((LMenuList){lsub, false, currentMenuList});
		}
		items->info.lmenu = lm;
		items->info.where = where;
		VCustomizedMenuBar::set_menu_flag_do(items, items->flag);
	}
}

void
VCustomizedMenuBarImp::install_menu_items(VMenuItem *items)
{
	VMenuItem *m, *sub;
	Str255 name;
	ResIDT id;
	MenuHandle handle;
	SInt16 item;
	for ( m = items ; m ; m = m->next ) {
		if (m->type == VMT_APPLICATION) {
			for ( sub = m->submenu ; sub ; sub = sub->next ) {
				if ( sub->type == VMT_ABOUT ) {
					LMenuBar::GetCurrentMenuBar()->FindMenuItem(cmd_About, id, handle, item);
					if ( item ) {
						c2pstrcpy(name, sub->name ? n_string(std_cm, sub->name) : "");
						SetMenuItemText(handle, item, name);
					}
					MenuFlag_about = true;
				}
				if ( sub->type == VMT_SETUP ) {
					MenuFlag_setup = true;
				}
			}
		}
		else if ( !(m->flag & VMF_MERGED) || (m->flag & VMF_BEHAVIOR_MASK) != VMF_B_HIDE ) {
			c2pstrcpy(name, m->name ? n_string(std_cm, m->name) : "");
			LMenu *lm = new LMenu(m->id, name);
			m->info.lmenu = lm; /* menu title */
			m->info.where = 0;
			install_menu_items_sub(m->submenu, lm);
			LMenuBar::GetCurrentMenuBar()->InstallMenu(lm, InstallMenu_AtEnd);
			currentMenuList = new LMenuList((LMenuList){lm, true, currentMenuList});
		}
	}

	currentMenuItems = items;
}

void
VCustomizedMenuBarImp::remove_current_menu_items_sub(VMenuItem *items)
{
	for ( ; items ; items = items->next ) {
		for ( VMenuItem *sub = items->submenu ; sub ; sub = sub->next )
			remove_current_menu_items_sub(sub);
		items->info.lmenu = 0;
	}
}

void
VCustomizedMenuBarImp::remove_current_menu_items()
{
	remove_current_menu_items_sub(currentMenuItems);
	
	while ( currentMenuList ) {
		if ( currentMenuList->in_menu_bar )
			LMenuBar::GetCurrentMenuBar()->RemoveMenu(currentMenuList->menu);
		delete currentMenuList->menu;
		LMenuList *list = currentMenuList->next;
		delete currentMenuList;
		currentMenuList = list;
	}
	MenuFlag_about = MenuFlag_setup = false;
}

void
VCustomizedMenuBarImp::setup_window_menu(VMenuItem *items)
{
	for ( ; items ; items = items->next ) {
		if ( items->type == VMT_WINDOW ) {
			CWindowMenuAttachment::GetMain()->SetupMenu(items->info.lmenu);
			break;
		}
	}
}

void
VCustomizedMenuBarImp::update_window_menu()
{
	CWindowMenuAttachment::GetMain()->UpdateMenu();
}

void
VCustomizedMenuBarImp::switch_to_menu(VCustomizedMenuBar *bar)
{
	if ( bar == 0 )
		bar = menu_no_window;
	currentCustomizedMenuBar = bar;
	if ( bar == 0 ) {
		remove_current_menu_items();
	}
	else {
		remove_current_menu_items();
		install_menu_items(bar->items);
		setup_window_menu(bar->items);
	}
}

void
v_setup_menu_choosed()
{
	if ( currentCustomizedMenuBar )
		currentCustomizedMenuBar->menu_choosed(cmd_Preferences);
}

void
v_about_menu_choosed()
{
	if ( currentCustomizedMenuBar )
		currentCustomizedMenuBar->menu_choosed(cmd_About);
}