///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  SHELLICO.CPP
//
//      Shell tray icon handler
//
//      Copyright (c) Microsoft Corporation     1998
//    
//      3/15/98 David Stewart / dstewart
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <tchar.h>
#include <shellapi.h>
#include "shellico.h"
#include "mmenu.h"
#include "resource.h"

#define IDM_HOMEMENU_BASE               (LAST_SEARCH_MENU_ID + 1)
#define IDM_NETMENU_BASE                (LAST_SEARCH_MENU_ID + 100)
#define IDM_DISCLIST_BASE               20000
#define SHELLTIMERID                    1500

//file-global variables
PCOMPNODE g_pNode = NULL;
IMMComponentAutomation* g_pAuto = NULL;
HWND g_hwnd = NULL;
HINSTANCE g_hInst = NULL;
BOOL g_fShellIconCreated = FALSE;

//icon
HICON hIconPlay = NULL;
HICON hIconPause = NULL;
HICON hIconNoDisc = NULL;

extern fOptionsDlgUp;
extern nCDMode;
extern BOOL fPlaying;
extern BOOL IsDownloading();
extern CustomMenu* g_pMenu;
extern "C" void NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen);

void CheckDiscState()
{
    if (g_fShellIconCreated)
    {
        MMMEDIAID mmMedia;
        mmMedia.nDrive = -1;
        g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);

        if (mmMedia.dwMediaID != 0)
        {
            ShellIconSetState(fPlaying ? PAUSE_ICON : PLAY_ICON);
        }
        else
        {
            ShellIconSetState(NODISC_ICON);
        }
    }
}

BOOL CreateShellIcon(HINSTANCE hInst, HWND hwndOwner, PCOMPNODE pNode, TCHAR* sztip)
{
    BOOL retval = FALSE;
    g_hInst = hInst;

	HRESULT hr = pNode->pComp->QueryInterface(IID_IMMComponentAutomation,(void**)&g_pAuto);
    if (SUCCEEDED(hr))
    {
        g_pNode = pNode;

        //load all of the icon images
        hIconPlay = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_PLAY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
        hIconPause = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_PAUSE), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
        hIconNoDisc = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_SHELL_NODISC), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);

        //now, create the tray icon
        g_hwnd = hwndOwner;
        NOTIFYICONDATA nData;
        nData.cbSize = sizeof(nData);
        nData.hWnd = hwndOwner;
        nData.uID = SHELLMESSAGE_CDICON;
        nData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
        nData.uCallbackMessage = SHELLMESSAGE_CDICON;
        nData.hIcon = hIconPlay;
        _tcscpy(nData.szTip,sztip);

        retval = Shell_NotifyIcon(NIM_ADD,&nData);

        if (!retval)
        {
            g_pAuto->Release();
            g_pAuto = NULL;
        }
        else
        {
            g_fShellIconCreated = TRUE;
            CheckDiscState();
        }
    }

    return (retval);
}

void DestroyShellIcon()
{
    g_fShellIconCreated = FALSE;

    if (g_pAuto)
    {
        //g_pAuto was addref'ed when we qi'ed for it.
        g_pAuto->Release();
    }

    NOTIFYICONDATA nData;
    nData.cbSize = sizeof(nData);
    nData.uID = SHELLMESSAGE_CDICON;
    nData.hWnd = g_hwnd;
    Shell_NotifyIcon(NIM_DELETE,&nData);

    //kill the icons
    DestroyIcon(hIconPlay);
    DestroyIcon(hIconPause);
    DestroyIcon(hIconNoDisc);

    hIconPlay = NULL;
    hIconPause = NULL;
    hIconNoDisc = NULL;
}

//works around bug in all Windows versions
//where ampersands are stripped from tooltips on shell icon
void EscapeTooltip(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen)
{
    ZeroMemory(szOutput,cbLen);
    WORD index1 = 0;
    WORD index2 = 0;
    for (; ((index1 < _tcslen(szInput)) && (index2 < ((cbLen/sizeof(TCHAR))-1))); index1++)
    {
        szOutput[index2] = szInput[index1];
        if (szOutput[index2] == TEXT('&'))
        {
            szOutput[++index2] = TEXT('&');
            szOutput[++index2] = TEXT('&');
        }
        index2++;
    }
}

