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

                                 ChessV

                   COPYRIGHT (C) 2005 BY GREGORY STRONG

This file is part of ChessV.  ChessV 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.

ChessV 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; the file 'COPYING' contains the License text, but if for
some reason you need a copy, please write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

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


#include "StdAfx.h"
#include "ChessV.h"
#include "GUI/Brush.h"


// *** DEBUG MEMORY ALLOCATION *** //
#ifdef _DEBUG
#define new SAFE_NEW
#endif


COLORREF customColors[16];

DWORD g_crItems[] = 
{
	RGB(  0,   0,   0),	RGB(108,   0,   0),	RGB(128,  70,  70),	RGB(254,   0,   0),	RGB(255, 128, 128), RGB(255, 128,   0),	
	RGB(255, 255, 255), RGB(255, 255, 220), RGB(255, 255, 204), RGB(255, 255, 182), RGB(255, 255, 128),	RGB(255, 255,   0),	
	RGB(192, 192,  64), RGB(192, 152,  64), RGB(192, 128,  64), RGB(128, 128,  64),	RGB( 96, 128,  64), RGB(  0, 128,   0),	
	RGB( 93, 126, 126),	RGB( 63, 145, 133),	RGB( 63, 145, 108),	RGB(121, 197, 162),	RGB(180, 221, 199),	RGB(150, 175, 161),
	RGB( 97, 110, 133),	RGB(121, 137, 147),	RGB( 92, 125, 145),	RGB(151, 174, 190),	RGB(109, 157, 187), RGB(  0, 128, 192),
	RGB(  0, 128, 255),	RGB(  0, 121, 197),	RGB(104, 122, 181),	RGB(121, 113, 151),	RGB(144, 131, 184),	RGB(188, 179, 214),
	RGB( 89, 132, 189),	RGB(  0, 128, 128),	RGB(  0,  64,  98),	RGB(128, 128, 192),	RGB(188, 130, 155), RGB(255, 128, 192),
	RGB(255, 128, 255),	RGB(255,   0, 255), RGB(255,   0, 128),	RGB(128,   0, 255), RGB(154,  96, 137), RGB(221, 221, 221)
};

LRESULT CALLBACK textureSelectDlgProc
	( HWND hdlg, 
	  UINT message, 
	  WPARAM wParam, 
	  LPARAM lparam )
{
	static int selection;
	static HBITMAP hbm;
	LPDRAWITEMSTRUCT lpdis;
	HDC hdcMem;
	int x;

	switch( message )
	{
	  case WM_INITDIALOG:
		selection = 0;
		hbm = NULL;
		for( x = 0; x < nTextures; x++ )
		{
			::SendMessage( ::GetDlgItem( hdlg, IDC_TEXTURE_LIST_COMBO ), CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) textures[x] );
		}
		::SendMessage( ::GetDlgItem( hdlg, IDC_TEXTURE_LIST_COMBO ), CB_SETCURSEL, 0, 0 );
		return FALSE;

	  case WM_COMMAND:
		if( HIWORD(wParam) == CBN_SELCHANGE )
		{
			int oldSelection = selection;
			selection = (int) ::SendMessage( ::GetDlgItem( hdlg, IDC_TEXTURE_LIST_COMBO ), CB_GETCURSEL, 0, 0 );
			if( selection != oldSelection )
			{
				if( hbm != NULL )
				{
					::DeleteObject( hbm );
					hbm = NULL;
				}
				RECT r;
				r.left = r.top = 0;
				r.right = r.bottom = 500;
				::InvalidateRect( ::GetDlgItem( hdlg, IDC_TEXTURE_DISPLAY_BUTTON ), NULL, false );
			}
		}
		else if( LOWORD(wParam) == IDOK )
		{
			::EndDialog( hdlg, selection );
			return TRUE;
		}
		else if( LOWORD(wParam) == IDCANCEL )
		{
			::EndDialog( hdlg, -1 );
			return TRUE;
		}
		return TRUE;

	  case WM_DRAWITEM:
		lpdis = (LPDRAWITEMSTRUCT) lparam;
		hdcMem = ::CreateCompatibleDC( lpdis->hDC ); 
		if( hbm == NULL )
		{
			char filename[100];
			sprintf( filename, "images\\textures\\%s\\sample_image.bmp", textureDirectories[selection] );
			hbm = (HBITMAP) ::LoadImage( NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
		}
		::SelectObject( hdcMem, hbm );
		::BitBlt( lpdis->hDC, 0, 0, lpdis->rcItem.right, lpdis->rcItem.bottom, hdcMem, 0, 0, SRCCOPY );
		::DeleteDC( hdcMem );
		return TRUE;
	}
	return FALSE;
}

