// XTPAnimationDC.cpp : implementation file
//
// This file is a part of the XTREME CONTROLS MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "Common/XTPColorManager.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPVC80Helpers.h"

#include "XTPAnimationDC.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

CXTPAnimationDC::LPFNANIMATION CXTPAnimationDC::m_pCustomAnimation = 0;

//===========================================================================
// CXTPAnimationDC class
//===========================================================================

CXTPAnimationDC::CXTPAnimationDC(HDC hDestDC, const CRect& rcPaint)
	: m_hDestDC (hDestDC)
{
	m_rect = rcPaint;
	Attach (::CreateCompatibleDC (m_hDestDC));
	if (!m_hDC)
		return;

	m_bitmap.Attach (::CreateCompatibleBitmap(
		m_hDestDC, m_rect.Width(), m_rect.Height()));

	m_hOldBitmap = ::SelectObject (m_hDC, m_bitmap);
}

CXTPAnimationDC::CXTPAnimationDC(HDC hDestDC, const CRect& rcPaint, const CXTPPaintManagerColorGradient& clrBack, const BOOL bHorz /*=FALSE*/)
	: m_hDestDC (hDestDC)
{
	m_rect = rcPaint;
	Attach (::CreateCompatibleDC (m_hDestDC));
	if (!m_hDC)
		return;

	m_bitmap.Attach (::CreateCompatibleBitmap(
		m_hDestDC, m_rect.Width(), m_rect.Height()));
	m_hOldBitmap = ::SelectObject (m_hDC, m_bitmap);

	if (!clrBack.IsNull())
	{
		XTPDrawHelpers()->GradientFill(this, CRect(0, 0, m_rect.Width(), m_rect.Height()), clrBack, bHorz);
	}
}

CXTPAnimationDC::~CXTPAnimationDC()
{
	if (!m_hDC)
		return;

	if (m_hDestDC)
	{
		::BitBlt (m_hDestDC, m_rect.left, m_rect.top, m_rect.Width(),
			m_rect.Height(), m_hDC, 0, 0, SRCCOPY);
	}
	::SelectObject (m_hDC, m_hOldBitmap);
}

#define BIT(a, b) (a & (1 << b))

