/*
 *(C) 2006 Roku LLC
 *
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License Version 2 as published 
 * by the Free Software Foundation.
 *
 * 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. See the GNU General 
 * Public License for more details.
 *
 * Please read README.txt in the same directory as this source file for 
 * further license information.
 */

#include "stdafx.h"
#include "resource.h"
#include "NotifyIcon.h"
#include "FireflyShell.h"

CNotifyIcon::CNotifyIcon()
{
	ZeroMemory(&m_nid, sizeof(NOTIFYICONDATA));
	m_nid.cbSize = sizeof(NOTIFYICONDATA);
	m_nid.uID = ID_SHELLNOTIFY;

	m_running_icon = AtlLoadIcon(IDI_SHELL_RUNNING);
	m_stopped_icon = AtlLoadIcon(IDI_SHELL_STOPPED);
}

BOOL CNotifyIcon::Create()
{
	m_registered_activation_message = GetApplication()->GetRegisteredActivationMessage();

	RECT rect = {0, 0, 0, 0};
	
	// Hidden window.
	if (base::Create(NULL, rect, _T("FireflyShellNotifyIconHidden"), WS_POPUP))
	{
		m_nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP;

		InflictIconState();

		//wcsncpy(niData.szTip, TEXT("Foo?"), sizeof(niData.szTip));
		m_nid.hWnd = m_hWnd;
		m_nid.uCallbackMessage = PRIVATE_WM_NOTIFYICON;

		Shell_NotifyIcon(NIM_ADD, &m_nid);

		SetTimer(TIMER_ID, 5000, NULL);

		GetApplication()->ServiceStatusSubscribe(this);

		EnableUserSwitchNotifications();

		return TRUE;
	}

	return FALSE;
}

void CNotifyIcon::Destroy()
{
	GetApplication()->ServiceStatusUnsubscribe(this);
	KillTimer(TIMER_ID);
	Shell_NotifyIcon(NIM_DELETE, &m_nid);
	DestroyIcon(m_nid.hIcon);


	base::DestroyWindow();
}

void CNotifyIcon::OnClose()
{
	// The only time this should happen is if something else explicitly
	// sends us the message (such as the installer). We'll just
	// exit completely.
	GetApplication()->Exit();
}

void CNotifyIcon::PopupBalloon(UINT title_id, UINT text_id, DWORD flags)
{
	CString title, text;
	title.LoadString(title_id);
	text.LoadString(text_id);

	m_nid.uFlags |= NIF_INFO;

	SafeStringCopy(m_nid.szInfoTitle, title, 64);
	SafeStringCopy(m_nid.szInfo, text, 256);

	m_nid.dwInfoFlags = flags;
	m_nid.uTimeout = 10000;
	Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}

void CNotifyIcon::Update()
{
	InflictIconState();
	// I suspect we'll need this line too.
	// m_nid.uFlags &= ~NIF_INFO;
	Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}

void CNotifyIcon::InflictIconState()
{
	// Will the icons leak?
	Service::Status status = GetApplication()->GetServiceStatus();
	UINT state_id;
	if (status.IsPending())
	{
		state_id = IDS_SERVER_PENDING;
		m_nid.hIcon = m_stopped_icon; // As good as any?
	}
	else if (status.IsRunning())
	{
		state_id = IDS_SERVER_RUNNING;
		m_nid.hIcon = m_running_icon;
	}
	else
	{
		state_id = IDS_SERVER_STOPPED;
		m_nid.hIcon = m_stopped_icon;
	}

	CString tip;
	tip.LoadString(state_id);
	SafeStringCopy(m_nid.szTip, tip, 64);
}

void CNotifyIcon::OnServiceStatus(Service::Status old_status, Service::Status new_status)
{
	Update();
}

void CNotifyIcon::OnTimer(UINT id, TIMERPROC proc)
{
	if (id == TIMER_ID)
	{
		GetApplication()->CheckServiceStatus();
	}
}

