/*    
	GdiRegions.c	2.29
    	Copyright 1997 Willows Software, Inc. 

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.


For more information about the Willows Twin Libraries.

	http://www.willows.com	

To send email to the maintainer of the Willows Twin Libraries.

	mailto:twin@willows.com 

 */

#include "windows.h"
#include "windowsx.h"

#include "WinDefs.h"		/* TestWF and such */
#include "Log.h"
#include "GdiDC.h"
#include "GdiRegions.h"
#include "Driver.h"

/* imported routines */
extern InternalPaintWindows(HWND,UINT);

/* internal routines */
static int DoRectAndRegion(HRGN, const LPRECT, int);

BOOL
IsRegion(HRGN hRgn)
{
    REGIONINFO *lpR = GETREGIONINFO(hRgn);
    BOOL b = (lpR != NULL)?TRUE:FALSE;
    if (b)
      RELEASEREGIONINFO(lpR);
    return  b;
}

REGION32
TWIN_InternalGetRegionData(HRGN hRgn)
{
    LPREGIONINFO lpRegion;

    if (!(lpRegion = GETREGIONINFO(hRgn)))
	return (REGION32)0;
    else {
        REGION32 lpr32 = (REGION32)lpRegion->Rgn32;
        RELEASEREGIONINFO(lpRegion);
	return lpr32;
    }
}

HRGN    WINAPI 
CreateRectRgn(int left, int top, int right, int bottom)
{
    RECT	rc;

    SetRect(&rc,left,top,right,bottom);
    return  CreateRectRgnIndirect(&rc);
}

HRGN    WINAPI
CreateRectRgnIndirect(const RECT *lpr)
{
    REGION32 Rgn32;

    LOGSTR((LF_REGIONS,"CreateRectRgnIndirect: %d,%d %d,%d\n",
		lpr->left,lpr->top,lpr->right,lpr->bottom));

    Rgn32 = (REGION32)DRVCALL_REGIONS(PRH_CREATERECTREGION,0,0,lpr);

    return GdiCreateRegion((LPSTR)Rgn32);
}

void    WINAPI 
SetRectRgn(HRGN hRgn, int left, int top, int right, int bottom)
{
    REGION32 Rgn32;
    RECT rc;

    LOGSTR((LF_REGIONS,"SetRectRgn: %d,%d %d,%d\n",
		left,top,right,bottom));

    if (!(Rgn32 = TWIN_InternalGetRegionData(hRgn)))
	return;

    SetRect(&rc,left,top,right,bottom);
    (void)DRVCALL_REGIONS(PRH_SETRECTREGION,Rgn32,0,&rc);
}

int
UnionRectWithRegion(HRGN hRgn, const LPRECT lprc)
{
    return DoRectAndRegion(hRgn,lprc,RGN_OR);
}

int
SubtractRectFromRegion(HRGN hRgn, const LPRECT lprc)
{
    return DoRectAndRegion(hRgn,lprc,RGN_DIFF);
}

int
IntersectRectWithRegion(HRGN hRgn, const LPRECT lprc)
{
    return DoRectAndRegion(hRgn,lprc,RGN_AND);
}

int
XorRectWithRegion(HRGN hRgn, const LPRECT lprc)
{
    return DoRectAndRegion(hRgn,lprc,RGN_XOR);
}

static int
DoRectAndRegion(HRGN hRgn, const LPRECT lprc, int fCombineMode)
{
    REGION32 Rgn32;
    HRGN hRectRgn;
    int ret;

    if (!lprc)
	return ERROR;

    if (fCombineMode == RGN_OR) {
	if (!(Rgn32 = TWIN_InternalGetRegionData(hRgn)))
	    return ERROR;
	else
	    return (int)DRVCALL_REGIONS(PRH_UNIONRECTWITHREGION,
				Rgn32,0,lprc);
    }

    hRectRgn = CreateRectRgnIndirect(lprc);

    ret = CombineRgn(hRgn,hRgn,hRectRgn,fCombineMode);

    DeleteObject(hRectRgn);

    return ret;
}

