//////////////////////////////////////////////////////////////////////////////////
// $Source: /cvsroot/cdx/cdx3.0/addons/CDXBitmapMenu/cdxmenu.cpp,v $
// $Author: hebertjo $
//
// $Log: cdxmenu.cpp,v $
// Revision 1.1.1.1  2000/04/22 16:51:05  hebertjo
// Initial checkin of v3.0 to SourceForge CVS.
//
// Revision 1.1.1.1  1999/05/04 23:59:24  bsimser
// Imported addons
//
// $Revision: 1.1.1.1 $
//////////////////////////////////////////////////////////////////////////////////

#include "CDX.h"
#include "cdxbitmap.h"

#define         IsInRange(x,m,n)                ( (x>=m) && (x<=n) )
#define         DEFAULT_MENU_KEY_TIME           150


POINT	CDXMenu::LockMouse = {-1,-1};
int		CDXMenu::ButtonState = -1;

void CDXMenu::Constructor()
{
    m_MenuTimer.Reset();
    m_pMouse = NULL;
	m_ItemCount = 0;
    m_CurButtonID = -1;
    m_CurKey = 0;
    m_Wrap = 1;
	m_pSubMenu = NULL;
}


CDXMenu::CDXMenu() : CDXSurface()
{
	Constructor();
}


CDXMenu::CDXMenu(CDXScreen *pScreen, int Width, int Height, BOOL memoryType) :
		 CDXSurface(pScreen, Width, Height, memoryType)
{
	Constructor();
}


CDXMenu::CDXMenu(CDXScreen *pScreen, const char *szFilename, BOOL memoryType) :
		 CDXSurface(pScreen, szFilename, memoryType)
{
	Constructor();
}


CDXMenu::~CDXMenu()
{
	for (int i=0; i<m_ItemCount; i++)
	{
		delete m_pMenuButton[i];
	}
}


int	CDXMenu::IsInMenu(POINT pt)
{
	return PtInRect(&DestRect, pt);
}


void CDXMenu::AddItem(CDXMenuButton* pMenuButton, POINT pt, UINT buttonID)
{
	m_pMenuButton[m_ItemCount++] = pMenuButton;
    pMenuButton->SetButton(buttonID, pt);
}


//
//
//  Note:   This function returns a BitmapButton via parameter,
//          and the user doesn't need to delete the button himself.
//
/////////////////////////////////////////////////////////////////////
void CDXMenu::AddItem(CDXScreen* pScreen, BitmapButtonInfo* pButtonInfo, CDXBitmapButton** pBitmapButton)
{
    RECT rc = { pButtonInfo->pos.x, pButtonInfo->pos.y, pButtonInfo->pos.x+pButtonInfo->w, pButtonInfo->pos.y+pButtonInfo->h };

    if ( (!pBitmapButton) || (!pButtonInfo) )
    {
        return;
    }
    *pBitmapButton = new CDXBitmapButton(   pScreen,
                                            pButtonInfo->szFileName,
                                            pButtonInfo->w,
                                            pButtonInfo->h,
                                            pButtonInfo->num,
                                            pButtonInfo->memoryType);


    AddItem(*pBitmapButton, pButtonInfo->pos, pButtonInfo->buttonID);
}


//
//
//  Note:   This function returns a BitmapButton via parameter,
//          and the user doesn't need to delete the button himself.
//
/////////////////////////////////////////////////////////////////////
void CDXMenu::AddItem(CDXScreen* pScreen, TextButtonInfo* pButtonInfo, CDXTextButton** pTextButton)
{
    if ( (!pTextButton) || (!pButtonInfo) )
    {
        return;
    }
	*pTextButton = new CDXTextButton(   pScreen,
                                        pButtonInfo->szString,
                                        pButtonInfo->flagClr,
                                        pButtonInfo->downClr,
                                        pButtonInfo->upClr);

    AddItem(*pTextButton, pButtonInfo->pos, pButtonInfo->buttonID);
}


