/*
 * The contents of this file are subject to the Mozilla 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/MPL/
 * 
 * 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 Dainis Jonitis,
 * <Dainis_Jonitis@swh-t.lv>.  Portions created by Dainis Jonitis are
 * Copyright (C) 2001 Dainis Jonitis. All Rights Reserved.
 *
 * Contributor(s):
 */


#ifndef nsRegion_h__
#define nsRegion_h__


// Implementation of region. 
// Region is represented as circular double-linked list of nsRegion::RgnRect structures.
// Rectangles in this list do not overlap and are sorted by (y, x) coordinates.

#include "nsRect.h"


// Special version of nsRect structure for speed optimizations in nsRegion code.
// Most important functions could be made inline and be sure that passed rectangles
// will always be non-empty. 

struct nsRectFast : public nsRect
{
  nsRectFast () {}      // No need to call parent constructor to set default values
  nsRectFast (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) : nsRect (aX, aY, aWidth, aHeight) {}
  nsRectFast (const nsRect& aRect) : nsRect (aRect) {}

#if 1   // Override nsRect methods to make them inline. Do not check for emptiness.
  PRBool Contains (const nsRectFast &aRect) const
  {
    return (PRBool) ((aRect.x >= x) && (aRect.y >= y) &&
                     (aRect.XMost() <= XMost()) && (aRect.YMost() <= YMost()));
  }

  PRBool Intersects (const nsRectFast &aRect) const
  {
    return (PRBool) ((x < aRect.XMost()) && (y < aRect.YMost()) &&
                     (aRect.x < XMost()) && (aRect.y < YMost()));
  }

  PRBool IntersectRect (const nsRectFast &aRect1, const nsRectFast &aRect2)
  {
    const nscoord xmost = PR_MIN (aRect1.XMost(), aRect2.XMost());
    x = PR_MAX (aRect1.x, aRect2.x);
    width = xmost - x;
    if (width <= 0) return PR_FALSE;

    const nscoord ymost = PR_MIN (aRect1.YMost(), aRect2.YMost());
    y = PR_MAX (aRect1.y, aRect2.y);
    height = ymost - y;
    if (height <= 0) return PR_FALSE;

    return PR_TRUE;
  }

  void UnionRect (const nsRectFast &aRect1, const nsRectFast &aRect2)
  {
    const nscoord xmost = PR_MAX (aRect1.XMost(), aRect2.XMost());
    const nscoord ymost = PR_MAX (aRect1.YMost(), aRect2.YMost());
    x = PR_MIN (aRect1.x, aRect2.x);
    y = PR_MIN (aRect1.y, aRect2.y);
    width  = xmost - x;
    height = ymost - y;
  }
#endif
};



class nsRegion
{
  friend class nsRegionRectIterator;
  friend class RgnRectMemoryAllocator;

  struct RgnRect : public nsRectFast
  {
    RgnRect* prev;
    RgnRect* next;

    RgnRect () {}                           // No need to call parent constructor to set default values
    RgnRect (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) : nsRectFast (aX, aY, aWidth, aHeight) {}
    RgnRect (const nsRectFast& aRect) : nsRectFast (aRect) {}

    inline void* operator new (size_t);
    inline void  operator delete (void* aRect, size_t);
    RgnRect& operator = (const RgnRect& aRect)       // Do not overwrite prev/next pointers
    { 
       x = aRect.x;
       y = aRect.y;
       width  = aRect.width;
       height = aRect.height;
       return *this;
    }
  };


public:
  nsRegion ();
 ~nsRegion () { SetToElements (0); }

  nsRegion& Copy (const nsRegion& aRegion);
  nsRegion& Copy (const nsRectFast& aRect);

  nsRegion& And  (const nsRegion& aRgn1,   const nsRegion& aRgn2);
  nsRegion& And  (const nsRegion& aRegion, const nsRectFast& aRect);
  nsRegion& And  (const nsRectFast& aRect, const nsRegion& aRegion)
  {
    return  And  (aRegion, aRect);
  }
  nsRegion& And  (const nsRectFast& aRect1, const nsRectFast& aRect2)
  {
    nsRectFast TmpRect;
    
    // Force generic nsRect::IntersectRect to handle empty rectangles!
    TmpRect.nsRect::IntersectRect (aRect1, aRect2);
    return Copy (TmpRect);
  }
    
  nsRegion& Or   (const nsRegion& aRgn1,   const nsRegion& aRgn2);
  nsRegion& Or   (const nsRegion& aRegion, const nsRectFast& aRect);
  nsRegion& Or   (const nsRectFast& aRect, const nsRegion& aRegion)
  {  
    return  Or   (aRegion, aRect);
  }
  nsRegion& Or   (const nsRectFast& aRect1, const nsRectFast& aRect2)
  {
    nsRegion TmpRegion;
    TmpRegion.Copy (aRect1);
    return Or (TmpRegion, aRect2);
  }

