/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * 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 Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */
#ifndef nsLineBox_h___
#define nsLineBox_h___

#include "nsVoidArray.h"
#include "nsPlaceholderFrame.h"
#include "nsILineIterator.h"
#include "nsISizeOfHandler.h"

class nsISpaceManager;
class nsLineBox;
class nsFloaterCache;
class nsFloaterCacheList;
class nsFloaterCacheFreeList;

// State cached after reflowing a floater. This state is used during
// incremental reflow when we avoid reflowing a floater.
class nsFloaterCache {
public:
  nsFloaterCache();
#ifdef DEBUG
  ~nsFloaterCache();
#else
  ~nsFloaterCache() { }
#endif

  nsFloaterCache* Next() const { return mNext; }

  nsPlaceholderFrame* mPlaceholder;     // nsPlaceholderFrame

  // This will be true if the floater was placed on the current line
  // instead of below the current line.
  PRBool mIsCurrentLineFloater;

  nsMargin mMargins;                    // computed margins

  nsMargin mOffsets;                    // computed offsets (relative pos)

  // Region in the spacemanager impacted by this floater; the
  // coordinates are relative to the containing block frame. The
  // region includes the margins around the floater, but doesn't
  // include the relative offsets.
  nsRect mRegion;

  // Combined area for the floater. This will not include the margins
  // for the floater. Like mRegion, the coordinates are relative to
  // the containing block frame.
  nsRect mCombinedArea;

protected:
  nsFloaterCache* mNext;

  friend class nsFloaterCacheList;
  friend class nsFloaterCacheFreeList;
};

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

class nsFloaterCacheList {
public:
  nsFloaterCacheList() : mHead(nsnull) { }
  ~nsFloaterCacheList();

  PRBool IsEmpty() const {
    return nsnull == mHead;
  }

  PRBool NotEmpty() const {
    return nsnull != mHead;
  }

  nsFloaterCache* Head() const {
    return mHead;
  }

  nsFloaterCache* Tail() const;

  nsFloaterCache* Find(nsIFrame* aOutOfFlowFrame);

  void Remove(nsFloaterCache* aElement);

  void Append(nsFloaterCacheFreeList& aList);

#ifdef DEBUG
  void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
#endif

protected:
  nsFloaterCache* mHead;

  friend class nsFloaterCacheFreeList;
};

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

class nsFloaterCacheFreeList : public nsFloaterCacheList {
public:
  nsFloaterCacheFreeList() : mTail(nsnull) { }
  ~nsFloaterCacheFreeList() { }

  // Steal away aList's nsFloaterCache objects and put them on this
  // free-list.
  void Append(nsFloaterCacheList& aList);

  void Append(nsFloaterCache* aFloaterCache);

  // Allocate a new nsFloaterCache object
  nsFloaterCache* Alloc();

protected:
  nsFloaterCache* mTail;

  friend class nsFloaterCacheList;
};

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

#define LINE_MAX_BREAK_TYPE  ((1 << 4) - 1)
#define LINE_MAX_CHILD_COUNT ((1 << 24) - 1)

#if NS_STYLE_CLEAR_LAST_VALUE > 15
need to rearrange the mBits bitfield;
#endif

// Funtion to create a line box
nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
                         PRInt32 aCount, PRBool aIsBlock);

/**
 * The nsLineBox class represents a horizontal line of frames. It contains
 * enough state to support incremental reflow of the frames, event handling
 * for the frames, and rendering of the frames.
 */
class nsLineBox {
private:
  nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock);
  ~nsLineBox();
  
  // Overloaded new operator. Uses an arena (which comes from the presShell)
  // to perform the allocation.
  void* operator new(size_t sz, nsIPresShell* aPresShell);
  void operator delete(void* aPtr, size_t sz);