void CDXMenu::AddItem(BmpTextButtonInfo* pButtonInfo, CDXBmpTextButton** pBmpTextButton)
{
    if ( (!pBmpTextButton) || (!pButtonInfo) )
    {
        return;
    }
	*pBmpTextButton = new CDXBmpTextButton( pButtonInfo->szString,
                                            pButtonInfo->bmpFontFlag,
                                            pButtonInfo->bmpFontDown,
                                            pButtonInfo->bmpFontUp);

    AddItem(*pBmpTextButton, pButtonInfo->pos, pButtonInfo->buttonID);
}


HRESULT CDXMenu::DrawMenu(CDXSurface* lpDDS)
{
	// Draw background first
	HRESULT retVal=CDXSurface::Draw(lpDDS);

	// Draw buttons
	for (int i=0; i<m_ItemCount; i++)
	{
		m_pMenuButton[i]->DrawButton(lpDDS);
	}

	// Draw submenu on top if it has one
	if (m_pSubMenu)
	{
		return m_pSubMenu->DrawMenu(lpDDS);
	}
	return retVal;
}


void CDXMenu::RegisterMouse(POINT* pt)
{
    if (pt)
    {
        m_pMouse = pt;
    }
}


//
//
//	Return:	ButtonID when mouse button down on a button.
//			>=0		 when it is not a down button
//
//////////////////////////////////////////////////////
HRESULT CDXMenu::MouseMove(int newState, POINT* pt)
{
    if (!pt)
    {
        if (!m_pMouse)
        {
            return 0;
        } else {
            pt = m_pMouse;
        }
    }

	//	Lock the mouse positon and state
	//	Ignore the message if it is the same as before
	if ( (CDXMenu::LockMouse.x==pt->x) &&
		 (CDXMenu::LockMouse.y==pt->y) &&
         (CDXMenu::ButtonState==newState) )
	{
		return -1;
	}

    int retVal=-1;
	int MouseMoveVal=-1;

	//	Check each button.
	for (int i=0; i<m_ItemCount; i++)
	{
		retVal = m_pMenuButton[i]->MouseMove(*pt, newState);
		if (retVal<0)
		{
			continue;
		}

		if (!retVal)
		{
			m_pSubMenu = m_pMenuButton[i]->m_pSubMenu;
            MouseMoveVal = 0;
        } else {
            MouseMoveVal = retVal;
        }
	}

	//	If it is up button, we have to reset the instances in CDXMenuButton class
	if (newState==BUTTON_UP)
	{
		CDXMenuButton::ResetButton();
	}

	//	In this case, sub menu doesn't need to be drawn
    if (MouseMoveVal<0)
    {
        m_pSubMenu = NULL;
    }
	return MouseMoveVal;
}


void CDXMenu::IsWrap(int wrap)
{
    m_Wrap = wrap;
}


//
//	Purpose:	It updates and returns the new button id.
//
//	Parameter:	inc -- it is the value we want to increment.
//
////////////////////////////////////////////////////////////
int CDXMenu::GetNextMenuButtonID(int inc)
{
    if (m_CurButtonID==-1)
    {
        return (m_CurButtonID=0);
    }

    int newButtonID=m_CurButtonID+inc;

	//	Determine if we need to loop the menu or not
    if (!m_Wrap)
    {
        if ( (newButtonID<0) || (newButtonID==m_ItemCount) )
        {
            return m_CurButtonID;
        }
    }
    return (m_CurButtonID=(newButtonID+m_ItemCount)%m_ItemCount);
}


HRESULT CDXMenu::KeyMessage(int buttonID, POINT* pt, int buttonState)
{
    int retVal=MouseMove(buttonState, &m_pMenuButton[buttonID]->GetPtInButton());
    if (!pt)
    {
        pt = m_pMouse;
    }
    LockMouse.x = pt->x;
    LockMouse.y = pt->y;
    ButtonState = BUTTON_FLAG;
    return retVal;
}


