/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#include <svpm.h>

#include <string.h>

#include <tools/debug.hxx>
#include <tools/svwin.h>

#include "vcl/svapp.hxx"

#include "os2/saldata.hxx"
#include "os2/salinst.h"
#include "os2/salframe.h"
#include "os2/salobj.h"

// =======================================================================

static sal_Bool ImplIsSysWindowOrChild( HWND hWndParent, HWND hWndChild )
{
	if ( hWndParent == hWndChild )
		return TRUE;

	HWND hTempWnd = WinQueryWindow( hWndChild, QW_PARENT );
	while ( hTempWnd )
	{
		if ( hTempWnd == hWndParent )
			return TRUE;
		hTempWnd = WinQueryWindow( hTempWnd, QW_PARENT );
	}

	return FALSE;
}

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

static Os2SalObject* ImplFindOs2SalObject( HWND hWndChild )
{
	SalData*	pSalData = GetSalData();
	Os2SalObject*	pObject = pSalData->mpFirstObject;
	while ( pObject )
	{
		if ( ImplIsSysWindowOrChild( pObject->mhWndChild, hWndChild ) )
			return pObject;

		pObject = pObject->mpNextObject;
	}

	return NULL;
}

// =======================================================================

sal_Bool EXPENTRY SalSysMsgProc( HAB /* hAB */, QMSG* pMsg, ULONG /* fs */ )
{
	if ( (pMsg->msg == WM_BUTTON1DOWN) ||
		 (pMsg->msg == WM_BUTTON2DOWN) ||
		 (pMsg->msg == WM_BUTTON3DOWN) )
	{
		SalData*	pSalData = GetSalData();
		Os2SalObject*	pObject = ImplFindOs2SalObject( pMsg->hwnd );
		if ( pObject )
			WinPostMsg( pObject->mhWnd, SALOBJ_MSG_TOTOP, 0, 0 );
	}

	// Focus fangen wir hier nicht ab, da wir erstmal davon ausgehen,
	// das unser Os2SalObject-Fenster immer eine WM_FOCUSCHANGE-Message
	// bekommt.

	return FALSE;
}

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

MRESULT EXPENTRY SalSysObjWndProc( HWND hWnd, ULONG nMsg,
								   MPARAM nMP1, MPARAM nMP2 )
{
	Os2SalObject*	pSysObj;
	MRESULT 	nRet = 0;
	int 		bDef = TRUE;

#if OSL_DEBUG_LEVEL>0
	debug_printf( "SalSysObjWndProc hWnd 0x%x nMsg %d\n", hWnd, nMsg);
#endif

	switch( nMsg )
	{
		case WM_ERASEBACKGROUND:
			nRet = (MRESULT)FALSE;
			bDef = FALSE;
			break;
		case WM_PAINT:
			{
			HPS 	hPS;
			RECTL	aRect;
			hPS = WinBeginPaint( hWnd, NULLHANDLE, &aRect );
			WinEndPaint( hPS );
			bDef = FALSE;
			}
			bDef = FALSE;
			break;

		case WM_BUTTON1DOWN:
		case WM_BUTTON2DOWN:
		case WM_BUTTON3DOWN:
		case SALOBJ_MSG_TOTOP:
			if ( ImplSalYieldMutexTryToAcquire() )
			{
				pSysObj = GetSalObjWindowPtr( hWnd );
				pSysObj->mpProc( pSysObj->mpInst, pSysObj,
											  SALOBJ_EVENT_TOTOP, 0 );
				ImplSalYieldMutexRelease();
			}
			else
				WinPostMsg( hWnd, SALOBJ_MSG_TOTOP, 0, 0 );
			break;

		case WM_FOCUSCHANGE:
		case SALOBJ_MSG_POSTFOCUS:
			if ( ImplSalYieldMutexTryToAcquire() )
			{
				pSysObj = GetSalObjWindowPtr( hWnd );
				if ( SHORT1FROMMP( nMP2 ) )
				{
					pSysObj->mhLastFocusWnd = WinQueryFocus( HWND_DESKTOP );
					pSysObj->mpProc( pSysObj->mpInst, pSysObj,
												  SALOBJ_EVENT_GETFOCUS, 0 );
				}
				else
				{
					HWND hWndFocus = HWNDFROMMP( nMP1 );
					if ( !hWndFocus || !ImplIsSysWindowOrChild( hWnd, hWndFocus ) )
					{
						pSysObj->mpProc( pSysObj->mpInst, pSysObj,
													  SALOBJ_EVENT_LOSEFOCUS, 0 );
					}
				}
				ImplSalYieldMutexRelease();
			}
			else
				WinPostMsg( hWnd, SALOBJ_MSG_POSTFOCUS, nMP1, nMP2 );
			break;

		case WM_SIZE:
			{
			pSysObj = GetSalObjWindowPtr( hWnd );
			pSysObj->mnHeight = (short)SHORT2FROMMP( nMP2 );
			WinSetWindowPos( pSysObj->mhWndChild, 0,
							 0, 0,
							 (short)SHORT1FROMMP( nMP2 ), (short)SHORT2FROMMP( nMP2 ),
							 SWP_SIZE | SWP_MOVE );
			bDef = FALSE;
			}
			break;

		case WM_CREATE:
			{
			// Window-Instanz am Windowhandle speichern
			CREATESTRUCT* pStruct = (CREATESTRUCT*)nMP2;
			pSysObj = (Os2SalObject*)pStruct->pPresParams;
			SetSalObjWindowPtr( hWnd, pSysObj );
			bDef = FALSE;
			}
			break;
	}

	if ( bDef )
		nRet = WinDefWindowProc( hWnd, nMsg, nMP1, nMP2 );
	return nRet;
}

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