void ShellIconSetTooltip()
{
    if (g_fShellIconCreated)
    {
        NOTIFYICONDATA nData;
        nData.cbSize = sizeof(nData);
        nData.hWnd = g_hwnd;
        nData.uID = SHELLMESSAGE_CDICON;
        nData.uFlags = NIF_TIP;

        MMMEDIAID mmMedia;
        mmMedia.nDrive = -1;
        mmMedia.szTrack[0] = TEXT('\0');
        g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);

        TCHAR szTempTip[MAX_PATH+sizeof(TCHAR)];
        TCHAR szEscapeTip[MAX_PATH+sizeof(TCHAR)];

        if ((mmMedia.dwMediaID != 0) || (_tcslen(mmMedia.szTrack)!=0))
        {
            //128 + 128 + 4 = 260 ... that's max_path
            wsprintf(szTempTip,TEXT("%s (%s)"),mmMedia.szTitle,mmMedia.szTrack);
        }
        else
        {
            _tcscpy(szTempTip,mmMedia.szTitle);
        }
        
        //escape out & symbols if they are in the string
        if (_tcschr(szTempTip,TEXT('&')))
        {
            EscapeTooltip(szTempTip,szEscapeTip,sizeof(szEscapeTip));
            _tcscpy(szTempTip,szEscapeTip);
        }

        //truncate long tip to size of tooltip
        szTempTip[(sizeof(nData.szTip)/sizeof(TCHAR))-1] = TEXT('\0');
        _tcscpy(nData.szTip,szTempTip);

        Shell_NotifyIcon(NIM_MODIFY,&nData);

        //bit of a hack, but if we're getting a new tool tip, then we might be need to turn
        //on or off the "nodisc" state
        CheckDiscState();
    }
}

void CreateTransportMenu(CustomMenu* pMenu, CustomMenu* pTransMenu)
{
    //play or pause
    if (fPlaying)
    {
        pTransMenu->AppendMenu(IDB_PLAY,g_hInst,IDS_SH_PAUSE);
    }
    else
    {
        pTransMenu->AppendMenu(IDB_PLAY,g_hInst,IDS_SH_PLAY);
    }

    //stop
    pTransMenu->AppendMenu(IDB_STOP,g_hInst,IDS_SH_STOP);

    //eject
    pTransMenu->AppendMenu(IDB_EJECT,g_hInst,IDS_SH_EJECT);

    //previous track
    pTransMenu->AppendMenu(IDB_PREVTRACK,g_hInst,IDS_SH_PREVTRACK);

    //next track
    pTransMenu->AppendMenu(IDB_NEXTTRACK,g_hInst,IDS_SH_NEXTTRACK);

    pMenu->AppendMenu(g_hInst,IDS_SH_TRANS,pTransMenu);
}

void CreateOptionsMenu(CustomMenu* pMenu, CustomMenu* pOptionsMenu)
{
    pOptionsMenu->AppendMenu(IDM_OPTIONS,g_hInst,IDM_OPTIONS);
    pOptionsMenu->AppendMenu(IDM_PLAYLIST,g_hInst,IDM_PLAYLIST);
    pOptionsMenu->AppendSeparator();

//do not bother graying out the playlist menu, it causes too much delay in odbc load
/*
    LPCDDATA pData = GetCDData();
    if (pData)
    {
        if (FAILED(pData->CheckDatabase(g_hwnd)))
        {
            EnableMenuItem(pOptionsMenu->GetMenuHandle(),
                            IDM_PLAYLIST,
                            MF_BYCOMMAND | MF_GRAYED);
        }
    }
*/

    pOptionsMenu->AppendMenu(IDM_HELP,g_hInst,IDM_HELP);

    pMenu->AppendMenu(g_hInst,IDS_SH_OPTIONS,pOptionsMenu);
}

