/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code 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.

OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "ui_local.h"

MenuManager menuManager;

Event EV_PushMenu
	(
	"pushmenu",
	EV_DEFAULT,
	"s",
	"menuname",
	"Pushes the menu on the stack"
	);

Event EV_LockMenus
	(
	"lock",
	EV_DEFAULT,
	NULL,
	NULL,
	"Lock out the menu from receiving input"
	);

Event EV_UnlockMenus
	(
	"unlock",
	EV_DEFAULT,
	NULL,
	NULL,
	"Unlock the menu from receiving input"
	);

Event EV_ShowMenu
	(
	"showmenu",
	EV_DEFAULT,
	"B",
	"activate",
	"Shows the menu."
	);

Event EV_HideMenu
	(
	"hidemenu",
	EV_DEFAULT,
	NULL,
	NULL,
	"Hides the menu."
	);

CLASS_DECLARATION( Listener, Menu, NULL )
{
	{ &EV_HideMenu,			&Menu::HideMenu },
	{ &EV_ShowMenu,			&Menu::ShowMenu },
	{ NULL, NULL }
};

Menu::Menu()
{
	menuManager.AddMenu( this );
	m_fullscreen = qfalse;
}

Menu::Menu
	(
	str name
	)

{
	setName( name );
	m_fullscreen = qfalse;
	menuManager.AddMenu( this );
}

void Menu::AddMenuItem
	(
	UIWidget *item
	)

{
	m_itemlist.AddObject( item );

	// add all item's children
	for( int i = 1; i <= item->m_children.NumObjects(); i++ )
	{
		m_itemlist.AddObject( item->m_children.ObjectAt( i ) );
	}
}

void Menu::DeleteMenuItem
	(
	UIWidget *item
	)

{
	// remove all item's children
	for( int i = item->m_children.NumObjects(); i > 0; i-- )
	{
		m_itemlist.RemoveObject( item->m_children.ObjectAt( i ) );
	}

	m_itemlist.RemoveObject( item );
}

void Menu::setName
	(
	str name
	)

{
	m_name = name;
}

void Menu::ShowMenu
	(
	Event *ev
	)

{
	qboolean activate;
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		UIWidgetContainer *widcon = ( UIWidgetContainer * )wid;

		if( wid->isSubclassOf( UIWidgetContainer ) && widcon->m_layout )
		{
			widcon->m_layout->ForceLoad();
		}

		if( wid->isEnabled() )
		{
			wid->setShow( true );
			wid->ResetMotion( MOTION_IN );
			wid->UpdateUIElement();
			wid->UpdateData();
			wid->ExecuteShowCommands();
		}
	}

	if( ev && ev->NumArgs() > 0 ) {
		activate = ev->GetBoolean( 1 );
	} else {
		activate = qtrue;
	}

	if( n && activate )
	{
		UIWidgetContainer *widcon = ( UIWidgetContainer * )m_itemlist.ObjectAt( 1 );
		uWinMan.ActivateControl( widcon );
		widcon->SetLastActiveWidgetOrderNum();
	}

	uWinMan.setFirstResponder( NULL );
}

void Menu::HideMenu
	(
	Event *ev
	)

{
	int i, n;
	bool force = false;
	float maxtime;

	maxtime = GetMaxMotionTime();

	if( ev->NumArgs() > 0 )
		force = ev->GetBoolean( 1 );

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		if( wid->getMotionType() != MOTION_OUT )
		{
			wid->ResetMotion( MOTION_OUT );
			if( !force )
			{
				Event *event = new Event( "hide" );
				wid->PostEvent( event, maxtime );
			}
			else {
				wid->setShow(false);
			}
		}
	}
}

void Menu::ForceShow
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		UIWidgetContainer *widcon = ( UIWidgetContainer * )wid;

		if( wid->isSubclassOf( UIWidgetContainer ) && widcon->m_layout )
		{
			widcon->m_layout->ForceLoad();
		}

		if( wid->isEnabled() )
		{
			wid->setShow( true );
		}
	}
}

void Menu::ForceHide
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		wid->ExecuteHideCommands();
		wid->setShow( false );
	}
}

UIWidget *Menu::GetContainerWidget
	(
	void
	)

{
	if( m_itemlist.NumObjects() > 0 ) {
		return m_itemlist.ObjectAt( 1 );
	} else {
		return NULL;
	}
}

UIWidget *Menu::GetNamedWidget
	(
	const char *pszName
	)

