// gifshow.cpp : Defines the entry point for the application.
//
// Simple test program for testing CGIFWin class.  Skeleton Windows
// application generated by Microsoft Visual C++.
//
//	Licensed under the terms laid out in the libungif COPYING file.
//
// Usage:
//   pass the GIF file as a command-line parameter or drag-and-drop
//   files from Explorer onto the main window
//
// Known Limitations:
//   1. Does not support palette messages so
//		test only on 16-bit/24-bit color

#include "stdafx.h"
#include <windowsx.h>
#include <process.h>
#include <shellapi.h>
#include "resource.h"
#include "GifWin.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// The title bar text

// List of files to display and pointer to current file
LPTSTR pszFileList = NULL;
LPTSTR pszCurrentFile = NULL;

int iViewScale = 0;                             // View size (-1=50, 0=100, 1=200)
BOOL bAnimate = TRUE;
HANDLE hFrameEvent = NULL;

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);


CGIFWin gif;


HWND ghwndGIF = NULL;

#define PALETTE_SUPPORT

#ifdef PALETTE_SUPPORT
	HPALETTE hPalette = NULL;
#endif // PALETTE_SUPPORT

void _cdecl DrawGifThread(LPVOID /*pParam*/)
{
	while (ghwndGIF)
	{
		COLORREF rgbBack = ::GetSysColor(COLOR_BTNFACE);

		int iLength = lstrlen(pszCurrentFile);
		if (iLength)
		{
			// Set Window Title (show filename)
			TCHAR szWindowTitle[MAX_PATH + MAX_LOADSTRING];
			::wsprintf(szWindowTitle, "%s - %s", szTitle, pszCurrentFile);
			if (ghwndGIF)
				::SetWindowText(ghwndGIF, szWindowTitle);
            UINT uLoop = 0;

			// Draw animated GIF to Windows Device Context (HDC)
			if (gif.Open(pszCurrentFile, rgbBack) == 0)
			{
				while (ghwndGIF)
				{
					int delay = gif.NextImage();
					if (ghwndGIF)
					{
						::InvalidateRect(ghwndGIF, NULL, FALSE);
						::UpdateWindow(ghwndGIF);
					}
					if (delay <= 0)
					{
						TRACE("NextImage() returned %d\n", delay);

                        // Limit loops to 2 loops (3 times)
                        UINT uLoopCount = gif.GetLoopCount();
                        if (uLoopCount > 2)
                            uLoopCount = 2;

                        // Exit loop if loop count expired
                        // or there is an error closing/reopening the file
                        if (++uLoop > uLoopCount)
                            break;

                        TRACE("Loop count=%d\n", uLoop);
                        gif.Close();
                        if (gif.Open(pszCurrentFile, rgbBack) != 0)
                        {
                            break;
                        }
					}
                    else
                    {
                        ASSERT( hFrameEvent );
                        ::WaitForSingleObject(hFrameEvent, bAnimate ? delay : INFINITE);
                    }
				}
				gif.Close();
				// Skip to next filename
				pszCurrentFile += (iLength + 1);
				::Sleep(500);
				if (ghwndGIF)
				{
					::SetWindowText(ghwndGIF, szTitle);
					::InvalidateRect(ghwndGIF, NULL, TRUE);
				}
			}
		}
		else // (iLength == 0)
		{
			::DragAcceptFiles(ghwndGIF, TRUE);
		}
	} // while
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE /*hPrevInstance*/,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	MSG msg;
	HACCEL hAccelTable;

	TRACE("WinMain: lpCmdLine=%s\n", lpCmdLine);

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_GIFSHOW, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Set current file to command line and convert spaces to separating '\0' nulls
	pszCurrentFile = lpCmdLine;
	LPTSTR pszSep = lpCmdLine;
	while (pszSep)
	{
		pszSep = strchr(pszSep, ' ');
		if (pszSep == NULL)
			break;
		*pszSep++ = '\0';
	}

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_GIFSHOW);
    hFrameEvent = ::CreateEvent(NULL, /*bManualReset=*/ FALSE, /*bInitialState=*/ FALSE, _T("frame"));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

    CloseHandle(hFrameEvent);

	return msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	HICON hIcon = (HICON) ::LoadImage(hInstance, MAKEINTRESOURCE(IDI_GIFSHOW), IMAGE_ICON, 16,16, LR_DEFAULTSIZE);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= hIcon;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_BTNFACE+1);
	wcex.lpszMenuName	= (LPCSTR)IDC_GIFSHOW;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= hIcon;

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, 500, 250, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