MRESULT EXPENTRY SalSysObjChildWndProc( HWND hWnd, ULONG nMsg,
										MPARAM nMP1, MPARAM nMP2 )
{
	MRESULT 	nRet = 0;
	int 		bDef = TRUE;

#if OSL_DEBUG_LEVEL>0
	debug_printf( "SalSysObjChildWndProc hWnd 0x%x nMsg %d\n", hWnd, nMsg);
#endif

	switch( nMsg )
	{
		case WM_ERASEBACKGROUND:
			// Wegen PlugIn's loeschen wir erstmal den Hintergrund
/*
			nRet = (MRESULT)FALSE;
			bDef = FALSE;
*/
			break;
		case WM_PAINT:
			{
			HPS 	hPS;
			RECTL	aRect;
			hPS = WinBeginPaint( hWnd, NULLHANDLE, &aRect );
			WinEndPaint( hPS );
			bDef = FALSE;
			}
			break;
	}

	if ( bDef )
		nRet = WinDefWindowProc( hWnd, nMsg, nMP1, nMP2 );
	return nRet;
}

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

MRESULT EXPENTRY SalSysObjClipWndProc( HWND hWnd, ULONG nMsg,
									   MPARAM nMP1, MPARAM nMP2 )
{
	MRESULT 	nRet = 0;
	int 		bDef = TRUE;

#if OSL_DEBUG_LEVEL>0
	debug_printf( "SalSysObjClipWndProc hWnd 0x%x nMsg %d\n", hWnd, nMsg);
#endif

	switch( nMsg )
	{
		case WM_MOUSEMOVE:
		case WM_BUTTON1DOWN:
		case WM_BUTTON2DOWN:
		case WM_BUTTON3DOWN:
		case WM_BUTTON1DBLCLK:
		case WM_BUTTON2DBLCLK:
		case WM_BUTTON3DBLCLK:
		case WM_BUTTON1UP:
		case WM_BUTTON2UP:
		case WM_BUTTON3UP:
			{
			// Alle Events an den Frame weiterreichen, da diese Bereiche
			// dem Frame gehoeren. Dazu muessen die Mouse-Koordinaaten
			// entsprechend umgerechnet werden
			HWND hWndParent = WinQueryWindow( hWnd, QW_PARENT ); // ergibt SysChild-Fenster
			hWndParent = WinQueryWindow( hWndParent, QW_PARENT );
			short nX = (short)SHORT1FROMMP( nMP1 );
			short nY = (short)SHORT2FROMMP( nMP1 );
			POINTL aPos;
			aPos.x = nX;
			aPos.y = nY;
			WinMapWindowPoints( hWnd, hWndParent, &aPos, 1 );
			nMP1 = MPFROM2SHORT( (short)aPos.x, (short)aPos.y );
			bDef = FALSE;
			nRet = WinSendMsg( hWndParent, nMsg, nMP1, nMP2 );
			}
			break;

		case WM_HITTEST:
			// Damit im disablten Zustand die MouseKlicks immer noch
			// an den Frame geschickt werden
			// Dieser Code reicht leider nicht aus, deshalb wir unter
			// OS2 immer das Child-Fenster disablen, im Gegensatz
			// zu Windows, wo immer der Parent disablte wird, da
			// sich das Fenster evtl. anders Darstellen koennte,
			// wenn es disablte wird. Da dieser Fall uns bisher
			// nicht bekannt ist, ignorieren wir das.
			nRet = HT_NORMAL;
			bDef = FALSE;
			break;

		case WM_ERASEBACKGROUND:
			nRet = (MRESULT)FALSE;
			bDef = FALSE;
			break;
		case WM_PAINT:
			{
			HPS 	hPS;
			RECTL	aRect;
			hPS = WinBeginPaint( hWnd, NULLHANDLE, &aRect );
			WinEndPaint( hPS );
			bDef = FALSE;
			}
			break;
	}

	if ( bDef )
		nRet = WinDefWindowProc( hWnd, nMsg, nMP1, nMP2 );
	return nRet;
}