{
	int i;
	UIWidget *pWidget;

	for( i = 1; i <= m_itemlist.NumObjects(); i++ )
	{
		pWidget = m_itemlist.ObjectAt( i );
		if( !stricmp( pszName, pWidget->getName() ) )
		{
			return pWidget;
		}
	}

	return NULL;
}

void Menu::Update
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		wid->UpdateUIElement();
		wid->UpdateData();
	}
}

void Menu::RealignWidgets
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		wid->Realign();
	}
}

float Menu::GetMaxMotionTime
	(
	void
	)

{
	int i;
	int n;
	float maxtime = 0.0;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		if( maxtime < wid->getMotionTime() )
			maxtime = wid->getMotionTime();
	}

	return maxtime;
}

void Menu::ActivateMenu
	(
	void
	)

{
	if( m_itemlist.NumObjects() )
	{
		UIWidgetContainer *widcon = ( UIWidgetContainer * )m_itemlist.ObjectAt( 1 );
		uWinMan.ActivateControl( widcon );
		widcon->SetLastActiveWidgetOrderNum();
		uWinMan.setFirstResponder( NULL );
		widcon->BringToFrontPropogated();
	}
}

qboolean Menu::isFullscreen
	(
	void
	)

{
	return m_fullscreen;
}

void Menu::setFullscreen
	(
	qboolean bFullScreen
	)

{
	m_fullscreen = bFullScreen;
}

int Menu::getVidMode
	(
	void
	)

{
	return m_vidmode;
}

void Menu::setVidMode
	(
	int iMode
	)

{
	m_vidmode = iMode;
}

qboolean Menu::isVisible
	(
	void
	)

{
	UIWidget *wid = GetContainerWidget();
	if( wid )
		return wid->IsVisible();

	return false;
}

void Menu::SaveCVars
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		if( wid->m_cvarname.length() )
		{
			const char *ret = UI_GetCvarString( wid->m_cvarname, NULL );
			if( ret )
				wid->m_cvarvalue = ret;
			else
				wid->m_cvarvalue = "";
		}
	}
}

void Menu::RestoreCVars
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		if( wid->m_cvarname.length() )
		{
			uii.Cvar_Set( wid->m_cvarname, wid->m_cvarvalue );
		}
	}
}

void Menu::ResetCVars
	(
	void
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		if( wid->m_cvarname.length() )
		{
			uii.Cvar_Reset( wid->m_cvarname );
		}
	}
}

void Menu::PassEventToWidget
	(
	str name,
	Event *ev
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		if( wid->getName() == name && wid->ValidEvent( ev->getName() ) )
		{
			wid->ProcessEvent( ev );
		}
	}
}

void Menu::PassEventToAllWidgets
	(
	Event& ev
	)

{
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );
		if( wid->ValidEvent( ev.getName() ) )
		{
			wid->ProcessEvent( ev );
		}
	}
}

void Menu::CheckRestart
	(
	void
	)

{
	cvar_t *cvar;
	qboolean do_restart = false;
	qboolean do_snd_restart = false;
	qboolean do_ter_restart = false;
	int i;
	int n;

	n = m_itemlist.NumObjects();
	for( i = 1; i <= n; i++ )
	{
		UIWidget *wid = m_itemlist.ObjectAt( i );

		if( wid->m_cvarname.length() )
		{
			cvar = uii.Cvar_Find( wid->m_cvarname );
			if( cvar && cvar->latchedString )
			{
				if( str::icmp( wid->m_cvarvalue, cvar->latchedString ) )
				{
					if( cvar->flags & CVAR_LATCH )
					{
						do_restart = true;
					}
					else if( cvar->flags & CVAR_SOUND_LATCH )
					{
						do_snd_restart = true;
					}
					else if( cvar->flags & CVAR_TERRAIN_LATCH )
					{
						do_ter_restart = true;
					}
				}
			}
		}
	}

	if( do_restart )
	{
		uii.Cmd_Stuff( "vid_restart\n" );
	}
	else
	{
		if( do_snd_restart ) {
			uii.Cmd_Stuff( "snd_restart\n" );
		}

		if( do_ter_restart ) {
			uii.Cmd_Stuff( "ter_restart\n" );
		}
	}
}

CLASS_DECLARATION( Listener, MenuManager, NULL )
{
	{ &EV_PushMenu,			&MenuManager::PushMenu },
	{ &EV_LockMenus,		&MenuManager::Lock },
	{ &EV_UnlockMenus,		&MenuManager::Unlock },
	{ NULL, NULL }
};

