/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_fpicker.hxx"

//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------

#include <tchar.h>
#include "dibpreview.hxx"
#include <osl/diagnose.h>

#ifndef _COM_SUN_STAR_UI_DIALOG_FILEPREVIEWIMAGEFORMATS_HPP_
#include <com/sun/star/ui/dialogs/FilePreviewImageFormats.hpp>
#endif

#ifndef _USTRING_HXX_
#include <rtl/ustring.hxx>
#endif

#include <stdexcept>
#include <string>

//------------------------------------------------------------------------
//
//------------------------------------------------------------------------

using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::RuntimeException;
using ::com::sun::star::uno::Any;
using ::com::sun::star::lang::IllegalArgumentException;
using rtl::OUString;

//------------------------------------------------------------------------
//
//------------------------------------------------------------------------

namespace /* private */
{
	const LPTSTR CURRENT_INSTANCE = TEXT("CurrInst");
};

//------------------------------------------------------------------------
// defines
//------------------------------------------------------------------------

#define PREVIEWWND_CLASS_NAME TEXT("DIBPreviewWnd###")

// means 3 pixel left and 3 pixel right
#define HORZ_BORDER_SPACE	6

// means 3 pixel top and 3 pixel bottom
#define VERT_BORDER_SPACE	6

//---------------------------------------------------
// static member initialization
//---------------------------------------------------

osl::Mutex CDIBPreview::s_Mutex;
ATOM CDIBPreview::s_ClassAtom = 0;
sal_Int32 CDIBPreview::s_RegisterDibPreviewWndCount = 0;

//---------------------------------------------------
//
//---------------------------------------------------

CDIBPreview::CDIBPreview(HINSTANCE instance,HWND parent,sal_Bool bShowWindow) :
	m_Instance(instance)
{
	RegisterDibPreviewWindowClass();

	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

	if (bShowWindow)
		dwStyle |= WS_VISIBLE;

	m_Hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,
		PREVIEWWND_CLASS_NAME,
		TEXT(""),
		dwStyle,
		0, 0, 0, 0,
		parent,
		(HMENU)0x0, // for child windows this will
					// be used as child window identifier
		m_Instance,
		(LPVOID)this // pass a pointer to the current
					 // instance of this class
	);

	bool bSuccess = IsWindow(m_Hwnd);

	OSL_POSTCOND(bSuccess,"Could not create preview window");

	if (!bSuccess)
	{
		UnregisterDibPreviewWindowClass();
		throw std::runtime_error("Could not create preview window");
	}
}

//---------------------------------------------------
//
//---------------------------------------------------

CDIBPreview::~CDIBPreview( )
{
	// remember: we don't have to destroy the
	// preview window because it will be destroyed
	// by its parent window (the FileOpen dialog)
	// but we have to unregister the window class
	//if ( m_bWndClassRegistered )
	UnregisterDibPreviewWindowClass();
}

//-------------------------------
//
//-------------------------------

sal_Int32 SAL_CALL CDIBPreview::getTargetColorDepth() throw (RuntimeException)
{
	HDC hdc = GetDC(m_Hwnd);
	int clrRes = 0;

	if (hdc)
		clrRes = GetDeviceCaps(hdc, COLORRES);

	return clrRes;
}

//-------------------------------
//
//-------------------------------

sal_Int32 SAL_CALL CDIBPreview::getAvailableWidth() throw (RuntimeException)
{
	RECT rect;
	bool bRet = GetClientRect(m_Hwnd,&rect);

	sal_Int32 cx = 0;

	if ( bRet )
		cx = rect.right;

	return cx;
}

//-------------------------------
//
//-------------------------------

sal_Int32 SAL_CALL CDIBPreview::getAvailableHeight() throw (RuntimeException)
{
	RECT rect;
	bool bRet = GetClientRect(m_Hwnd,&rect);

	sal_Int32 cy = 0;

	if ( bRet )
		cy = rect.bottom;

	return cy;
}

//-------------------------------
//
//-------------------------------

