/**************************************************************

mv_class.c (C-Munipack project)
Base class for display classes
Copyright (C) 2012 David Motl, dmotl@volny.cz

This program 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.

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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdio.h>
#include <Windows.h>
#include <htmlhelp.h>

#include "mv_class.h"
#include "about_dlg.h"
#include "main.h"

enum {
	CMD_HELP = 1,
	CMD_ABOUT
};

//-------------------------   DEFAULT SETTINGS   ------------------------------------

TCCD_Interface::TCCD_Interface(void):m_hWnd(0), m_FilePath(NULL), m_hBitmap(0), 
	m_BmpWidth(0), m_BmpHeight(0), m_NeedUpdate(true)
{
}

TCCD_Interface::~TCCD_Interface(void)
{
	if (m_hBitmap)
		DeleteObject(m_hBitmap);
	free(m_FilePath);
}

void TCCD_Interface::RegisterClass(HINSTANCE hInst)
{
	WNDCLASSEX wcex;

	memset(&wcex, 0, sizeof(WNDCLASSEX));
	wcex.cbSize			= sizeof(WNDCLASSEX); 
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.hInstance		= hInst;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.lpszClassName	= PAINTWNDCLASS;
	RegisterClassEx(&wcex);
}

LRESULT CALLBACK TCCD_Interface::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	TCCD_Interface *pMe = (TCCD_Interface*)GetWindowLongPtr(hWnd, GWLP_USERDATA);

	switch(message)
	{
	case WM_CREATE:
		// Save the pointer to display object to window extra memory
		pMe = (TCCD_Interface *)(((LPCREATESTRUCT)lParam)->lpCreateParams);
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pMe);
		if (pMe) {
			pMe->m_hWnd = hWnd; 
			return pMe->InstanceWndProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_DESTROY:
		if (pMe) {
			LRESULT result = pMe->InstanceWndProc(hWnd, message, wParam, lParam);
			SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
			delete pMe;
			return result;
		}
		break;
	default:
		if (pMe)
			return pMe->InstanceWndProc(hWnd, message, wParam, lParam);
		break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

//
// Instance window procedure
//
LRESULT TCCD_Interface::InstanceWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	POINT pt;
	PAINTSTRUCT ps;

	switch (message) 
	{
	case WM_CREATE:
		OnCreate();
		break;
	case WM_DESTROY:
		OnDestroy();
		break;
	case WM_SIZE:
		OnSize();
		break;
	case WM_PAINT:
		// Paint the client area
		BeginPaint(hWnd, &ps);
		OnPaint(ps.hdc, &ps.rcPaint);
		EndPaint(hWnd, &ps);
		return TRUE;
	case WM_ENABLE:
		// Window enabled/disabled
		OnEnable(wParam!=0);
		break;
	case WM_VSCROLL:
        // Handles messages sent by vertical scroll bar and vertical zoom slider
		OnVScroll(LOWORD(wParam));
		return TRUE;
	case WM_HSCROLL:
        // Handles messages sent by horizontal scroll bar and horizontal zoom slider
		OnHScroll(LOWORD(wParam));
		return TRUE;
	case WM_KEYDOWN: 
		// Keyboard message
		OnKeyDown((int)wParam);
		break;
	case WM_CONTEXTMENU:
		// Open context menu
		pt.x = lParam & 0xFFFF; 
		pt.y = (lParam >> 16) & 0xFFFF; 
		OnContextMenu(&pt);
		return TRUE;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

// Keyboard event handler
void TCCD_Interface::OnKeyDown(int vKey)
{
	switch (vKey)
	{
	case VK_F1:
		ShowHelp(GetTopicId());
		break;
	}
}

// Window enabled/disabled
void TCCD_Interface::OnEnable(bool enabled)
{
	Invalidate();
}

// Invalidates the window
void TCCD_Interface::Invalidate(void)
{ 
	m_NeedUpdate = true;
	if (m_hWnd) 
		InvalidateRect(m_hWnd, NULL, false); 
}

// Set file path
void TCCD_Interface::SetFilePath(const char *filepath)
{
	int len;

	free(m_FilePath);
	if (filepath) {
		len = (int)strlen(filepath);
		m_FilePath = (char*)malloc(len+1);
		if (m_FilePath)
			strcpy_s(m_FilePath, len+1, filepath);
	} else 
		m_FilePath = NULL;
}

//
// Paints backscreen buffer to display context
//
void TCCD_Interface::OnPaint(HDC hDC, RECT *pRect)
{
	int left, top, width, height;
	HDC	mem_dc;
	HBITMAP old_bmp;

	if (m_hBitmap) {
		mem_dc = CreateCompatibleDC(hDC);
		old_bmp = (HBITMAP)SelectObject(mem_dc, m_hBitmap);
		if (m_NeedUpdate) {
			// Refresh content of the backscreen buffer
			OnRender(mem_dc);
			m_NeedUpdate = false;
		}
		// Copy backscreen buffer to painting context
		left = pRect->left;
		top = pRect->top;
		width = pRect->right - pRect->left;
		height = pRect->bottom - pRect->top;
		BitBlt(hDC, left, top, width, height, mem_dc, left, top, SRCCOPY);
		SelectObject(mem_dc, old_bmp);
		DeleteDC(mem_dc);
	}
}

//
// Initialize control
//
void TCCD_Interface::OnCreate(void)
{
}

//
// Delete allocated GDI objects
//
void TCCD_Interface::OnDestroy(void)
{
	if (m_hBitmap) {
		DeleteObject(m_hBitmap);
		m_hBitmap = NULL;
	}
}

//
// Resize window
//
void TCCD_Interface::OnSize(void)
{
	RECT rc;

	if (!GetWindowRect(m_hWnd, &rc))
        return;

	// Resize bitmap on back-screen buffer
	int width = rc.right - rc.left;
	int height = rc.bottom - rc.top;
	if (width!=m_BmpWidth || height!=m_BmpHeight) {
		if (m_hBitmap)
			DeleteObject(m_hBitmap);
		m_BmpWidth = width;
		m_BmpHeight = height;
		HDC wnd_dc = GetDC(m_hWnd);
		m_hBitmap = CreateCompatibleBitmap(wnd_dc, m_BmpWidth, m_BmpHeight);
		ReleaseDC(m_hWnd, wnd_dc);
		OnSizeChanged();
		Invalidate();
	}
}

//
// Update HSB parameters
//
void TCCD_Interface::UpdateHSB(int nMin, int nMax, int nPage, int nPos)
{
	SCROLLINFO sinfo;

	sinfo.cbSize = sizeof(SCROLLINFO);
	sinfo.fMask = SIF_ALL;
	sinfo.nMin = nMin;
	sinfo.nMax = nMax;	
	sinfo.nPage = nPage;		
	sinfo.nPos = nPos;
	SetScrollInfo(m_hWnd, SB_HORZ, &sinfo, TRUE);
}

//
// Update VSB parameters
//
void TCCD_Interface::UpdateVSB(int nMin, int nMax, int nPage, int nPos)
{
	SCROLLINFO sinfo;

	sinfo.cbSize = sizeof(SCROLLINFO);
	sinfo.fMask = SIF_ALL;
	sinfo.nMin = nMin;
	sinfo.nMax = nMax;	
	sinfo.nPage = nPage;		
	sinfo.nPos = nPos;
	SetScrollInfo(m_hWnd, SB_VERT, &sinfo, TRUE);
}

//
// Horizontal scroll bar
//
void TCCD_Interface::OnHScroll(int nCode)
{
	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);
	info.fMask = SIF_POS | SIF_TRACKPOS;
	GetScrollInfo(m_hWnd, SB_HORZ, &info);

	switch (nCode)
	{
	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		info.nPos = info.nTrackPos;
		break;
	case SB_PAGEUP:
		info.nPos -= 100;
		break;
	case SB_PAGEDOWN:
		info.nPos += 100;
		break;
	case SB_LINEUP:
		info.nPos -= 1;
		break;
	case SB_LINEDOWN:
		info.nPos += 1;
		break;
	}
	if (nCode!=SB_ENDSCROLL && nCode!=SB_THUMBPOSITION) {
		// Changes the position of visible area
		info.fMask = SIF_POS;
		SetScrollPos(m_hWnd, SB_HORZ, info.nPos, TRUE);
		OnHSBChanged(info.nPos);
	}
}

//
// Vertical scroll bar
//
void TCCD_Interface::OnVScroll(int nCode)
{
	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);
	info.fMask = SIF_POS | SIF_TRACKPOS;
	GetScrollInfo(m_hWnd, SB_VERT, &info);
	
	switch (nCode)
	{
	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		info.nPos = info.nTrackPos;
		break;
	case SB_PAGEUP:
		info.nPos -= 100;
		break;
	case SB_PAGEDOWN:
		info.nPos += 100;
		break;
	case SB_LINEUP:
		info.nPos -= 1;
		break;
	case SB_LINEDOWN:
		info.nPos += 1;
		break;
	}

    if (nCode!=SB_ENDSCROLL && nCode!=SB_THUMBPOSITION) {
        // Changes the position of visible area
		SetScrollPos(m_hWnd, SB_VERT, info.nPos, TRUE);
		OnVSBChanged(info.nPos);
    }
}

// Open context menu
void TCCD_Interface::OnContextMenu(POINT *pt)
{
    HMENU hMenu = CreateCtxMenu();
    if (hMenu) {
	    HMENU hPopup = GetSubMenu(hMenu, 0);
        if (hPopup) {
			UpdateMenu(hPopup);
			int cmd = TrackPopupMenu(hPopup, TPM_NONOTIFY | TPM_RETURNCMD, pt->x, pt->y, 0, m_hWnd, NULL);
			if (cmd!=0)
				OnMenuCommand(cmd);
		}
		DestroyMenu(hMenu);
	}
}

// Create base menu
HMENU TCCD_Interface::CreateCtxMenu(void)
{
	HMENU hMenu = CreateMenu(), hPopup = CreateMenu();

	InsertMenu(hPopup, 0, MF_BYPOSITION | MF_STRING, CMD_HELP, "View help");
	InsertMenu(hPopup, 0, MF_BYPOSITION | MF_STRING, CMD_ABOUT, "About plug-in");
    InsertMenu(hMenu, 0, MF_BYPOSITION | MF_STRING | MF_POPUP, (UINT_PTR) hPopup, "");
	return hMenu;
}

// Context menu commands
void TCCD_Interface::OnMenuCommand(int iCmd)
{
	switch (iCmd)
	{
	case CMD_HELP:
		ShowHelp(GetTopicId());
		break;
	case CMD_ABOUT:
		ShowAboutDlg();
		break;
	}
}

// Show "About plugin" dialog
void TCCD_Interface::ShowAboutDlg(void)
{
	CAboutDlg dlg;
	dlg.ShowModal(m_hWnd);
}

// Show help
void TCCD_Interface::ShowHelp(int context_id)
{
	char helpfile[FILENAME_MAX+64], *ptr;
	GetModuleFileName(g_hInst, helpfile, FILENAME_MAX);
	ptr = strrchr(helpfile, '\\');
	if (ptr)
		strcpy_s(ptr+1, (FILENAME_MAX+64)-(ptr-helpfile)-1, "muniview.chm");

	if (context_id==0)
		HtmlHelp(GetParent(m_hWnd), helpfile, HH_DISPLAY_TOPIC, 0);
	else
		HtmlHelp(GetParent(m_hWnd), helpfile, HH_HELP_CONTEXT, context_id);
}