HRGN    WINAPI
CreatePolygonRgn(const POINT *lpPt, int nPt, int mode)
{
    REGION32 Rgn32;

    LOGSTR((LF_REGIONS,"CreatePolygonRgn: count: %d, mode %x\n",nPt,mode));

    if (!(Rgn32 = (REGION32)DRVCALL_REGIONS(PRH_CREATEPOLYREGION,nPt,mode,lpPt)))
	return (HRGN)0;
    else
	return GdiCreateRegion((LPSTR)Rgn32);
}

HRGN    WINAPI 
CreatePolyPolygonRgn(const POINT *lpPt, const int *nPt, int n, int mode)
{
    REGION32 Rgn32, Rgn32Poly;
    int	  	i,nsides,offset;

    LOGSTR((LF_REGIONS,"CreatePolyPolygonRgn:\n"));

    if (!(Rgn32 = (REGION32)DRVCALL_REGIONS(PRH_CREATEREGION,0,0,0)))
	return (HRGN)0;

    for(i=0,offset=0;i<n;i++) {
	nsides = nPt[i];
	if (!(Rgn32Poly = (REGION32)DRVCALL_REGIONS(PRH_CREATEPOLYREGION,
			nsides,mode,&lpPt[offset]))) {
	    (void)DRVCALL_REGIONS(PRH_DESTROYREGION,Rgn32,0,0);
	    return (HRGN)0;
	}
	offset += nsides;
	(void)DRVCALL_REGIONS(PRH_UNIONREGION,Rgn32Poly,Rgn32,Rgn32);
	(void)DRVCALL_REGIONS(PRH_DESTROYREGION,Rgn32Poly,0,0);
    }

    return GdiCreateRegion((LPSTR)Rgn32);
}

BOOL
GdiDeleteRgn(HRGN hRgn)
{
    REGION32 Rgn32;

    if ( (Rgn32 = TWIN_InternalGetRegionData(hRgn))) {
	(void)DRVCALL_REGIONS(PRH_DESTROYREGION,Rgn32,0,0);
	return TRUE;
    }
    else
	return FALSE;
}

int     WINAPI 
OffsetRgn(HRGN hRgn, int x, int y)
{
    REGION32 Rgn32;
    POINT pt;

    LOGSTR((LF_REGIONS,"OffsetRgn: hRgn: %x, pt: %d,%d\n",hRgn,x,y));

    if ((Rgn32 = TWIN_InternalGetRegionData(hRgn))) {
	pt.x = x; pt.y = y;
	return (int)DRVCALL_REGIONS(PRH_OFFSETREGION,Rgn32,0,&pt);
    }
    else
	return ERROR;
}

BOOL    WINAPI 
PtInRegion(HRGN hRgn, int x, int y)
{
    REGION32 Rgn32;
    POINT pt;

    LOGSTR((LF_REGIONS,"PtInRegion: hRgn: %x, pt: %d,%d\n",hRgn,x,y));

    if ((Rgn32 = TWIN_InternalGetRegionData(hRgn))) {
	pt.x = x; pt.y = y;
	return (BOOL)DRVCALL_REGIONS(PRH_PTINREGION,Rgn32,0,&pt);
    }
    else
	return FALSE;
}

BOOL    WINAPI 
RectInRegion(HRGN hRgn, const RECT FAR* lpr)
{
    REGION32 Rgn32;

    if (!lpr)
	return FALSE;

    LOGSTR((LF_REGIONS,"RectInRegion: hRgn: %x, rect: %d,%d %d,%d\n",
		hRgn,lpr->left,lpr->top,lpr->right,lpr->bottom));

    if ((Rgn32 = TWIN_InternalGetRegionData(hRgn)))
	return (BOOL)DRVCALL_REGIONS(PRH_RECTINREGION,Rgn32,0,lpr);
    else
	return FALSE;
}

BOOL    WINAPI 
EqualRgn(HRGN hRgn1, HRGN hRgn2)
{
    REGION32 Rgn32_1,Rgn32_2;

    LOGSTR((LF_REGIONS,"EqualRgn: hRgn1: %x, hRgn2: %x\n",hRgn1,hRgn2));

    if (!(Rgn32_1 = TWIN_InternalGetRegionData(hRgn1)) ||
        !(Rgn32_2 = TWIN_InternalGetRegionData(hRgn2)))
	return FALSE;

    return (BOOL)DRVCALL_REGIONS(PRH_EQUALREGION,Rgn32_1,Rgn32_1,0);
}