void CreateModeMenu(CustomMenu* pMenu, CustomMenu* pModeMenu)
{
    pModeMenu->AppendMenu(IDM_MODE_NORMAL,g_hInst,IDI_MODE_NORMAL,IDM_MODE_NORMAL);
    pModeMenu->AppendMenu(IDM_MODE_RANDOM,g_hInst,IDI_MODE_RANDOM,IDM_MODE_RANDOM);
    pModeMenu->AppendMenu(IDM_MODE_REPEATONE,g_hInst,IDI_MODE_REPEATONE,IDM_MODE_REPEATONE);
    pModeMenu->AppendMenu(IDM_MODE_REPEATALL,g_hInst,IDI_MODE_REPEATALL,IDM_MODE_REPEATALL);
    pModeMenu->AppendMenu(IDM_MODE_INTRO,g_hInst,IDI_MODE_INTRO,IDM_MODE_INTRO);
    pModeMenu->SetMenuDefaultItem(nCDMode,FALSE);

    pMenu->AppendMenu(g_hInst,IDS_SH_MODE,pModeMenu);
}

void CreateNetMenu(CustomMenu* pMenu, CustomMenu* pNetMenu, CustomMenu* pSearchSubMenu, CustomMenu* pProviderSubMenu)
{
	MMMEDIAID mmMedia;
    mmMedia.nDrive = -1;
	g_pAuto->OnAction(MMACTION_GETMEDIAID,&mmMedia);

    BOOL fContinue = TRUE;

    //append static menu choices
    if (IsDownloading())
    {
        pNetMenu->AppendMenu(IDM_NET_CANCEL,g_hInst,IDM_NET_CANCEL);
    }
    else
    {
        pNetMenu->AppendMenu(IDM_NET_UPDATE,g_hInst,IDM_NET_UPDATE);
        if (mmMedia.dwMediaID == 0)
        {
            //need to gray out menu
            MENUITEMINFO mmi;
            mmi.cbSize = sizeof(mmi);
            mmi.fMask = MIIM_STATE;
            mmi.fState = MFS_GRAYED;
            HMENU hMenu = pNetMenu->GetMenuHandle();
            SetMenuItemInfo(hMenu,IDM_NET_UPDATE,FALSE,&mmi);
        }
    }
	
    //if networking is not allowed, gray it out ...
    //don't worry about cancel case, it won't be there
    LPCDDATA pData = GetCDData();
    if (mmMedia.dwMediaID != 0)
    {
        //don't allow searching if title isn't available
        if (pData)
        {
            if (pData->QueryTitle(mmMedia.dwMediaID))
            {
                pSearchSubMenu->AppendMenu(IDM_NET_BAND,g_hInst,IDM_NET_BAND);
                pSearchSubMenu->AppendMenu(IDM_NET_CD,g_hInst,IDM_NET_CD);
                pSearchSubMenu->AppendMenu(IDM_NET_ROLLINGSTONE_ARTIST,g_hInst,IDM_NET_ROLLINGSTONE_ARTIST);
                pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ARTIST,g_hInst,IDM_NET_BILLBOARD_ARTIST);
                pSearchSubMenu->AppendMenu(IDM_NET_BILLBOARD_ALBUM,g_hInst,IDM_NET_BILLBOARD_ALBUM);
                pNetMenu->AppendMenu(g_hInst,IDM_NET_SEARCH_HEADING,pSearchSubMenu);
            }
        } //end if pdata
    }

    //display any provider home pages
    DWORD i = 0;
    LPCDOPT pOpt = GetCDOpt();
    if( pOpt )
    {
        LPCDOPTIONS pCDOpts = pOpt->GetCDOpts();

        LPCDPROVIDER pProviderList = pCDOpts->pProviderList;

        while (pProviderList!=NULL)
        {
            TCHAR szProviderMenu[MAX_PATH];
            TCHAR szHomePageFormat[MAX_PATH/2];
            LoadString(g_hInst,IDS_HOMEPAGEFORMAT,szHomePageFormat,sizeof(szHomePageFormat)/sizeof(TCHAR));
            wsprintf(szProviderMenu,szHomePageFormat,pProviderList->szProviderName);

            pProviderSubMenu->AppendMenu(IDM_HOMEMENU_BASE+i,szProviderMenu);

            pProviderList = pProviderList->pNext;
            i++;
        } //end while

        pNetMenu->AppendMenu(g_hInst,IDM_NET_PROVIDER_HEADING,pProviderSubMenu);
    } //end home pages

    //display internet-loaded disc menus
    if (mmMedia.dwMediaID != 0)
    {
        if (pData)
        {
            if (pData->QueryTitle(mmMedia.dwMediaID))
            {
                LPCDTITLE pCDTitle = NULL;
                HRESULT hr =  pData->LockTitle(&pCDTitle,mmMedia.dwMediaID);

                if (SUCCEEDED(hr))
                {
                    for (i = 0; i < pCDTitle->dwNumMenus; i++)
                    {
                        if (i==0)
                        {
                            pNetMenu->AppendSeparator();
                        }

                        TCHAR szDisplayNet[MAX_PATH];
                        NormalizeNameForMenuDisplay(pCDTitle->pMenuTable[i].szMenuText,szDisplayNet,sizeof(szDisplayNet));
            	        pNetMenu->AppendMenu(i + IDM_NETMENU_BASE,szDisplayNet);
                    }

                    pData->UnlockTitle(pCDTitle,FALSE);
                }
            } //end if query title
        }
    }

    pMenu->AppendMenu(g_hInst,IDS_SH_NET,pNetMenu);
}