void CXTPAnimationDC::Animate(int nType, int nSteps, int nAnimationTime)
{
	//
	// lets say the animation is done in m_nSteps and it should not take longer
	// then m_nAnimationTime then we calculate as follows
	//

	switch (nType)
	{
		case xtpAniDefault:
			{
				//
				// This registry entry is defined and explained under the
				// following URLs
				//
				// http://www.microsoft.com/WINDOWS2000/techinfo/reskit/en/Regentry/55198.htm
				// http://msdn.microsoft.com/library/en-us/regentry/55198.asp
				// http://msdn.microsoft.com/library/default.asp ?url=/library/en-us/regentry/regazbd.asp

				HKEY hKey = NULL;
				DWORD dwType = 0, dwValue = 0, dwSize = sizeof(dwValue);
				::RegOpenKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"), &hKey);
				::RegQueryValueEx(hKey, _T("UserPreferencesMask"), NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
				::RegCloseKey(hKey);

				if (BIT(dwValue, 1)) // is Menu animation enabled ?
				{
					if (BIT(dwValue, 9)) // if so, which animation ?
					{
						nType = xtpAniFade;
					}
					else
					{
						nType = xtpAniSlide;
					}
					// windows doesn't support more settings (currently)
				}
			}
			break;

		// choose any animation based on a random number
		case xtpAniRandom:
			{
				switch (RAND_S() % 3)
				{
					case 0:
						nType = xtpAniFade;
						break;

					case 1:
						nType = xtpAniSlide;
						break;

					default:
						nType = xtpAniUnfold;
						break;
				}
			}
			break;
	}

	LPFNANIMATION Animate = m_pCustomAnimation ? m_pCustomAnimation : DefaultAnimation;
	Animate(m_rect, CDC::FromHandle(m_hDestDC), this, nType, nSteps, nAnimationTime);
}

void CXTPAnimationDC::DefaultAnimation(CRect rc, CDC* pDestDC, CDC* pSrcDC, int nType, int nSteps, int nAnimationTime)
{
	CRect r;
	switch (nType)
	{
		case xtpAniSlide:
			{
				const int bottom = rc.bottom - rc.top;
				int i;
				for (i = 0; i < rc.Height();
					i += (1 + (rc.Height() / nSteps)))
				{
					pDestDC->BitBlt(rc.left, rc.top, rc.Width(), i, pSrcDC,
						0, bottom - i, SRCCOPY);
					Sleep(nAnimationTime / nSteps);
				}
			}
			break;

		case xtpAniUnfold:
			{
				r = rc;
				int i;
				for (i = 0; i < nSteps; ++i)
				{
					r.bottom = r.top + rc.Height() * (i + 1) / nSteps;
					r.right = r.left + rc.Width() * (i + 1) / nSteps;
					pDestDC->BitBlt(r.left, r.top, r.Width(), r.Height(), pSrcDC,
						0, 0, SRCCOPY);
					Sleep(nAnimationTime / nSteps);
				}
			}
			break;

		case xtpAniFade:
			{

				const int cx = rc.Width();
				const int cy = rc.Height();

				BITMAPINFOHEADER BMI;
				// Fill in the header info.
				ZeroMemory (&BMI, sizeof(BMI));
				BMI.biSize = sizeof(BITMAPINFOHEADER);
				BMI.biWidth = cx;
				BMI.biHeight = cy;
				BMI.biPlanes = 1;
				BMI.biBitCount = 32;
				BMI.biCompression = BI_RGB;   // No compression

				BYTE * pSrcBits = NULL;
				HBITMAP hbmSrc = CreateDIBSection (NULL, (BITMAPINFO *)&BMI, DIB_RGB_COLORS, (void **)&pSrcBits, 0, 0l);

				BYTE * pSrcBackBits = NULL;
				HBITMAP hbmSrcBack = CreateDIBSection (NULL, (BITMAPINFO *)&BMI, DIB_RGB_COLORS, (void **)&pSrcBackBits, 0, 0l);

				BYTE * pDestBits = NULL;
				HBITMAP hbmDest = CreateDIBSection (NULL, (BITMAPINFO *)&BMI, DIB_RGB_COLORS, (void **)&pDestBits, 0, 0l);

				// Copy our source and destination bitmaps onto our DIBSections,
				// so we can get access to their bits using the BYTE *'s we passed into CreateDIBSection

				CDC dc;
				dc.CreateCompatibleDC(NULL);
				HBITMAP hbmpOld = (HBITMAP) ::SelectObject(dc, hbmSrc);
				::BitBlt(dc, 0, 0, cx, cy, pSrcDC->GetSafeHdc(), 0, 0, SRCCOPY);

				::SelectObject(dc, hbmSrcBack);
				::BitBlt(dc, 0, 0, cx, cy, pDestDC->GetSafeHdc (), rc.left, rc.top, SRCCOPY);

				::SelectObject(dc, hbmDest);

				DWORD dwTimePer = nAnimationTime / nSteps;
				::SelectObject(dc, hbmDest);
				for (int i = 1; i < nSteps; ++i)
				{
					DWORD dwTime = GetTickCount ();
					AlphaBlendU(pDestBits, pSrcBackBits, cx, cy, pSrcBits, (BYTE)((255*i) / nSteps));
					pDestDC->BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &dc, 0, 0, SRCCOPY);
					dwTime = GetTickCount () - dwTime;
					if (dwTime < dwTimePer)
					{
						Sleep(dwTimePer - dwTime);
					}
				}
				::SelectObject(dc, hbmpOld);
				DeleteObject(hbmSrc);
				DeleteObject(hbmSrcBack);
				DeleteObject(hbmDest);
			}
			break;
	}
}

void CXTPAnimationDC::AlphaBlendU(PBYTE pDest, PBYTE pSrcBack, int cx, int cy, PBYTE pSrc, BYTE byAlpha)
{
	BYTE byDiff = (BYTE)(255 - byAlpha);

	int iRowOffset = 0, j = cy;
	while (j > 0)
	{
		--j;
		LPBYTE pbSrcRGB = (LPBYTE)&((DWORD*)pSrc)[iRowOffset];
		LPBYTE pbSrcBackRGB = (LPBYTE)&((DWORD*)pSrcBack)[iRowOffset];
		LPBYTE pbDestRGB = (LPBYTE)&((DWORD*)pDest)[iRowOffset];
		iRowOffset += cx;
		int i = cx;
		while (i > 0)
		{
			--i;
			pbDestRGB[0] = (BYTE)((pbSrcBackRGB[0] * byDiff + pbSrcRGB[0] * byAlpha) >> 8);
			pbDestRGB[1] = (BYTE)((pbSrcBackRGB[1] * byDiff + pbSrcRGB[1] * byAlpha) >> 8);
			pbDestRGB[2] = (BYTE)((pbSrcBackRGB[2] * byDiff + pbSrcRGB[2] * byAlpha) >> 8);
			pbSrcRGB += 4;
			pbSrcBackRGB += 4;
			pbDestRGB += 4;
		}
	}
}

void CXTPAnimationDC::SetCustomAnimation(LPFNANIMATION pCustom)
{
	m_pCustomAnimation = pCustom;
}