void UpdateMenu(HWND hWnd)
{
    HMENU hMenu = GetMenu(hWnd);
    CheckMenuRadioItem(hMenu, IDM_VIEW_25, IDM_VIEW_400, IDM_VIEW_100 + iViewScale, MF_BYCOMMAND);
    CheckMenuItem(hMenu, ID_VIEW_ANIMATE, bAnimate ? MF_CHECKED : MF_UNCHECKED);
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	TCHAR szHello[MAX_LOADSTRING];
	LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

	switch (message) 
	{
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			// Parse the menu selections:
			switch (wmId)
			{
				case IDM_ABOUT:
				   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
				   break;
				case IDM_VIEW_25:
				case IDM_VIEW_33:
				case IDM_VIEW_50:
				case IDM_VIEW_100:
				case IDM_VIEW_200:
				case IDM_VIEW_300:
				case IDM_VIEW_400:
                    iViewScale = wmId - IDM_VIEW_100;
                    InvalidateRect(hWnd, NULL, TRUE);
                    UpdateMenu(hWnd);
                    break;
                case ID_VIEW_ANIMATE:
                    bAnimate = !bAnimate;
                    UpdateMenu(hWnd);
                    break;
                case ID_VIEW_FRAME:
                    bAnimate = FALSE;
                    if (hFrameEvent)
                        SetEvent(hFrameEvent);
                    UpdateMenu(hWnd);
                    break;
				case IDM_EXIT:
				   DestroyWindow(hWnd);
				   break;
				default:
				   return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
		case WM_DROPFILES:
		{
			HDROP hDrop = (HDROP) wParam;

			// Don't accept more dropped files
			::DragAcceptFiles(hWnd, FALSE);

			// Get number of files dropped
			const int iFiles = ::DragQueryFile(hDrop, ~0U, NULL, 0);

			// Free previous file list (if allocated)
			if (pszFileList)
			{
				GlobalFreePtr(pszFileList);
			}

			// Allocate buffer to hold list of files
			pszFileList = (LPTSTR) GlobalAllocPtr(GMEM_ZEROINIT, iFiles * MAX_PATH);
			if (pszFileList)
			{
				LPTSTR pFile = pszFileList;
				for (int iIndex = 0; iIndex < iFiles; ++iIndex)
				{
					if (::DragQueryFile(hDrop, iIndex, pFile, MAX_PATH))
					{
						TRACE("WM_DROPFILES (%d of %d) szPathName=%s\n", iIndex, iFiles, pFile);
						// Add terminating '\n' between files
						int iLength = lstrlen(pFile);
						pFile += (iLength + 1);
					}
				}
				*pFile++ = '\0';
				pszCurrentFile = pszFileList;
			}
			DragFinish(hDrop);
			break;
		}
		case WM_CREATE:
		{
			HDC hDC = ::GetDC(hWnd);
#ifdef PALETTE_SUPPORT
			hPalette = ::CreateHalftonePalette(hDC);
#endif // PALETTE_SUPPORT
			::ReleaseDC(hWnd, hDC);
			ghwndGIF = hWnd;
			_beginthread(DrawGifThread, 0, hWnd);
            UpdateMenu(hWnd);
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		case WM_PAINT:
		{	
			RECT rect;
			GetClientRect(hWnd, &rect);

			HDC hDC = BeginPaint(hWnd, &ps);
#ifdef PALETTE_SUPPORT
			// Select and realize palette (for 8-bit displays)
			HPALETTE hOldPal = ::SelectPalette(hDC, hPalette, TRUE);
			::RealizePalette(hDC);
			gif.Draw(hDC, &rect, iViewScale);
			if (hOldPal)
				::SelectPalette(hDC, hOldPal, TRUE);
#else
			gif.Draw(hDC, &rect, iViewScale);
#endif // PALETTE_SUPPORT
			EndPaint(hWnd, &ps);
			break;
		}
		case WM_DESTROY:
		{
			ghwndGIF = NULL;
			Sleep(1000);			// Wait for thread to self destruct

			// Free previous file list (if allocated)
			if (pszFileList)
			{
				TRACE("Free pszFileList!\n");
				GlobalFreePtr(pszFileList);
				pszFileList = NULL;
			}
#ifdef PALETTE_SUPPORT
			if (hPalette)
			{
				TRACE("Delete palette!\n");
				::DeleteObject(hPalette);
				hPalette = NULL;
			}
#endif // PALETTE_SUPPORT
			PostQuitMessage(0);
			break;
		}
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/)
{
	switch (message)
	{
		case WM_INITDIALOG:
				return TRUE;

		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
				EndDialog(hDlg, LOWORD(wParam));
				return TRUE;
			}
			break;
	}
    return FALSE;
}