int     WINAPI 
CombineRgn(HRGN hDestRgn, HRGN hSrcRgn1, HRGN hSrcRgn2, int nCombineMode)
{
    REGION32 Rgn32Src1, Rgn32Src2, Rgn32Dest;
	
    LOGSTR((LF_REGIONS,"CombineRgn: src1 %x src2 %x dest %x mode %x\n",
	hSrcRgn1,hSrcRgn2,hDestRgn,nCombineMode));

    /* destination and source1 are always passed */
    if (!(Rgn32Dest = TWIN_InternalGetRegionData(hDestRgn)) ||
	!(Rgn32Src1 = TWIN_InternalGetRegionData(hSrcRgn1)))
	return ERROR;

    switch(nCombineMode) {
	case RGN_AND:
	    if (!(Rgn32Src2 = TWIN_InternalGetRegionData(hSrcRgn2)))
		return ERROR;
	    (void)DRVCALL_REGIONS(PRH_INTERSECTREGION,
		Rgn32Src1,Rgn32Src2,Rgn32Dest);
	    break;
			
	case RGN_COPY:
	    (void)DRVCALL_REGIONS(PRH_COPYREGION,Rgn32Src1,Rgn32Dest,0);
	    break;

	case RGN_DIFF:
	    if (!(Rgn32Src2 = TWIN_InternalGetRegionData(hSrcRgn2)))
		return ERROR;
	    (void)DRVCALL_REGIONS(PRH_DIFFREGION,Rgn32Src1,Rgn32Src2,Rgn32Dest);
	    break;

	case RGN_OR:
	    if (!(Rgn32Src2 = TWIN_InternalGetRegionData(hSrcRgn2)))
		return ERROR;
	    (void)DRVCALL_REGIONS(PRH_UNIONREGION,
		Rgn32Src1,Rgn32Src2,Rgn32Dest);
	    break;

	case RGN_XOR:
	    if (!(Rgn32Src2 = TWIN_InternalGetRegionData(hSrcRgn2)))
		return ERROR;
	    (void)DRVCALL_REGIONS(PRH_XORREGION,Rgn32Src1,Rgn32Src2,Rgn32Dest);
	    break;

	default:
	    return ERROR;
    }

    if ((BOOL)DRVCALL_REGIONS(PRH_ISEMPTYREGION,Rgn32Dest,0,0))
	return NULLREGION;
    else
	return COMPLEXREGION;
}

int WINAPI 
GetRgnBox(HRGN hRgn, RECT * lpr)
{
    REGION32 Rgn32;

    LOGSTR((LF_REGIONS,"GetRgnBox: hRgn: %x\n",hRgn));

    if (!(Rgn32 = TWIN_InternalGetRegionData(hRgn)) ||
	(BOOL)DRVCALL_REGIONS(PRH_ISEMPTYREGION,Rgn32,0,0)) {
	SetRectEmpty(lpr);
        return NULLREGION;
    }

    return (int)DRVCALL_REGIONS(PRH_REGIONBOX,Rgn32,0,lpr);
}

int WINAPI 
ExcludeUpdateRgn(HDC hDC,HWND hWnd)
{
    REGION32 Rgn32Clip, Rgn32Update;
    HDC32 hDC32;

    APISTR((LF_API,"ExcludeUpdateRgn: hDC %x hWnd %x\n", hDC,hWnd));

    ASSERT_HDC(hDC32,hDC,0);

    if (0 == (Rgn32Clip = (REGION32)
		DRVCALL_DC(PDCH_GETCLIP,0L,0L,hDC32->lpDrvData))) {
        RELEASEDCINFO(hDC32);
	return NULLREGION;
    }
    
    RELEASEDCINFO(hDC32);      /*  done with it by here, apparently */

    if ((Rgn32Update = (REGION32)GetWindowLong(hWnd,GWL_UPDATE))) {
	return (int)DRVCALL_REGIONS(PRH_DIFFREGION,
		Rgn32Clip,Rgn32Update,Rgn32Clip);
    }
    else
	if ((BOOL)DRVCALL_REGIONS(PRH_ISEMPTYREGION,Rgn32Clip,0,0))
	    return NULLREGION;
	else
	    return COMPLEXREGION;
}