public:
  // Use these two functions to allocate and destroy line boxes
  friend nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
                                  PRInt32 aCount, PRBool aIsBlock);

  void Destroy(nsIPresShell* aPresShell);

  // mBlock bit
  PRBool IsBlock() const {
    return mFlags.mBlock;
  }
  PRBool IsInline() const {
    return 0 == mFlags.mBlock;
  }

  // mDirty bit
  void MarkDirty() {
    mFlags.mDirty = 1;
  }
  void ClearDirty() {
    mFlags.mDirty = 0;
  }
  PRBool IsDirty() const {
    return mFlags.mDirty;
  }

  // mImpactedByFloater bit
  void SetLineIsImpactedByFloater(PRBool aValue) {
    NS_ASSERTION((PR_FALSE==aValue || PR_TRUE==aValue), "somebody is playing fast and loose with bools and bits!");
    mFlags.mImpactedByFloater = aValue;
  }
  PRBool IsImpactedByFloater() const {
    return mFlags.mImpactedByFloater;
  }

  // mTrimmed bit
  void SetTrimmed(PRBool aOn) {
    NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!");
    mFlags.mTrimmed = aOn;
  }
  PRBool IsTrimmed() const {
    return mFlags.mTrimmed;
  }

  // mHasPercentageChild bit
  void SetHasPercentageChild(PRBool aOn) {
    NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!");
    mFlags.mHasPercentageChild = aOn;
  }
  PRBool HasPercentageChild() const {
    return mFlags.mHasPercentageChild;
  }

  // mLineWrapped bit
  void SetLineWrapped(PRBool aOn) {
    NS_ASSERTION((PR_FALSE==aOn || PR_TRUE==aOn), "somebody is playing fast and loose with bools and bits!");
    mFlags.mLineWrapped = aOn;
  }
  PRBool IsLineWrapped() const {
    return mFlags.mLineWrapped;
  }
  
  // mChildCount value
  PRInt32 GetChildCount() const {
    return (PRInt32) mFlags.mChildCount;
  }
  void SetChildCount(PRInt32 aNewCount) {
    if (NS_WARN_IF_FALSE(aNewCount >= 0, "negative child count")) {
      aNewCount = 0;
    }
    if (aNewCount > LINE_MAX_CHILD_COUNT) {
      aNewCount = LINE_MAX_CHILD_COUNT;
    }
    mFlags.mChildCount = aNewCount;
  }

  // mBreakType value
  PRBool HasBreak() const {
    return NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
  }
  void SetBreakType(PRUint8 aBreakType) {
    NS_WARN_IF_FALSE(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
    mFlags.mBreakType = aBreakType;
  }
  PRUint8 GetBreakType() const {
    return mFlags.mBreakType;
  }

  // mCarriedOutBottomMargin value
  nscoord GetCarriedOutBottomMargin() const;
  void SetCarriedOutBottomMargin(nscoord aValue);

  // mFloaters
  PRBool HasFloaters() const {
    return (IsInline() && mInlineData) && mInlineData->mFloaters.NotEmpty();
  }
  nsFloaterCache* GetFirstFloater();
  void FreeFloaters(nsFloaterCacheFreeList& aFreeList);
  void AppendFloaters(nsFloaterCacheFreeList& aFreeList);
  PRBool RemoveFloater(nsIFrame* aFrame);
  void RemoveFloatersFromSpaceManager(nsISpaceManager* aSpaceManager);

  // Combined area
  void SetCombinedArea(const nsRect& aCombinedArea);
  void GetCombinedArea(nsRect* aResult);
  PRBool CombinedAreaIntersects(const nsRect& aDamageRect) {
    nsRect* ca = (mData ? &mData->mCombinedArea : &mBounds);
    return !((ca->YMost() <= aDamageRect.y) ||
             (ca->y >= aDamageRect.YMost()));
  }

  void SlideBy(nscoord aDY) {
    mBounds.y += aDY;
    if (mData) {
      mData->mCombinedArea.y += aDY;
    }
  }

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

  nscoord GetHeight() const {
    return mBounds.height;
  }

  static void DeleteLineList(nsIPresContext* aPresContext, nsLineBox* aLine);

  static nsLineBox* LastLine(nsLineBox* aLine);

  static nsLineBox* FindLineContaining(nsLineBox* aLine, nsIFrame* aFrame,
                                       PRInt32* aFrameIndexInLine);

#ifdef DEBUG
  void List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const;
#endif

  nsIFrame* LastChild() const;

  PRBool IsLastChild(nsIFrame* aFrame) const;

  char* StateToString(char* aBuf, PRInt32 aBufSize) const;

  PRInt32 IndexOf(nsIFrame* aFrame) const;

  PRBool Contains(nsIFrame* aFrame) const {
    return IndexOf(aFrame) >= 0;
  }

#ifdef DEBUG
  nsIAtom* SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
  static PRInt32 GetCtorCount();
  static PRInt32 ListLength(nsLineBox* aLine);
#endif

  nsIFrame* mFirstChild;
  nsLineBox* mNext;

  nsRect mBounds;
  nscoord mMaxElementWidth;  // width part of max-element-size
  nscoord mMaximumWidth;     // maximum width (needed for incremental reflow of tables)

  struct FlagBits {
    PRUint32 mDirty : 1;
    PRUint32 mBlock : 1;
    PRUint32 mImpactedByFloater : 1;
    PRUint32 mTrimmed : 1;
    PRUint32 mHasPercentageChild : 1;
    PRUint32 mLineWrapped: 1;

    PRUint32 mBreakType : 4;

    PRUint32 mChildCount : 22;
  };

  struct ExtraData {
    ExtraData(const nsRect& aBounds) : mCombinedArea(aBounds) {
    }
    nsRect mCombinedArea;
  };

  struct ExtraBlockData : public ExtraData {
    ExtraBlockData(const nsRect& aBounds) : ExtraData(aBounds) {
      mCarriedOutBottomMargin = 0;
    }
    nscoord mCarriedOutBottomMargin;
  };

  struct ExtraInlineData : public ExtraData {
    ExtraInlineData(const nsRect& aBounds) : ExtraData(aBounds) {
    }
    nsFloaterCacheList mFloaters;
  };

protected:
  union {
    PRUint32 mAllFlags;
    FlagBits mFlags;
  };

  union {
    ExtraData* mData;
    ExtraBlockData* mBlockData;
    ExtraInlineData* mInlineData;
  };

  void Cleanup();
  void MaybeFreeData();
};

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