LRESULT CNotifyIcon::OnNotifyIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	switch (lParam)
	{
	case WM_LBUTTONDBLCLK:
		GetApplication()->Configure(true);
		bHandled = true;
		return 0L;
	case WM_RBUTTONDOWN:
	case WM_CONTEXTMENU:
		OnContextMenu();
		bHandled = true;
		return 0L;
	}

	return 0L;
}

void CNotifyIcon::OnContextMenu()
{
	HMENU hMenu;

	HINSTANCE hInstance = _Module.GetResourceInstance();
	hMenu = ::LoadMenu(hInstance, MAKEINTRESOURCE(IDM_CONTEXT));

	POINT pt;
	GetCursorPos(&pt);

	// See TrackPopupMenu in MSDN.
	SetForegroundWindow(m_hWnd);
	//::SetForegroundWindow(m_hWnd);
	HMENU hPopup = GetSubMenu(hMenu, 0);
	::SetMenuDefaultItem(hPopup, ID_CONFIGURE, FALSE);
	TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_BOTTOMALIGN, pt.x, pt.y, 0, m_hWnd, NULL);
	::PostMessage(m_hWnd, WM_NULL, 0, 0);

	::DestroyMenu(hMenu);
}

LRESULT CNotifyIcon::OnConfigure(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	GetApplication()->Configure(true);
	return 0;
}

LRESULT CNotifyIcon::OnExit(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	/// @todo
	Application::GetInstance()->Exit();
	return 0;
}

LRESULT CNotifyIcon::OnRegisteredActivation(UINT, WPARAM, LPARAM, BOOL &bHandled)
{
	ATLTRACE(_T("Activate\n"));
	bHandled = true;
	GetApplication()->Configure(false);

	// We return a magic number so that the caller knows we've been found
	// and can give up.
	return m_registered_activation_message;
}

void CNotifyIcon::OnServerEvent(UINT32 id, UINT32 intval, const CString &strval)
{
	// Note that we're running on a different thread here. We need to punt
	// the event over to the main thread using SendMessage.
	switch (id)
	{
	case 0:
		break;
	case 1:
	case 2:
		SendMessage(WM_SERVEREVENT, id);
		break;
	default:
		ATLASSERT(false);
		break;
	}
}

LRESULT CNotifyIcon::OnServerEvent(UINT, WPARAM wparam, LPARAM, BOOL &bHandled)
{
	bHandled = true;
	switch (wparam)
	{
	case 1:
		PopupBalloon(IDR_MAINFRAME, IDS_SCAN_START);
		break;
	case 2:
		PopupBalloon(IDR_MAINFRAME, IDS_SCAN_STOP);
		break;
	}
	return 0;
}

void CNotifyIcon::EnableUserSwitchNotifications()
{
	HMODULE h = ::LoadLibrary(_T("WtsApi32.dll"));
	if (h)
	{
		typedef BOOL  (WINAPI *Proc)(HWND, DWORD);
		Proc fn = reinterpret_cast<Proc>(GetProcAddress(h, "WTSRegisterSessionNotification"));
		if (fn)
		{
			(*fn)(m_hWnd, NOTIFY_FOR_THIS_SESSION);
		}
		::FreeLibrary(h);
	}
}

LRESULT CNotifyIcon::OnSessionChange(UINT, WPARAM wparam, LPARAM, BOOL &bHandled)
{
	// Because only one process can get events through the mailslot we
	// disconnect from it when the user uses XP fast user switching to
	// switch to a different user.
	switch (wparam)
	{
	case WTS_CONSOLE_CONNECT:
	case WTS_REMOTE_CONNECT:
		ATLTRACE("SESSION CONNECT\n");
		GetApplication()->EnableServerEvents(true);
		break;

	case WTS_CONSOLE_DISCONNECT:
	case WTS_REMOTE_DISCONNECT:
		ATLTRACE("SESSION DISCONNECT\n");
		GetApplication()->EnableServerEvents(false);
		break;
	}
	bHandled = true;
	return 0;
}