int WINAPI 
GetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL fErase)
{
    return GetUpdateRgnEx(hWnd,(LPRECT)0,hRgn,fErase);
}

BOOL    WINAPI 
GetUpdateRect(HWND hWnd,LPRECT lpr,BOOL fErase)
{
    WORD	wClassStyle;
    HDC	hDC;

    if (!lpr)
	return FALSE;
    (void) GetUpdateRgnEx(hWnd,lpr,(HRGN)0,fErase);
    if ((wClassStyle = GetClassStyle(hWnd)) & CS_OWNDC) {
	if ((hDC = GetOwnDC(hWnd)))
	    DPtoLP(hDC,(POINT *)lpr,2);
    }
    return !IsRectEmpty(lpr);
}

int
GetUpdateRgnEx(HWND hWnd,LPRECT lpr,HRGN hRgn,BOOL bErase)
{
    REGION32 Rgn32, Rgn32Update;

    if (hRgn) {
	if (!(Rgn32 = TWIN_InternalGetRegionData(hRgn)))
	    return ERROR;
    }
    else
	Rgn32 = (REGION32)0;

    if (!(Rgn32Update = (REGION32) GetWindowLong(hWnd,GWL_UPDATE))) {
	if(lpr)
	    SetRectEmpty(lpr);
	if (Rgn32)
	    (void)DRVCALL_REGIONS(PRH_SETREGIONEMPTY,Rgn32,0,0);
	return NULLREGION;
    }

    if (!lpr && !Rgn32)
	return NULLREGION;

    if (lpr) {
	(void)DRVCALL_REGIONS(PRH_REGIONBOX,Rgn32Update,0,lpr);
	LOGSTR((LF_REGIONS,
		"GetUpdateRgnEx:update rectangle: %d,%d %d,%d\n",
		lpr->left,lpr->top,lpr->right,lpr->bottom));
    }
    else {
	if (Rgn32) {
	    (void)DRVCALL_REGIONS(PRH_COPYREGION,Rgn32Update,Rgn32,0);
	}
    }

    if (bErase)
	InternalPaintWindows(hWnd,RDW_ERASENOW|RDW_NOCHILDREN);

	return COMPLEXREGION;
}

/*****************************************************************************/
/* these are convinience functions to deal with update regions */

int
AddRgnToUpdate(HWND hWnd, HRGN hRgn)
{
    REGION32 Rgn32Update, Rgn32;
    HRGN hRgnClient;
    RECT   rcTemp;
    int rc;

    if ((Rgn32Update = (REGION32)GetWindowLong(hWnd,GWL_UPDATE))) {
	GetClientRect(hWnd, &rcTemp);
	if (hRgn) {
	    if (!(hRgnClient = CreateRectRgnIndirect(&rcTemp)))
		return ERROR;
	    IntersectRgn(hRgnClient,hRgnClient,hRgn);
	    Rgn32 = TWIN_InternalGetRegionData(hRgnClient);
	    rc = (int)DRVCALL_REGIONS(PRH_UNIONREGION,
			Rgn32,Rgn32Update,Rgn32Update);
	    DeleteObject(hRgnClient);
	}
	else
	    rc = (int)DRVCALL_REGIONS(PRH_UNIONRECTWITHREGION,
			Rgn32Update,0,&rcTemp);
	return rc;
    }
    else
#ifdef	LATER
	Should we create a new update region here?
#endif
	return NULLREGION;
}