class nsLineIterator : public nsILineIteratorNavigator {
public:
  nsLineIterator();
  virtual ~nsLineIterator();

  NS_DECL_ISUPPORTS

  NS_IMETHOD GetNumLines(PRInt32* aResult);
  NS_IMETHOD GetDirection(PRBool* aIsRightToLeft);
  NS_IMETHOD GetLine(PRInt32 aLineNumber,
                     nsIFrame** aFirstFrameOnLine,
                     PRInt32* aNumFramesOnLine,
                     nsRect& aLineBounds,
                     PRUint32* aLineFlags);
  NS_IMETHOD FindLineContaining(nsIFrame* aFrame,
                                PRInt32* aLineNumberResult);
  NS_IMETHOD FindLineAt(nscoord aY,
                        PRInt32* aLineNumberResult);
  NS_IMETHOD FindFrameAt(PRInt32 aLineNumber,
                         nscoord aX,
                         nsIFrame** aFrameFound,
                         PRBool* aXIsBeforeFirstFrame,
                         PRBool* aXIsAfterLastFrame);

  NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber);

  nsresult Init(nsLineBox* aLines, PRBool aRightToLeft);

protected:
  PRInt32 NumLines() const {
    return mNumLines;
  }

  nsLineBox* CurrentLine() {
    return mLines[mIndex];
  }

  nsLineBox* PrevLine() {
    if (0 == mIndex) {
      return nsnull;
    }
    return mLines[--mIndex];
  }

  nsLineBox* NextLine() {
    if (mIndex >= mNumLines - 1) {
      return nsnull;
    }
    return mLines[++mIndex];
  }

  nsLineBox* LineAt(PRInt32 aIndex) {
    if ((aIndex < 0) || (aIndex >= mNumLines)) {
      return nsnull;
    }
    return mLines[aIndex];
  }

  nsLineBox** mLines;
  PRInt32 mIndex;
  PRInt32 mNumLines;
  PRBool mRightToLeft;
};

#endif /* nsLineBox_h___ */