void SAL_CALL CDIBPreview::setImage(sal_Int16 aImageFormat, const Any& aImage)
	throw (IllegalArgumentException, RuntimeException)
{
	PreviewBase::setImage(aImageFormat,aImage);

	// if the any has no value we have an
	// empty Sequence which clears the
	// preview window
	osl::ClearableMutexGuard aGuard(m_PaintLock);

	m_Image.realloc(0);
	m_ImageData >>= m_Image;

	aGuard.clear();

	InvalidateRect(m_Hwnd,NULL,sal_False);
	UpdateWindow(m_Hwnd);
}

//-------------------------------
//
//-------------------------------

sal_Bool SAL_CALL CDIBPreview::setShowState(sal_Bool bShowState) throw (RuntimeException)
{
	PreviewBase::setShowState(bShowState);
	ShowWindow(m_Hwnd, m_bShowState ? SW_SHOW : SW_HIDE);
	return sal_True;
}

//-------------------------------
//
//-------------------------------

sal_Bool SAL_CALL CDIBPreview::getShowState() throw (RuntimeException)
{
	return (sal_Bool)IsWindowVisible(m_Hwnd);
}

//-------------------------------
//
//-------------------------------

HWND SAL_CALL CDIBPreview::getWindowHandle() const
{
	return m_Hwnd;
}

//---------------------------------------------------
//
//---------------------------------------------------

void SAL_CALL CDIBPreview::onPaint(HWND hWnd, HDC hDC)
{
	BITMAPFILEHEADER*  pbmfh;
	BITMAPINFO      *  pbmi;
	sal_uInt8            *  pBits;
	int                cxDib;
	int                cyDib;

	osl::MutexGuard aGuard(m_PaintLock);

	try
	{
		pbmfh = reinterpret_cast<BITMAPFILEHEADER*>(m_Image.getArray());

		if ( !IsBadReadPtr( pbmfh, sizeof(BITMAPFILEHEADER)) &&
			 (pbmfh->bfType == ('B' | ('M' << 8))) )
		{
			pbmi  = reinterpret_cast<BITMAPINFO*>((pbmfh + 1));
			pBits = reinterpret_cast<sal_uInt8*>(((DWORD)pbmfh) + pbmfh->bfOffBits);

			cxDib =      pbmi->bmiHeader.biWidth;
			cyDib = abs (pbmi->bmiHeader.biHeight);

			SetStretchBltMode(hDC, COLORONCOLOR);

			int nWidth  = getAvailableWidth();
			int nHeight = getAvailableHeight();

			int nX = abs(nWidth - cxDib) / 2;
			int nY = abs(nHeight - cyDib) / 2;

			int GDIError = GDI_ERROR;
			GDIError = StretchDIBits(
				hDC, nX, nY, cxDib, cyDib,
				0, 0, cxDib, cyDib, pBits, pbmi,
				DIB_RGB_COLORS, SRCCOPY);

			OSL_ASSERT(GDI_ERROR != GDIError);

			// paint the border
			RECT rc;

			if (nY > 0)
			{
				// top
				rc.left	  = 0;
				rc.top	  = 0;
				rc.right  = nWidth;
				rc.bottom = nY;
				FillRect(hDC,&rc,(HBRUSH)(COLOR_INACTIVEBORDER + 1));

				// bottom
				rc.left	  = 0;
				rc.top	  = nHeight - nY - 1;
				rc.right  = nWidth;
				rc.bottom = nHeight;
				FillRect(hDC,&rc,(HBRUSH)(COLOR_INACTIVEBORDER + 1));
			}

			if (nX > 0)
			{
				// left
				rc.left	  = 0;
				rc.top	  = nY;
				rc.right  = nX;
				rc.bottom = nHeight - nY;
				FillRect(hDC,&rc,(HBRUSH)(COLOR_INACTIVEBORDER + 1));

				// right
				rc.left	  = nWidth - nX - 1;
				rc.top	  = nY;
				rc.right  = nWidth;
				rc.bottom = nHeight - nY;
				FillRect(hDC,&rc,(HBRUSH)(COLOR_INACTIVEBORDER + 1));
			}
		}
		else // clear background
		{
			RECT rc;
			GetClientRect(hWnd,&rc);
			FillRect(hDC,&rc,(HBRUSH)(COLOR_INACTIVEBORDER + 1));
		}
	}
	catch(...)
	{
		OSL_ASSERT(sal_False);
	}
}