int
AddRectToUpdate(HWND hWnd, const RECT *lpRect)
{
    RECT rcClient;
    REGION32 Rgn32Update;

    if ((Rgn32Update = (REGION32)GetWindowLong(hWnd,GWL_UPDATE))) {
	GetClientRect(hWnd,&rcClient);
	IntersectRect(&rcClient,&rcClient,lpRect);
	return (int)DRVCALL_REGIONS(PRH_UNIONRECTWITHREGION,
			Rgn32Update,0,&rcClient);
    }
    else
#ifdef	LATER
	Should we create a new update region here?
#endif
	return NULLREGION;
}

int
SubtractRgnFromUpdate(HWND hWnd, HRGN hRgn)
{
    REGION32 Rgn32Src,Rgn32Update;

    if ((Rgn32Update = (REGION32)GetWindowLong(hWnd,GWL_UPDATE))) {
	if ((BOOL)DRVCALL_REGIONS(PRH_ISEMPTYREGION,Rgn32Update,0,0)) {
	    LOGSTR((LF_REGIONS,"SubtractRgnFromUpdate: updateregion empty\n"));
	    return NULLREGION;
    	}
	if (hRgn) {
	    LOGSTR((LF_REGIONS,
		"SubtractRgnFromUpdate: validate subset of update\n"));
	    Rgn32Src = TWIN_InternalGetRegionData(hRgn);
	    if ((int)DRVCALL_REGIONS(PRH_DIFFREGION,
		Rgn32Update,Rgn32Src,Rgn32Update) == NULLREGION) {
		LOGSTR((LF_REGIONS,
		    "SubtractRgnFromUpdate: client rgn is valid\n"));
		return NULLREGION;
	    }
	    else
		return COMPLEXREGION;
	}
	else {
    	    LOGSTR((LF_REGIONS,"SubtractRgnFromUpdate: make update empty\n"));
	    (void)DRVCALL_REGIONS(PRH_SETREGIONEMPTY,Rgn32Update,0,0);
	    return NULLREGION;
	}
    }
    else {
	LOGSTR((LF_REGIONS,"SubtractRgnFromUpdate: no updateregion\n"));
	return NULLREGION;
    }
}

int
SubtractRectFromUpdate(HWND hWnd, const RECT *lpRect)
{
    HRGN hRgn;
    int rc;

    if (!lpRect)
	return SubtractRgnFromUpdate(hWnd,(HRGN)0);
    if (!(hRgn = CreateRectRgnIndirect(lpRect)))
	return ERROR;
    rc = SubtractRgnFromUpdate(hWnd,hRgn);
    DeleteObject(hRgn);
    return rc;
}

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

HRGN
GdiCreateRegion(LPSTR lpRgnData)
{
    HRGN hRegion;
    LPREGIONINFO lpRegInfo;

    lpRegInfo = CREATEHRGN(hRegion);

    if (!lpRegInfo) {
	ERRSTR((LF_ERR,"****ERROR**** CREATEHRGN failed\n"));
	return (HRGN)0;
    }
    LOGSTR((LF_LOG,"GdiCreateRegion: created %x for %x\n",
			hRegion,lpRgnData));
    lpRegInfo->Rgn32 = (REGION32)lpRgnData;
    RELEASEREGIONINFO(lpRegInfo);
    return hRegion;
}

/* (WIN32) GDI Region ****************************************************** */