// =======================================================================

void ImplDestroyAllClipWindows( HWND hWndLast )
{
	if ( hWndLast == HWND_TOP )
		return;

	HWND hWndPrev;
	while ( hWndLast )
	{
		hWndPrev = WinQueryWindow( hWndLast, QW_PREV );
		WinDestroyWindow( hWndLast );
		hWndLast = hWndPrev;
	}
}

// =======================================================================

SalObject* ImplSalCreateObject( Os2SalInstance* pInst, Os2SalFrame* pParent )
{
	SalData* pSalData = GetSalData();

	if ( !pSalData->mbObjClassInit )
	{
		if ( WinRegisterClass( pSalData->mhAB, (PSZ)SAL_OBJECT_CLASSNAME,
							   (PFNWP)SalSysObjWndProc, CS_MOVENOTIFY,
							   SAL_OBJECT_WNDEXTRA ) )
		{
			if ( WinRegisterClass( pSalData->mhAB, (PSZ)SAL_OBJECT_CLIPCLASSNAME,
								   (PFNWP)SalSysObjClipWndProc, CS_HITTEST | CS_MOVENOTIFY, 0 ) )
			{
				if ( WinRegisterClass( pSalData->mhAB, (PSZ)SAL_OBJECT_CHILDCLASSNAME,
									   (PFNWP)SalSysObjChildWndProc, CS_HITTEST | CS_MOVENOTIFY, 32 ) )
					pSalData->mbObjClassInit = TRUE;
			}
		}
	}

	if ( pSalData->mbObjClassInit )
	{
		Os2SalObject* pObject = new Os2SalObject;
		HWND hWnd = WinCreateWindow( pParent->mhWndClient, SAL_OBJECT_CLASSNAME, "",
									 0,
									 0, 0, 0, 0,
									 pParent->mhWndClient, HWND_TOP,
									 0, NULL, (void*)pObject );
		HWND hWndChild = WinCreateWindow( hWnd, SAL_OBJECT_CHILDCLASSNAME, "",
										  WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
										  0, 0, 0, 0,
										  hWnd, HWND_TOP,
										  0, NULL, NULL );

		if ( !hWndChild )
		{
			if ( hWnd )
				WinDestroyWindow( hWnd );
			delete pObject;
			return NULL;
		}

		if ( hWnd )
		{
#if OSL_DEBUG_LEVEL>0
        debug_printf("ImplSalCreateObject hWndChild %x\n", hWndChild);
        debug_printf("ImplSalCreateObject hWnd %x\n", hWnd);
#endif
			pObject->mhWnd = hWnd;
			pObject->mhWndChild = hWndChild;
			pObject->maSysData.hWnd = hWndChild;
			return pObject;
		}
	}

	return NULL;
}

// =======================================================================

long ImplSalObjCallbackDummy( void*, SalObject*, USHORT, const void* )
{
	return 0;
}

// =======================================================================

Os2SalObject::Os2SalObject()
{
	SalData* pSalData = GetSalData();

	mhLastClipWnd		= HWND_TOP;

	mhWnd				= 0;
	mhWndChild 		= 0;
	mhLastFocusWnd 	= 0;
	maSysData.nSize	= sizeof( SystemEnvData );
	mnHeight			= 0;
	mpInst 			= NULL;
	mpProc 			= ImplSalObjCallbackDummy;

	// Hook installieren, wenn es das erste Os2SalObject ist
	if ( !pSalData->mpFirstObject )
	{
		WinSetHook( pSalData->mhAB, pSalData->mhMQ,
					HK_INPUT, (PFN)SalSysMsgProc, (HMODULE)0 );
	}

	// Insert object in objectlist
	mpNextObject = pSalData->mpFirstObject;
	pSalData->mpFirstObject = this;
}

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