void CreateTrackMenu(CustomMenu* pMenu, CustomMenu* pTrackMenu)
{
    int i = 0;
    HRESULT hr = S_OK;
    while (SUCCEEDED(hr))
    {
		MMTRACKORDISC mmTrack;
        mmTrack.nNumber = i++;
        hr = g_pAuto->OnAction(MMACTION_GETTRACKINFO,&mmTrack);
        if (SUCCEEDED(hr))
        {
            pTrackMenu->AppendMenu(mmTrack.nID + IDM_TRACKLIST_SHELL_BASE, mmTrack.szName);
            if (mmTrack.fCurrent)
            {
                pTrackMenu->SetMenuDefaultItem(mmTrack.nID + IDM_TRACKLIST_SHELL_BASE,FALSE);
            } //end if current
        } //end if ok
    } //end while

    pMenu->AppendMenu(g_hInst,IDS_SH_TRACK,pTrackMenu);
}

void CreateDiscMenu(CustomMenu* pMenu, CustomMenu* pDiscMenu)
{
    int i = 0;
    HRESULT hr = S_OK;
    while (SUCCEEDED(hr))
    {
        MMTRACKORDISC mmDisc;
        mmDisc.nNumber = i++;
        hr = g_pAuto->OnAction(MMACTION_GETDISCINFO,&mmDisc);
        if (SUCCEEDED(hr))
        {
            pDiscMenu->AppendMenu(mmDisc.nID + IDM_DISCLIST_BASE, mmDisc.szName);
            if (mmDisc.fCurrent)
            {
                pDiscMenu->SetMenuDefaultItem(mmDisc.nID + IDM_DISCLIST_BASE,FALSE);
            } //end if current
        }
    }

    pMenu->AppendMenu(g_hInst,IDS_SH_DISC,pDiscMenu);
}

void CALLBACK lButtonTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    KillTimer(hwnd,idEvent);

    if (fPlaying)
    {
        g_pAuto->OnAction(MMACTION_PAUSE,NULL);
        //ShellIconSetState(PLAY_ICON);
    }
    else
    {
        g_pAuto->OnAction(MMACTION_PLAY,NULL);
        //ShellIconSetState(PAUSE_ICON);
    }
}