HRGN	WINAPI
ExtCreateRegion(CONST XFORM *lpXform,
	DWORD dwRgnDataSize,
	CONST RGNDATA *lpRgnData)
{
	DWORD dwRectCount;
	CONST RECT *lpRect;
	HRGN hRgn, hRectRgn;
	int nRgnMode;

	APISTR((LF_API, "ExtCreateRegion: dwRgnDataSize %d\n", dwRgnDataSize));

	if ((dwRgnDataSize < sizeof(RGNDATA))
	 || (lpRgnData == NULL)
	 || (lpRgnData->rdh.dwSize < sizeof(RGNDATAHEADER))
	 || (lpRgnData->rdh.iType != RDH_RECTANGLES)
	 || (lpRgnData->rdh.nCount <= 0)
	 || ((lpRgnData->rdh.nRgnSize != 0)
	  && (lpRgnData->rdh.nRgnSize < lpRgnData->rdh.nCount * sizeof(RECT))))
	{
		SetLastErrorEx(1, 0);
		return (HRGN)0;
	}

	/* For dwRectCount > 0,
	 *	lpRect = region rectangle in RGNDATA->Buffer
	 *	nRgnMode = RGN_OR
	 *
	 * For dwRectCount == 0,
	 *	lpRect = bounding rectangle in RGNDATAHEADER
	 *	nRgnMode = RGN_AND
	 *
	 * This allows us to implement bounding rectangle without retyping
	 * entire code within for loop twice.
	 */
	hRgn = (HRGN)0;
	nRgnMode = RGN_OR;
	for (dwRectCount = lpRgnData->rdh.nCount,
	     lpRect = (CONST RECT *) (lpRgnData->Buffer);
	     ;
	     dwRectCount--, lpRect++)
	{
		if (dwRectCount == 0)
		{
			lpRect = &(lpRgnData->rdh.rcBound);
			nRgnMode = RGN_AND;
		}
		if (lpXform == NULL)
		{
			hRectRgn = CreateRectRgnIndirect(lpRect);
		}
		else
		{
			/* We cannot call CreateRectRgn*() because after
			 * appling a shear or rotate transform, the resulting
			 * shape may not have edges parallel to the xy axis
			 * (and hence we won't be able to fully specify the
			 * shape simply by giving a upper-left and lower-right
			 * coordinate in a RECT structure).
			 */
			int i, x, y;
			POINT pt[4];
			pt[0].x = lpRect->left;  pt[0].y = lpRect->top;
			pt[1].x = lpRect->right; pt[1].y = lpRect->top;
			pt[2].x = lpRect->right; pt[2].y = lpRect->bottom;
			pt[3].x = lpRect->left;  pt[3].y = lpRect->bottom;
			for (i = 0; i < sizeof(pt)/sizeof(pt[0]); i++)
			{
				x = pt[i].x * lpXform->eM11
					+ pt[i].y * lpXform->eM21
					+ lpXform->eDx;
				y = pt[i].x * lpXform->eM12
					+ pt[i].y * lpXform->eM22
					+ lpXform->eDy;
				pt[i].x = x;
				pt[i].y = y;
			}
			hRectRgn = CreatePolygonRgn(pt,
				sizeof(pt)/sizeof(pt[0]), WINDING);
		}
		if (!hRectRgn)
		{
			if (hRgn)
				DeleteObject(hRgn);
			SetLastErrorEx(1, 0);
			return (HRGN)0;
		}
		if (!hRgn)
			hRgn = hRectRgn;
		else
		{
			if (CombineRgn(hRgn, hRgn, hRectRgn, nRgnMode)
				== ERROR)
			{
				DeleteObject(hRgn);
				DeleteObject(hRectRgn);
				SetLastErrorEx(1, 0);
				return (HRGN)0;
			}
			DeleteObject(hRectRgn);
		}
		if (dwRectCount == 0)
			break;
	}

	return (hRgn);

}

DWORD	WINAPI
GetRegionData(HRGN hRgn,
	DWORD dwCount,
	LPRGNDATA lpRgnData)
{
	REGION32 Rgn32;

	APISTR((LF_API, "GetRegionData: hRgn %x dwCount %d\n", hRgn, dwCount));

	Rgn32 = TWIN_InternalGetRegionData(hRgn);
	if (!Rgn32)
	{
		SetLastErrorEx(1, 0);
		return (0);
	}

	return ((DWORD) DRVCALL_REGIONS(PRH_GETREGIONDATA, Rgn32, dwCount,
		lpRgnData));

}

BOOL	WINAPI
SelectClipPath(HDC hDC,
	int nClipMode)
{
	HRGN hClipRgn;
	int nClipResult;

	APISTR((LF_API, "SelectClipPath: hDC %x, nClipMode %d\n",
		hDC, nClipMode));

	hClipRgn = PathToRegion(hDC);
	if (!hClipRgn)
		return (FALSE);

	nClipResult = ExtSelectClipRgn(hDC, hClipRgn, nClipMode);

	if (DeleteObject(hClipRgn) == FALSE)
		return (FALSE);

	return (nClipResult == ERROR) ? FALSE : TRUE;

}