  nsRegion& Xor  (const nsRegion& aRgn1,   const nsRegion& aRgn2);
  nsRegion& Xor  (const nsRegion& aRegion, const nsRectFast& aRect);
  nsRegion& Xor  (const nsRectFast& aRect, const nsRegion& aRegion)
  {
    return  Xor  (aRegion, aRect);
  }
  nsRegion& Xor  (const nsRectFast& aRect1, const nsRectFast& aRect2)
  {
    nsRegion TmpRegion;
    TmpRegion.Copy (aRect1);
    return Xor (TmpRegion, aRect2);
  }

  nsRegion& Sub  (const nsRegion& aRgn1,   const nsRegion& aRgn2);
  nsRegion& Sub  (const nsRegion& aRegion, const nsRectFast& aRect);
  nsRegion& Sub  (const nsRectFast& aRect, const nsRegion& aRegion)
  {
    nsRegion TmpRegion;
    TmpRegion.Copy (aRect);
    return Sub (TmpRegion, aRegion);
  }
  nsRegion& Sub  (const nsRectFast& aRect1, const nsRectFast& aRect2)
  {
    nsRegion TmpRegion;
    TmpRegion.Copy (aRect1);
    return Sub (TmpRegion, aRect2);
  }


  PRBool GetBoundRect (nsRect& aBound) const
  {
    aBound = mBoundRect;
    return !mBoundRect.IsEmpty ();
  }

  void Offset (PRInt32 aXOffset, PRInt32 aYOffset);
  void Empty () 
  { 
    SetToElements (0);
    mBoundRect.SetRect (0, 0, 0, 0);
  }
  PRBool IsEmpty () const { return mRectCount == 0; }
  PRBool IsComplex () const { return mRectCount > 1; }
  PRBool IsEqual (const nsRegion& aRegion) const;
  PRUint32 GetNumRects () const { return mRectCount; }


private:
  PRUint32    mRectCount;
  RgnRect*    mCurRect;
  RgnRect     mRectListHead;
  nsRectFast  mBoundRect;

  void InsertBefore (RgnRect* aNewRect, RgnRect* aRelativeRect)
  {
    aNewRect->prev = aRelativeRect->prev;
    aNewRect->next = aRelativeRect;
    aRelativeRect->prev->next = aNewRect;
    aRelativeRect->prev = aNewRect;
    mCurRect = aNewRect;
    mRectCount++;
  }

  void InsertAfter (RgnRect* aNewRect, RgnRect* aRelativeRect)
  {
    aNewRect->prev = aRelativeRect;
    aNewRect->next = aRelativeRect->next;
    aRelativeRect->next->prev = aNewRect;
    aRelativeRect->next = aNewRect;
    mCurRect = aNewRect;
    mRectCount++;
  }

  void SetToElements (PRUint32 aCount);
  RgnRect* Remove (RgnRect* aRect);
  void InsertInPlace (RgnRect* aRect, PRBool aOptimizeOnFly = PR_FALSE);
  void SaveLinkChain ();
  void RestoreLinkChain ();
  void Optimize ();
  void SubRegion (const nsRegion& aRegion, nsRegion& aResult) const;
  void SubRect (const nsRectFast& aRect, nsRegion& aResult, nsRegion& aCompleted) const;
  void SubRect (const nsRectFast& aRect, nsRegion& aResult) const
  {    SubRect (aRect, aResult, aResult);  }
  void Merge (const nsRegion& aRgn1, const nsRegion& aRgn2);
  void MoveInto (nsRegion& aDestRegion, const RgnRect* aStartRect);
  void MoveInto (nsRegion& aDestRegion)
  {    MoveInto (aDestRegion, mRectListHead.next);  }

  nsRegion (const nsRegion& aRegion);       // Prevent copying of regions
  nsRegion& operator = (const nsRegion& aRegion);
};



// Allow read-only access to region rectangles by iterating the list

class nsRegionRectIterator
{
  const nsRegion*  mRegion;
  const nsRegion::RgnRect* mCurPtr;

public:
  nsRegionRectIterator (const nsRegion& aRegion) 
  { 
    mRegion = &aRegion; 
    mCurPtr = &aRegion.mRectListHead; 
  }

  const nsRectFast* Next () 
  { 
    mCurPtr = mCurPtr->next; 
    return (mCurPtr != &mRegion->mRectListHead) ? mCurPtr : nsnull;
  }

  const nsRectFast* Prev ()
  { 
    mCurPtr = mCurPtr->prev; 
    return (mCurPtr != &mRegion->mRectListHead) ? mCurPtr : nsnull;
  }

  void Reset () 
  { 
    mCurPtr = &mRegion->mRectListHead; 
  }
};


#endif