//
//
//  Return: <0  when ignore the key message
//           0  when activate the key message
//          >0  when goto sub menu
//
//////////////////////////////////////////////
int CDXMenu::IsKeyActivate(int keyState, POINT* pt)
{
    if ( (!pt) && (!m_pMouse) )
    {
        return -1;
    }

    if (!IsInRange(keyState, 1, 7)) 
    {
        return -1;
    }

    if (keyState==m_CurKey) {

		//	See if the key down message is in time interval
        if (m_MenuTimer.IsInTime(DEFAULT_MENU_KEY_TIME, 0)>0)
        {
            return -1;
        }

    } else {
        m_CurKey = keyState;

    }

	//	Reset the timer each time we handle the key down message.
    m_MenuTimer.Reset();
    if (m_pSubMenu)
    {
		// If keyState is KEY_LEAVE_SUB, just goto the submenu.
        if (keyState==KEY_LEAVE_SUB)
        {
            return 1;
        }

        if (m_pSubMenu->m_CurButtonID!=-1)
        {
			// In this case, goto the submenu, otherwise, activate the key messge in current menu.
            return 1;
        }
    }
    return 0;
}


HRESULT CDXMenu::UpMenu(POINT* pt)
{
    int retVal=IsKeyActivate(KEY_UP, pt);
    if (retVal<0)
    {
        return 0;

    } else if (!retVal) {
        return KeyMessage(GetNextMenuButtonID(-1), pt, BUTTON_FLAG);

    }

    return m_pSubMenu->UpMenu();
}


HRESULT CDXMenu::DownMenu(POINT* pt)
{
    int retVal=IsKeyActivate(KEY_DOWN, pt);
    if (retVal<0)
    {
        return 0;

    } else if (!retVal) {
        return KeyMessage(GetNextMenuButtonID(), pt, BUTTON_FLAG);

    }

    return m_pSubMenu->DownMenu();
}


HRESULT CDXMenu::GoSubMenu(POINT* pt)
{
	// No key is set
    if (m_CurButtonID==-1)
    {
        return 0;
    }
    int retVal=IsKeyActivate(KEY_GO_SUB, pt);
    if (retVal<0)
    {
        return 0;

    } else if (!retVal) {
        return KeyMessage(m_CurButtonID, pt, BUTTON_FLAG);

    }

    return m_pSubMenu->GoSubMenu();
}


HRESULT CDXMenu::LeaveSubMenu(POINT* pt)
{
	// No key is set
    if (m_CurButtonID==-1)
    {
        return 0;
    }
    int retVal=IsKeyActivate(KEY_LEAVE_SUB, pt);
    if (retVal<0)
    {
        return 0;

    } else if (retVal>0) {

		// It is the second last sub menu. Exit the submenu and 
        if (m_pSubMenu->LeaveSubMenu()<0)
        {
            return KeyMessage(m_CurButtonID, pt, BUTTON_FLAG);
        }

    }

	// Reset the current button ID since no more need this menu. We will exit this menu.
    m_CurButtonID = -1;
    return -1;
}


HRESULT CDXMenu::HomeMenu(POINT* pt)
{
    int retVal=IsKeyActivate(KEY_HOME, pt);
    if (retVal<0)
    {
        return 0;

    } else if (retVal>0) {
        return m_pSubMenu->HomeMenu();

    }

	// Set the current button Id to 0, and go the active that button
    m_CurButtonID = 0;
    return KeyMessage(m_CurButtonID, pt, BUTTON_FLAG);
}


HRESULT CDXMenu::EndMenu(POINT* pt)
{
    int retVal=IsKeyActivate(KEY_END, pt);
    if (retVal<0)
    {
        return 0;

    } else if (retVal>0) {
        return m_pSubMenu->EndMenu();

    }

	// Set the current button Id to last index, and go the active that button
    m_CurButtonID = m_ItemCount-1;
    return KeyMessage(m_CurButtonID, pt, BUTTON_FLAG);
}


HRESULT CDXMenu::EnterMenu(POINT* pt)
{
	// No key is set
	if (m_CurButtonID==-1)
    {
        return 0;
    }

    int retVal=IsKeyActivate(KEY_ENTER, pt);
    if (retVal<0)
    {
        return 0;

    } else if (retVal>0) {
        return m_pSubMenu->EnterMenu();

    }

    // Send down and up actions to active the button
    KeyMessage(m_CurButtonID, pt, BUTTON_DOWN);
    return KeyMessage(m_CurButtonID, pt, BUTTON_UP);
}