//---------------------------------------------------
//
//---------------------------------------------------

LRESULT CALLBACK CDIBPreview::WndProc(
	HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult = 0;

	switch(uMsg)
	{

	// we connect a pointer to the current instance
	// with a window instance via SetProp
	case WM_CREATE:
        {
            LPCREATESTRUCT lpcs =
                reinterpret_cast< LPCREATESTRUCT >(lParam);

            OSL_ASSERT(lpcs->lpCreateParams);

            // connect the instance handle to the window
            SetProp(hWnd, CURRENT_INSTANCE, lpcs->lpCreateParams);
        }
        break;

    // we remove the window property which connects
    // a class instance with a window class
    case WM_NCDESTROY:
        {
            // RemoveProp returns the saved value on success
            if (reinterpret_cast<CDIBPreview*>(
                    RemoveProp(hWnd, CURRENT_INSTANCE)) == NULL)
            {
                OSL_ASSERT(false);
            }
        }
        break;

	case WM_PAINT:
	{
		CDIBPreview* pImpl = reinterpret_cast<CDIBPreview*>(
            GetProp(hWnd, CURRENT_INSTANCE));

        OSL_ASSERT(pImpl);

		HDC         hDC;
		PAINTSTRUCT ps;

		hDC = BeginPaint(hWnd,&ps);
		pImpl->onPaint(hWnd,hDC);
		EndPaint(hWnd,&ps);
	}
	break;

	// ignore this message in order to
	// avoid flickering during paint
	case WM_ERASEBKGND:
		lResult = 1;
		break;

	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}

	return lResult;
}

//---------------------------------------------------
//
//---------------------------------------------------

ATOM SAL_CALL CDIBPreview::RegisterDibPreviewWindowClass()
{
	osl::MutexGuard aGuard( s_Mutex );

    if (0 == s_ClassAtom)
    {
        // register the preview window class
	    WNDCLASSEX wndClsEx;
	    ZeroMemory(&wndClsEx, sizeof(wndClsEx));

	    wndClsEx.cbSize        = sizeof(wndClsEx);
	    wndClsEx.style		   = CS_HREDRAW | CS_VREDRAW;
	    wndClsEx.lpfnWndProc   = CDIBPreview::WndProc;
	    wndClsEx.hInstance     = m_Instance;
	    wndClsEx.hbrBackground = (HBRUSH)(COLOR_INACTIVEBORDER + 1);
	    wndClsEx.lpszClassName = PREVIEWWND_CLASS_NAME;

	    // register the preview window class
	    // !!! Win95 -   the window class will be unregistered automatically
	    //			     if the dll is unloaded
	    //     Win2000 - the window class must be unregistered manually
	    //				 if the dll is unloaded
	    s_ClassAtom = RegisterClassEx(&wndClsEx);

		OSL_POSTCOND(s_ClassAtom,"Could not register preview window class");

		if (0 == s_ClassAtom)
			throw std::runtime_error("Preview window class could not be registered");
    }

    // increment the register class counter
    // so that we keep track of the number
    // of class registrations
    //if ( 0 != s_ClassAtom )
    s_RegisterDibPreviewWndCount++;

    return s_ClassAtom;
}

//---------------------------------------------------
//
//---------------------------------------------------

void SAL_CALL CDIBPreview::UnregisterDibPreviewWindowClass()
{
	osl::MutexGuard aGuard( s_Mutex );

	OSL_ASSERT( ( (0 != s_ClassAtom) && (s_RegisterDibPreviewWndCount > 0)) ||
				( (0 == s_ClassAtom) && (0 == s_RegisterDibPreviewWndCount) ) );

	// update the register class counter
	// and unregister the window class if
	// counter drops to zero
	if (0 != s_ClassAtom)
	{
		s_RegisterDibPreviewWndCount--;
		OSL_ASSERT(s_RegisterDibPreviewWndCount >= 0);
	}

	if (0 == s_RegisterDibPreviewWndCount)
	{
		UnregisterClass((LPCTSTR)MAKELONG(s_ClassAtom,0),m_Instance);
		s_ClassAtom = 0;
	}
}