LRESULT CALLBACK colorPickerDlgProc
	( HWND hdlg, 
	  UINT message, 
	  WPARAM wParam, 
	  LPARAM lparam )
{
	switch( message )
	{
	  case WM_INITDIALOG:
		for( int nColor = 0; nColor < sizeof(g_crItems)/sizeof(DWORD); nColor++ )
		{
			::SendDlgItemMessage( hdlg, IDC_COLOR_LIST, LB_ADDSTRING, nColor, (LPARAM) "" );
			::SendDlgItemMessage( hdlg, IDC_COLOR_LIST, LB_SETITEMDATA , nColor, (LPARAM) g_crItems[nColor] );
			if( *colorBeingSelected == g_crItems[nColor] )
				::SendDlgItemMessage( hdlg, IDC_COLOR_LIST, LB_SETCURSEL, nColor, 0 );
		}
		return TRUE;

	case WM_MEASUREITEM:
		RECT rc;
		LPMEASUREITEMSTRUCT lpmis; 
		lpmis = (LPMEASUREITEMSTRUCT) lparam; 
		GetWindowRect(GetDlgItem(hdlg, lpmis->CtlID), &rc);
		lpmis->itemHeight = (rc.bottom-rc.top)/6; 
		lpmis->itemWidth = (rc.right-rc.left)/8;
		break;

	case WM_CTLCOLORLISTBOX:
		return (LRESULT) CreateSolidBrush(GetSysColor(COLOR_3DFACE));

	case WM_DRAWITEM:
		HDC hdc;
		COLORREF	cr;
		HBRUSH		hbrush;

		DRAWITEMSTRUCT * pdis;

		pdis = (DRAWITEMSTRUCT *)lparam;
		hdc = pdis->hDC;
		rc = pdis->rcItem;

		// Transparent.
		SetBkMode(hdc,TRANSPARENT);

		// NULL object
		if (pdis->itemID == -1) return 0; 

		switch (pdis->itemAction)
		{
		case ODA_DRAWENTIRE:
			switch (pdis->CtlID)
			{
			case IDC_COLOR_LIST:
				rc = pdis->rcItem;
				cr = (COLORREF) pdis->itemData;
				InflateRect(&rc, -3, -3);
				hbrush = CreateSolidBrush((COLORREF)cr);
				FillRect(hdc, &rc, hbrush);
				DeleteObject(hbrush);
				FrameRect(hdc, &rc, (HBRUSH) GetStockObject(GRAY_BRUSH));
				break;
			}
			// *** FALL THROUGH *** //
		case ODA_SELECT:
			rc = pdis->rcItem;
			if (pdis->itemState & ODS_SELECTED)
			{
				rc.bottom--;
				rc.right--;
				// Draw the lighted side.
				HPEN hpen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
				HPEN holdPen = (HPEN)SelectObject(hdc, hpen);
				MoveToEx(hdc, rc.left, rc.bottom, NULL);
				LineTo(hdc, rc.left, rc.top);
				LineTo(hdc, rc.right, rc.top);
				SelectObject(hdc, holdPen);
				DeleteObject(hpen);
				// Draw the darkened side.
				hpen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
				holdPen = (HPEN)SelectObject(hdc, hpen);
				LineTo(hdc, rc.right, rc.bottom);
				LineTo(hdc, rc.left, rc.bottom);
				SelectObject(hdc, holdPen);
				DeleteObject(hpen);
			}
			else 
			{
				hbrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
				FrameRect(hdc, &rc, hbrush);
				DeleteObject(hbrush);
			}
			break;
		case ODA_FOCUS:
			rc = pdis->rcItem;
			InflateRect(&rc, -2, -2);
			DrawFocusRect(hdc, &rc);
			break;
		default:
			break;
		}
		return true;

	case WM_COMMAND:
		if( LOWORD(wParam) == IDOK ) 
		{
			int nItem = (int) ::SendDlgItemMessage( hdlg, IDC_COLOR_LIST, LB_GETCURSEL, 0, 0L );
			*colorBeingSelected = (COLORREF) ::SendDlgItemMessage( hdlg, IDC_COLOR_LIST, LB_GETITEMDATA, nItem, 0L );
			textureBeingSelected = -1;
			::EndDialog( hdlg, LOWORD(wParam) );
			::InvalidateRect( GetParent(hdlg), NULL, true );
			return TRUE;
		}
		else if( LOWORD(wParam) == IDCANCEL )
		{
			::EndDialog( hdlg, LOWORD(wParam) );
			return TRUE;
		}
		else if( LOWORD(wParam) == ID_OTHER )
		{
			CHOOSECOLOR cc;
			cc.lStructSize = sizeof(CHOOSECOLOR);
			cc.hwndOwner = theWindow;
			cc.hInstance = NULL;
			cc.rgbResult = *colorBeingSelected;
			cc.lpCustColors = customColors;
			cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
			cc.lCustData = NULL;
			cc.lpTemplateName = NULL;
			cc.lpfnHook = NULL;
			BOOL rtn = ::ChooseColor( &cc );
			if( rtn != 0 )
			{
				if( cc.rgbResult == RGB(255, 0, 0) )
					cc.rgbResult = RGB(254, 0, 0);
				*colorBeingSelected = cc.rgbResult;
				textureBeingSelected = -1;
				::EndDialog( hdlg, IDOK );
				::InvalidateRect( ::GetParent( hdlg ), NULL, true );
				return TRUE;
			}
		}
		else if( LOWORD(wParam) == ID_TEXTURES )
		{
			int sel = (int) ::DialogBox( theInstance, (LPCTSTR) IDD_TEXTURE_SELECT_DIALOG, hdlg, (DLGPROC) textureSelectDlgProc );
			if( sel >= 0 )
			{
				textureBeingSelected = sel;
				::EndDialog( hdlg, IDOK );
				::InvalidateRect( ::GetParent( hdlg ), NULL, true );
			}
		}
		break;
	}
    return FALSE;
}