Os2SalObject::~Os2SalObject()
{
	SalData* pSalData = GetSalData();

	// remove frame from framelist
	if ( this == pSalData->mpFirstObject )
	{
		pSalData->mpFirstObject = mpNextObject;

		// Wenn letztes Os2SalObject, dann Hook wieder entfernen
		if ( !pSalData->mpFirstObject )
		{
			WinReleaseHook( pSalData->mhAB, pSalData->mhMQ,
							HK_INPUT, (PFN)SalSysMsgProc, (HMODULE)0 );
		}
	}
	else
	{
		Os2SalObject* pTempObject = pSalData->mpFirstObject;
		while ( pTempObject->mpNextObject != this )
			pTempObject = pTempObject->mpNextObject;

		pTempObject->mpNextObject = mpNextObject;
	}

	// Cache-Daten zerstoeren
	ImplDestroyAllClipWindows( mhLastClipWnd );

	if ( mhWndChild )
		WinDestroyWindow( mhWndChild );
	if ( mhWnd )
		WinDestroyWindow( mhWnd );
}

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

void Os2SalObject::ResetClipRegion()
{
	ImplDestroyAllClipWindows( mhLastClipWnd );
	mhLastClipWnd = HWND_TOP;
}

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

USHORT Os2SalObject::GetClipRegionType()
{
	return SAL_OBJECT_CLIP_EXCLUDERECTS;
}

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

void Os2SalObject::BeginSetClipRegion( ULONG nRectCount )
{
	mhOldLastClipWnd = mhLastClipWnd;
}

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

void Os2SalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
{
	HWND hClipWnd = WinCreateWindow( mhWnd, SAL_OBJECT_CLIPCLASSNAME, "",
									 WS_VISIBLE,
									 nX, mnHeight-(nY+nHeight), nWidth, nHeight,
									 mhWnd, mhLastClipWnd,
									 0, NULL, NULL );
#if OSL_DEBUG_LEVEL>0
        debug_printf("Os2SalObject::UnionClipRegion hClipWnd %x\n", hClipWnd);
#endif
	mhLastClipWnd = hClipWnd;
}

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

void Os2SalObject::EndSetClipRegion()
{
	ImplDestroyAllClipWindows( mhOldLastClipWnd );
}

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

void Os2SalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight )
{
	ULONG  nStyle = 0;
	sal_Bool   bVisible = WinIsWindowVisible( mhWnd );
	if ( bVisible )
	{
		WinShowWindow( mhWnd, FALSE );
		nStyle |= SWP_SHOW;
	}
	SWP aParentSWP;
	WinQueryWindowPos( WinQueryWindow( mhWnd, QW_PARENT ), &aParentSWP );
	WinSetWindowPos( mhWnd, 0, nX, aParentSWP.cy-(nY+nHeight), nWidth, nHeight,
					 SWP_MOVE | SWP_SIZE | nStyle );
}

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

void Os2SalObject::Show( sal_Bool bVisible )
{
	WinShowWindow( mhWnd, bVisible );
}

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

void Os2SalObject::Enable( sal_Bool bEnable )
{
	// Im Gegensatz zu Windows disablen wir das Childfenster,
	// da ansonsten unser Clippen nicht mehr funktioniert, da
	// wir keine Events mehr bekommen. Dadurch kann sich evtl.
	// das Fenster anders darstellen, was wir eigentlich nicht
	// wollen. Aber da uns bisher kein Fall bekannt ist,
	// ignorieren wir dies. Ansonsten muss ein Fenster dazwischen
	// gezogen werden oder getestet werden, wie wir die
	// Maustransparenz erreichen, wenn mhWnd
	// disablte wird.
	WinEnableWindow( mhWndChild, bEnable );
}

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

void Os2SalObject::GrabFocus()
{
	if ( mhLastFocusWnd &&
		 WinIsWindow( GetSalData()->mhAB, mhLastFocusWnd ) &&
		 ImplIsSysWindowOrChild( mhWndChild, mhLastFocusWnd ) )
		WinSetFocus( HWND_DESKTOP, mhLastFocusWnd );
	else
		WinSetFocus( HWND_DESKTOP, mhWndChild );
}

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

void Os2SalObject::SetBackground()
{
}

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

void Os2SalObject::SetBackground( SalColor nSalColor )
{
}

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

const SystemChildData* Os2SalObject::GetSystemData() const
{
	return &maSysData;
}

// -----------------------------------------------------------------------
#if 0
void Os2SalObject::SetCallback( void* pInst, SALOBJECTPROC pProc )
{
	mpInst = pInst;
	if ( pProc )
		mpProc = pProc;
	else
		mpProc = ImplSalObjCallbackDummy;
}
#endif

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

void Os2SalObject::InterceptChildWindowKeyDown( sal_Bool /*bIntercept*/ )
{
}