MenuManager::MenuManager()
{
	m_lock = false;
}

void MenuManager::RealignMenus
	(
	void
	)

{
	int i;
	int num;

	num = m_menulist.NumObjects();
	for (i = 1; i <= num; i++)
	{
		m_menulist.ObjectAt(i)->RealignWidgets();
	}
}

void MenuManager::AddMenu
	(
	Menu *m
	)

{
	m_menulist.AddObject( m );
}

void MenuManager::DeleteMenu
	(
	Menu *m
	)

{
	m_menulist.RemoveObject( m );
}

void MenuManager::DeleteAllMenus
	(
	void
	)

{
	for( int i = m_menulist.NumObjects(); i > 0; i-- )
	{
		Menu *menu = m_menulist.ObjectAt( i );
		delete menu;
	}

	//
	// Added in OPM
	//  Make sure to clear the menu list and stack
	m_menulist.FreeObjectList();

	m_menustack.Clear();
	m_showmenustack.Clear();
}

Menu *MenuManager::FindMenu
	(
	str name
	)

{
	int i;
	int count;

	count = m_menulist.NumObjects();

	for( i = 1; i <= count; i++ )
	{
		Menu *m = m_menulist.ObjectAt( i );

		if( !str::icmp( m->m_name, name ) )
			return m;
	}

	return NULL;
}

bool MenuManager::PushMenu
	(
	str name
	)

{
	Menu *m;
	float maxouttime = 0.0;
	float maxintime;
	Menu *head;

	if( m_lock )
	{
		return false;
	}

	m = FindMenu( name );
	if( !m )
	{
		uii.Sys_Printf( "Couldn't find menu %s\n", name.c_str() );
		return false;
	}

	// don't push the same menu
	head = m_menustack.Head();
	if( head == m )
	{
		return false;
	}

	m->SaveCVars();

	if( head )
	{
		maxouttime = head->GetMaxMotionTime();
		head->ProcessEvent( Event( "hidemenu" ) );
	}

	if( maxouttime == 0.0 )
	{
		m->ProcessEvent( Event( "showmenu" ) );
	}
	else
	{
		m->PostEvent( Event( "showmenu" ), maxouttime );
	}

	maxintime = m->GetMaxMotionTime();

	Lock( NULL );
	PostEvent( EV_UnlockMenus, maxintime + maxouttime );

	m_menustack.Push( m );

	return true;
}

bool MenuManager::ShowMenu
	(
	str name
	)

{
	Menu *m;
	float maxintime;

	if( m_lock )
	{
		return false;
	}

	m = FindMenu( name );
	if( !m )
	{
		uii.Sys_Printf( "Couldn't find menu %s\n", name.c_str() );
		return false;
	}

	m->SaveCVars();
	m->ProcessEvent( new Event( "showmenu" ) );

	maxintime = m->GetMaxMotionTime();

	Lock( NULL );
	PostEvent( EV_UnlockMenus, maxintime );

	m_showmenustack.Push( m );

	return true;
}

void MenuManager::PushMenu
	(
	Event *ev
	)

{
	if( !ui_pLoadingMenu || CurrentMenu() != ui_pLoadingMenu )
	{
		PushMenu( ev->GetString( 1 ) );
	}
}

void MenuManager::PopMenu
	(
	qboolean restore_cvars
	)

{
	Menu *top;
	Menu *head;
	float maxtime = 0.0;

	if( m_lock )
	{
		return;
	}

	top = m_showmenustack.Pop();
	if( top )
	{
		if( restore_cvars ) {
			top->RestoreCVars();
		}

		//maxtime = top->GetMaxMotionTime();
		top->ProcessEvent( Event( "hidemenu" ) );
	}
	else
	{
		top = m_menustack.Pop();
		if( top )
		{
			if( restore_cvars ) {
				top->RestoreCVars();
			}

			maxtime = top->GetMaxMotionTime();
			top->ProcessEvent( Event( "hidemenu" ) );

			head = m_menustack.Head();
			if( head )
			{
				head->PostEvent( Event( "showmenu" ), maxtime );
			}
		}

		Lock(NULL);
		PostEvent(EV_UnlockMenus, maxtime);
	}
}

Menu *MenuManager::CurrentMenu
	(
	void
	)

