/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (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.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is 
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsRegionWin.h"
#include "prmem.h"

nsRegionWin :: nsRegionWin()
{
  mRegion = NULL;
  mRegionType = NULLREGION;
  mData = NULL;
  mDataSize = 0;
}

nsRegionWin :: ~nsRegionWin()
{
  if (NULL != mRegion)
  {
    ::DeleteObject(mRegion);
    mRegion = NULL;
  }

  if (NULL != mData)
  {
    PR_Free(mData);

    mData = NULL;
    mDataSize = 0;
  }
}

NS_IMPL_ISUPPORTS1(nsRegionWin, nsIRegion)

nsresult nsRegionWin :: Init(void)
{
  if (NULL != mRegion) {
    ::SetRectRgn(mRegion, 0, 0, 0, 0);
    FreeRects(nsnull);
  }
  else {
    mRegion = ::CreateRectRgn(0, 0, 0, 0);
  }
  
  mRegionType = NULLREGION;

  return NS_OK;
}

void nsRegionWin :: SetTo(const nsIRegion &aRegion)
{
  nsRegionWin *pRegion = (nsRegionWin *)&aRegion;

  mRegionType = ::CombineRgn(mRegion, pRegion->mRegion, NULL, RGN_COPY);
}

void nsRegionWin :: SetTo(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
  if (NULL != mRegion)
    ::SetRectRgn(mRegion, aX, aY, aX + aWidth, aY + aHeight);
  else
    mRegion = ::CreateRectRgn(aX, aY, aX + aWidth, aY + aHeight);

  if ((aWidth == 0) || (aHeight == 0))
    mRegionType = NULLREGION;
  else
    mRegionType = SIMPLEREGION;
}

void nsRegionWin :: Intersect(const nsIRegion &aRegion)
{
  nsRegionWin *pRegion = (nsRegionWin *)&aRegion;

  mRegionType = ::CombineRgn(mRegion, mRegion, pRegion->mRegion, RGN_AND);
}

void nsRegionWin :: Intersect(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
  HRGN tRegion;

  tRegion = ::CreateRectRgn(aX, aY, aX + aWidth, aY + aHeight);
  mRegionType = ::CombineRgn(mRegion, mRegion, tRegion, RGN_AND);

  ::DeleteObject(tRegion);
}

void nsRegionWin :: Union(const nsIRegion &aRegion)
{
  nsRegionWin *pRegion = (nsRegionWin *)&aRegion;

  mRegionType = ::CombineRgn(mRegion, mRegion, pRegion->mRegion, RGN_OR);
}

void nsRegionWin :: Union(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
  HRGN tRegion;

  tRegion = ::CreateRectRgn(aX, aY, aX + aWidth, aY + aHeight);
  mRegionType = ::CombineRgn(mRegion, mRegion, tRegion, RGN_OR);

  ::DeleteObject(tRegion);
}

void nsRegionWin :: Subtract(const nsIRegion &aRegion)
{
  nsRegionWin *pRegion = (nsRegionWin *)&aRegion;

  mRegionType = ::CombineRgn(mRegion, mRegion, pRegion->mRegion, RGN_DIFF);
}

void nsRegionWin :: Subtract(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
  HRGN tRegion;

  tRegion = ::CreateRectRgn(aX, aY, aX + aWidth, aY + aHeight);
  mRegionType = ::CombineRgn(mRegion, mRegion, tRegion, RGN_DIFF);

  ::DeleteObject(tRegion);
}

PRBool nsRegionWin :: IsEmpty(void)
{
  return (mRegionType == NULLREGION) ? PR_TRUE : PR_FALSE;
}

PRBool nsRegionWin :: IsEqual(const nsIRegion &aRegion)
{
  nsRegionWin *pRegion = (nsRegionWin *)&aRegion;

  return ::EqualRgn(mRegion, pRegion->mRegion) ? PR_TRUE : PR_FALSE;
}

void nsRegionWin :: GetBoundingBox(PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight)
{
  RECT  bounds;

  if (mRegionType != NULLREGION)
  {
    ::GetRgnBox(mRegion, &bounds);

    *aX = bounds.left;
    *aY = bounds.top;
    *aWidth = bounds.right - bounds.left;
    *aHeight = bounds.bottom - bounds.top;
  }
  else
    *aX = *aY = *aWidth = *aHeight = 0;
}

void nsRegionWin :: Offset(PRInt32 aXOffset, PRInt32 aYOffset)
{
  ::OffsetRgn(mRegion, aXOffset, aYOffset);
}

PRBool nsRegionWin :: ContainsRect(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight)
{
  RECT  trect;

  trect.left = aX;
  trect.top = aY;
  trect.right = aX + aWidth;
  trect.bottom = aY + aHeight;

  return ::RectInRegion(mRegion, &trect) ? PR_TRUE : PR_FALSE;
}

NS_IMETHODIMP nsRegionWin :: GetRects(nsRegionRectSet **aRects)
{
  nsRegionRectSet *rects;
  nsRegionRect    *rect;
	LPRECT          pRects;
	DWORD           dwCount, dwResult;
	unsigned int    num_rects;

  NS_ASSERTION(!(nsnull == aRects), "bad ptr");

  rects = *aRects;

  if (nsnull != rects)
    rects->mNumRects = 0;

  // code lifted from old winfe. MMP

	/* Get the size of the region data */
	dwCount = GetRegionData(mRegion, 0, NULL);

	NS_ASSERTION(!(dwCount == 0), "bad region");

	if (dwCount == 0)
	  return NS_OK;

  if (dwCount > mDataSize)
  {
    if (NULL != mData)
      PR_Free(mData);

	  mData = (LPRGNDATA)PR_Malloc(dwCount);
  }

	NS_ASSERTION(!(nsnull == mData), "failed allocation");

	if (mData == NULL)
	  return NS_OK;

	dwResult = GetRegionData(mRegion, dwCount, mData);

	NS_ASSERTION(!(dwResult == 0), "get data failed");

	if (dwResult == 0)
		return NS_OK;

  if ((nsnull == rects) || (rects->mRectsLen < mData->rdh.nCount))
  {
    void *buf = PR_Realloc(rects, sizeof(nsRegionRectSet) + (sizeof(nsRegionRect) * (mData->rdh.nCount - 1)));

    if (nsnull == buf)
    {
      if (nsnull != rects)
        rects->mNumRects = 0;

      return NS_OK;
    }

    rects = (nsRegionRectSet *)buf;
    rects->mRectsLen = mData->rdh.nCount;
  }

  rects->mNumRects = mData->rdh.nCount;
  rects->mArea = 0;
  rect = &rects->mRects[0];

  for (pRects = (LPRECT)mData->Buffer, num_rects = 0; 
		   num_rects < mData->rdh.nCount; 
		   num_rects++, pRects++, rect++)
  {
		rect->x = pRects->left;
		rect->y = pRects->top;
		rect->width = pRects->right - rect->x;
		rect->height = pRects->bottom - rect->y;

    rects->mArea += rect->width * rect->height;
	}

  *aRects = rects;

  return NS_OK;
}

NS_IMETHODIMP nsRegionWin :: FreeRects(nsRegionRectSet *aRects)
{
  if (nsnull != aRects)
    PR_Free((void *)aRects);

  if (NULL != mData)
  {
    PR_Free(mData);

    mData = NULL;
    mDataSize = 0;
  }

  return NS_OK;
}

NS_IMETHODIMP nsRegionWin :: GetNativeRegion(void *&aRegion) const
{
  aRegion = (void *)mRegion;
  return NS_OK;
}

NS_IMETHODIMP nsRegionWin :: GetRegionComplexity(nsRegionComplexity &aComplexity) const
{
  switch (mRegionType)
  {
    case NULLREGION:
      aComplexity = eRegionComplexity_empty;
      break;

    case SIMPLEREGION:
      aComplexity = eRegionComplexity_rect;
      break;

    default:
    case COMPLEXREGION:
      aComplexity = eRegionComplexity_complex;
      break;
  }

  return NS_OK;
}