void DrawTransparentBitmap
	( HDC hdc, 
	  HBITMAP hBitmap, 
	  short xStart,
	  short yStart, 
	  COLORREF cTransparentColor,
	  bool centerInSquare,
	  COLORREF colorToReplace, 
	  COLORREF replacementColor,
	  bool doubleSize,
	  bool centerOnCursor )
{
	BITMAP bm;
	COLORREF cColor;
	HBITMAP bmAndBack, bmAndObject, bmAndMem, bmSave, bmColor;
	HBITMAP bmBackOld, bmObjectOld, bmMemOld, bmSaveOld, bmColorOld;
	HDC hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave, hdcColor;
	POINT ptSize;
	RECT rect;

	hdcTemp = ::CreateCompatibleDC( hdc );
	::SelectObject( hdcTemp, hBitmap );   // Select the bitmap

	::GetObject( hBitmap, sizeof(BITMAP), (LPSTR) &bm );
	ptSize.x = bm.bmWidth;          // Get width of bitmap
	ptSize.y = bm.bmHeight;         // Get height of bitmap
	DPtoLP( hdcTemp, &ptSize, 1 );  // Convert from device
									// to logical points

	if( centerOnCursor )
	{
		xStart -= (short) (ptSize.x / (doubleSize == true ? 1 : 2));
		yStart -= (short) (ptSize.y / (doubleSize == true ? 1 : 2));
	}
	else if( centerInSquare )
	{
		xStart += (short) ((theBoard->GetSquareWidth() - ptSize.x) / 2);
		yStart += (short) ((theBoard->GetSquareHeight() - ptSize.y) / 2);
	}

	//	create some DCs to hold temporary data
	hdcBack   = ::CreateCompatibleDC( hdc );
	hdcObject = ::CreateCompatibleDC( hdc );
	hdcMem    = ::CreateCompatibleDC( hdc );
	hdcSave   = ::CreateCompatibleDC( hdc );
	hdcColor  = ::CreateCompatibleDC( hdc );

	//	create a bitmap for each DC. DCs are required for a number of
	//	GDI functions

	//	monochrome DCs
	bmAndBack   = ::CreateBitmap( ptSize.x, ptSize.y, 1, 1, NULL );
	bmAndObject = ::CreateBitmap( ptSize.x, ptSize.y, 1, 1, NULL );

	bmAndMem    = ::CreateCompatibleBitmap( hdc, ptSize.x, ptSize.y );
	bmSave      = ::CreateCompatibleBitmap( hdc, ptSize.x, ptSize.y );
	bmColor     = ::CreateCompatibleBitmap( hdc, ptSize.x, ptSize.y );

	//	each DC must select a bitmap object to store pixel data
	bmBackOld   = (HBITMAP) ::SelectObject( hdcBack, bmAndBack );
	bmObjectOld = (HBITMAP) ::SelectObject( hdcObject, bmAndObject );
	bmMemOld    = (HBITMAP) ::SelectObject( hdcMem, bmAndMem );
	bmSaveOld   = (HBITMAP) ::SelectObject( hdcSave, bmSave );
	bmColorOld  = (HBITMAP) ::SelectObject( hdcColor, bmColor );

	//	set proper mapping mode
	::SetMapMode( hdcTemp, ::GetMapMode( hdc ) );

	//	save the bitmap sent here, because it will be overwritten
	::BitBlt( hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY );

	//	set the background color of the source DC to the color
	//	contained in the parts of the bitmap that should be transparent
	cColor = ::SetBkColor( hdcTemp, cTransparentColor );

	//	create the object mask for the bitmap by performing a BitBlt
	//	from the source bitmap to a monochrome bitmap
	::BitBlt( hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY );

	//	set the background color of the source DC back to the original color
	::SetBkColor( hdcTemp, cColor );

	//	create the inverse of the object mask
	::BitBlt( hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY );

	//	copy the background of the main DC to the destination
	::BitBlt( hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, xStart, yStart, SRCCOPY );

	//	mask out the places where the bitmap will be placed
	::BitBlt( hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND );

	//	mask out the transparent colored pixels on the bitmap
	::BitBlt( hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND );

	//	XOR the bitmap with the background on the destination DC
	::BitBlt( hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT );

	//	copy mem DC back to hdcTemp; we will now use hdcTemp to do color replacement
	::BitBlt( hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY );

	//	set the background color of the source DC to the color to replace
	cColor = ::SetBkColor( hdcTemp, colorToReplace );
	
	//	create the mask representing the parts of the bitmap to be replaced
	::BitBlt( hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY );

	//	fill color DC with replacement color
	rect.top = 0;
	rect.left = 0;
	rect.right = 1000;
	rect.bottom = 1000;
	Brush br( replacementColor );
	::FillRect( hdcColor, &rect, br );

	//	turn the color DC black except where the mask is white
	::BitBlt( hdcColor, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND );

	//	create inverted mask
	Brush br2( RGB(255,255,255) );
	::FillRect( hdcObject, &rect, br2 );
	::BitBlt( hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCINVERT );

	//	AND the inverted mask with the picture turning the picture black 
	//	where the color is to be replaced.  the rest of the picture is not affected.
	::SetBkColor( hdcTemp, RGB(255,255,255) );
	::BitBlt( hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND );

	//	finally, OR the coloured item with the picture
	::BitBlt( hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcColor, 0, 0, SRCPAINT );

	//	copy hdcTemp back to hdcMem
	::BitBlt( hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY );

	//	set the background color of the source DC back to the original color
	::SetBkColor( hdcTemp, cColor );

	if( doubleSize )
	{
		//	copy the destination to the screen
		::StretchBlt( hdc, xStart, yStart, ptSize.x * 2, ptSize.y * 2, hdcMem, 0, 0, ptSize.x, ptSize.y, SRCCOPY );
	}
	else
	{
		//	copy the destination to the screen
		::BitBlt( hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY );
	}

	//	place the original bitmap back into the bitmap sent here
	::BitBlt( hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY );

	//	delete the memory bitmaps
	::DeleteObject( ::SelectObject( hdcBack, bmBackOld ) );
	::DeleteObject( ::SelectObject( hdcObject, bmObjectOld ) );
	::DeleteObject( ::SelectObject( hdcMem, bmMemOld ) );
	::DeleteObject( ::SelectObject( hdcSave, bmSaveOld ) );
	::DeleteObject( ::SelectObject( hdcColor, bmColorOld ) );

	//	delete the memory DCs
	::DeleteDC( hdcMem );
	::DeleteDC( hdcBack );
	::DeleteDC( hdcObject );
	::DeleteDC( hdcSave );
	::DeleteDC( hdcColor );
	::DeleteDC( hdcTemp );
}