{
	Menu *head;

	head = m_showmenustack.Head();
	if( head )
	{
		return head;
	}

	head = m_menustack.Head();
	if( head )
	{
		return head;
	}

	return NULL;
}

bool MenuManager::ClearMenus
	(
	bool force
	)

{
	int i, num;
	Menu* top;
	Menu* menu;
	float maxtime;
	float newmaxtime;

	maxtime = 0.0;
	if (m_lock) {
		return false;
	}

	if (!m_showmenustack.Head() &&!m_menustack.Head()) {
		return true;
	}

	top = m_showmenustack.Head();
	while (!m_showmenustack.Empty())
	{
		menu = m_showmenustack.Pop();
		newmaxtime = top->GetMaxMotionTime();
		if (maxtime < newmaxtime) {
			maxtime = newmaxtime;
		}

		if (force)
		{
			// force the menu to hide
			menu->ForceHide();
			continue;
		}

		// otherwise send the "hidemenu" event
		// the menu will process it or not
		menu->ProcessEvent(new Event("hidemenu"));
	}

	top = m_menustack.Head();
	while (!m_menustack.Empty())
	{
		menu = m_menustack.Pop();
		newmaxtime = top->GetMaxMotionTime();
		if (maxtime < newmaxtime) {
			maxtime = newmaxtime;
		}

		if (force)
		{
			// force the menu to hide
			menu->ForceHide();
			continue;
		}

		// otherwise send the "hidemenu" event
		// the menu will process it or not
		menu->ProcessEvent(new Event("hidemenu"));
	}

	num = m_menulist.NumObjects();
	for (i = 1; i <= num; i++)
	{
		menu = m_menulist.ObjectAt(i);
		if (!menu->isVisible()) {
			continue;
		}

		newmaxtime = top->GetMaxMotionTime();
		if (maxtime < newmaxtime) {
			maxtime = newmaxtime;
		}

		if (force)
		{
			menu->ForceHide();
			continue;
		}

		menu->ProcessEvent(new Event("hidemenu"));
	}

	if (!force)
	{
		Lock(NULL);
		PostEvent(EV_UnlockMenus, maxtime);
	}

	return true;
}

void MenuManager::ListMenus
	(
	void
	)

{
	int i;
	int num;

	num = m_menulist.NumObjects();
	for( i = 1; i <= num; i++ )
	{
		Menu *menu = m_menulist.ObjectAt( i );
		uii.Sys_Printf( "%s\n", menu->m_name.c_str() );
	}
}

void MenuManager::UpdateAllMenus
	(
	void
	)

{
	int i;
	int num;

	num = m_menulist.NumObjects();
	for( i = 1; i <= num; i++ )
	{
		Menu *menu = m_menulist.ObjectAt( i );
		menu->Update();
	}
}

void MenuManager::Lock
	(
	Event *ev
	)

{
	m_lock = true;
}

void MenuManager::Unlock
	(
	Event *ev
	)

{
	m_lock = false;
}

void MenuManager::PassEventToWidget
	(
	str name,
	Event *ev
	)

{
	Menu *head = CurrentMenu();

	if( head )
	{
		head->PassEventToWidget( name, ev );
	}
}

void MenuManager::PassEventToAllWidgets
	(
	Event& ev
	)

{
	int i;
	int iMenuCount;

	iMenuCount = m_menulist.NumObjects();
	for( i = 1; i <= iMenuCount; i++ )
	{
		Menu *pMenu = m_menulist.ObjectAt( i );
		pMenu->PassEventToAllWidgets( ev );
	}
}

void MenuManager::ResetCVars
	(
	void
	)

{
	Menu *head = CurrentMenu();

	if( head )
	{
		head->ResetCVars();
	}
}

void MenuManager::CheckRestart
	(
	void
	)

{
	Menu *head = CurrentMenu();

	if( head )
	{
		head->CheckRestart();
	}
}

bool MenuManager::ForceMenu
	(
	str name
	)

{
	Menu *m;
	Menu *head;


	m = FindMenu( name );
	if( !m )
	{
		uii.Sys_Printf( "Couldn't find menu %s\n", name.c_str() );
		return false;
	}

	// don't push the same menu
	head = m_menustack.Head();
	if( head == m )
	{
		return false;
	}

	m->SaveCVars();

	if( head )
	{
		head->ForceHide();
	}

	m->ProcessEvent( Event( "showmenu" ) );
	// add the new menu to the stack
	m_menustack.Push(m);

	return true;
}