LRESULT ShellIconHandeMessage(LPARAM lParam)
{
    switch (lParam)
    {
        case (WM_LBUTTONDOWN) :
        {
            SetTimer(g_hwnd,SHELLTIMERID,GetDoubleClickTime()+100,(TIMERPROC)lButtonTimerProc);
        }
        break;

        case (WM_LBUTTONDBLCLK) :
        {
            KillTimer(g_hwnd,SHELLTIMERID);
            
            if ((IsWindowVisible(g_hwnd)) && (!IsIconic(g_hwnd)))
            {
                ShowWindow(g_hwnd,SW_HIDE);
            }
            else
            {
                if (IsIconic(g_hwnd))
                {
                    ShowWindow(g_hwnd,SW_RESTORE);
                }

                ShowWindow(g_hwnd,SW_SHOW);
		        BringWindowToTop(g_hwnd);
		        SetForegroundWindow(g_hwnd);
            }
        }
        break;

        case (WM_RBUTTONUP) :
        {
            if (!fOptionsDlgUp)
            {
                CustomMenu* pTrackMenu = NULL;
                CustomMenu* pDiscMenu = NULL;
                CustomMenu* pOptionsMenu = NULL;
                CustomMenu* pNetMenu = NULL;
                CustomMenu* pSearchSubMenu = NULL;
                CustomMenu* pProviderSubMenu = NULL;
                CustomMenu* pModeMenu = NULL;
                CustomMenu* pTransMenu = NULL;

                AllocCustomMenu(&g_pMenu);
                AllocCustomMenu(&pTrackMenu);
                AllocCustomMenu(&pDiscMenu);
                AllocCustomMenu(&pOptionsMenu);
                AllocCustomMenu(&pNetMenu);
                AllocCustomMenu(&pSearchSubMenu);
                AllocCustomMenu(&pProviderSubMenu);
                AllocCustomMenu(&pModeMenu);
                AllocCustomMenu(&pTransMenu);

                if (g_pMenu)
                {
                    g_pMenu->AppendMenu(IDM_ABOUT,g_hInst,IDM_ABOUT);
                    g_pMenu->AppendSeparator();

                    CreateTransportMenu(g_pMenu,pTransMenu);
                    CreateOptionsMenu(g_pMenu,pOptionsMenu);
                    CreateNetMenu(g_pMenu,pNetMenu,pSearchSubMenu,pProviderSubMenu);
                    CreateModeMenu(g_pMenu,pModeMenu);
                    CreateDiscMenu(g_pMenu,pDiscMenu);
                    CreateTrackMenu(g_pMenu,pTrackMenu);

                    g_pMenu->AppendSeparator();
                    g_pMenu->AppendMenu(IDM_EXIT_SHELL,g_hInst,IDM_EXIT);

                    POINT mouse;
                    GetCursorPos(&mouse);
                    RECT rect;
                    SetRect(&rect,0,0,0,0);
                    SetForegroundWindow(g_hwnd);
                    g_pMenu->TrackPopupMenu(0,mouse.x,mouse.y,g_hwnd,&rect);

                    pTrackMenu->Destroy();
                    pDiscMenu->Destroy();
                    pOptionsMenu->Destroy();
                    pNetMenu->Destroy();
                    pSearchSubMenu->Destroy();
                    pProviderSubMenu->Destroy();
                    pModeMenu->Destroy();
                    pTransMenu->Destroy();
                }

                if (g_pMenu)
                {
                    g_pMenu->Destroy();
                    g_pMenu = NULL;
                }
            } //end if ok to do
            else
            {
                MessageBeep(0);
            }
        } //end right-button up
        break;
    }

    return 0;
}

void ShellIconSetState(int nIconType)
{
    if (g_fShellIconCreated)
    {
        int iID = IDI_SHELL_PLAY;
        HICON hIcon = hIconPlay;
    
        switch(nIconType)
        {
            case PAUSE_ICON : 
            {
                iID = IDI_SHELL_PAUSE;
                hIcon = hIconPause;
            }
            break;

            case NODISC_ICON :
            {
                iID = IDI_SHELL_NODISC;
                hIcon = hIconNoDisc;
            }
            break;
        }
   
        NOTIFYICONDATA nData;
        nData.cbSize = sizeof(nData);
        nData.hWnd = g_hwnd;
        nData.uID = SHELLMESSAGE_CDICON;
        nData.uFlags = NIF_ICON;
        nData.hIcon = hIcon;

        Shell_NotifyIcon(NIM_MODIFY,&nData);
    }
}