/* -*- 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.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.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.
 */
#ifndef nsInlineFrame_h___
#define nsInlineFrame_h___

#include "nsHTMLContainerFrame.h"
#include "nsAbsoluteContainingBlock.h"
#include "nsLineLayout.h"

class nsAnonymousBlockFrame;

#define NS_INLINE_FRAME_CID \
 { 0xa6cf90e0, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}

#define nsInlineFrameSuper nsHTMLContainerFrame

class nsInlineFrame : public nsInlineFrameSuper
{
public:
  friend nsresult NS_NewInlineFrame(nsIFrame** aNewFrame);

  // nsISupports overrides
  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);

  // nsIFrame overrides
  NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext,
                                 nsIAtom* aListName,
                                 nsIFrame* aChildList);
  NS_IMETHOD AppendFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aFrameList);
  NS_IMETHOD InsertFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aPrevFrame,
                          nsIFrame* aFrameList);
  NS_IMETHOD RemoveFrame(nsIPresContext& aPresContext,
                         nsIPresShell& aPresShell,
                         nsIAtom* aListName,
                         nsIFrame* aOldFrame);
  NS_IMETHOD Destroy(nsIPresContext& aPresContext);
  NS_IMETHOD GetFrameName(nsString& aResult) const;
  NS_IMETHOD GetFrameType(nsIAtom** aType) const;

  // nsIHTMLReflow overrides
  NS_IMETHOD Reflow(nsIPresContext& aPresContext,
                    nsHTMLReflowMetrics& aDesiredSize,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus& aStatus);
  NS_IMETHOD FindTextRuns(nsLineLayout& aLineLayout);
#if XXX_fix_me
  NS_IMETHOD AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace);
  NS_IMETHOD TrimTrailingWhiteSpace(nsIPresContext& aPresContext,
                                    nsIRenderingContext& aRC,
                                    nscoord& aDeltaWidth);
#endif

  static nsIID kInlineFrameCID;

protected:
  // Additional reflow state used during our reflow methods
  struct InlineReflowState {
    nsIFrame* mNextRCFrame;
    nsIFrame* mPrevFrame;
    nsInlineFrame* mNextInFlow;
  };

  // A helper class that knows how to take a list of frames and chop
  // it up into 3 sections.
  struct SectionData {
    SectionData(nsIFrame* aFrameList);

    PRBool SplitFrameList(nsFrameList& aSection1,
                          nsFrameList& aSection2,
                          nsFrameList& aSection3);

    PRBool HasABlock() const {
      return nsnull != firstBlock;
    }

    nsIFrame* firstBlock;
    nsIFrame* prevFirstBlock;
    nsIFrame* lastBlock;
    nsIFrame* firstFrame;
    nsIFrame* lastFrame;
  };

  nsInlineFrame();

  virtual PRIntn GetSkipSides() const;

  PRBool HaveAnonymousBlock() const {
    return mFrames.NotEmpty()
      ? nsLineLayout::TreatFrameAsBlock(mFrames.FirstChild())
      : PR_FALSE;
  }

  static PRBool ParentIsInlineFrame(nsIFrame* aFrame, nsIFrame** aParent) {
    void* tmp;
    nsIFrame* parent;
    aFrame->GetParent(&parent);
    *aParent = parent;
    if (NS_SUCCEEDED(parent->QueryInterface(kInlineFrameCID, &tmp))) {
      return PR_TRUE;
    }
    return PR_FALSE;
  }

  nsAnonymousBlockFrame* FindPrevAnonymousBlock(nsInlineFrame** aBlockParent);

  nsAnonymousBlockFrame* FindAnonymousBlock(nsInlineFrame** aBlockParent);

  nsresult CreateAnonymousBlock(nsIPresContext& aPresContext,
                                nsIFrame* aFrameList,
                                nsIFrame** aResult);

  nsresult AppendFrames(nsIPresContext& aPresContext,
                        nsIPresShell& aPresShell,
                        nsIFrame* aFrameList,
                        PRBool aGenerateReflowCommands);

  nsresult InsertBlockFrames(nsIPresContext& aPresContext,
                             nsIPresShell& aPresShell,
                             nsIFrame* aPrevFrame,
                             nsIFrame* aFrameList);

  nsresult InsertInlineFrames(nsIPresContext& aPresContext,
                              nsIPresShell& aPresShell,
                              nsIFrame* aPrevFrame,
                              nsIFrame* aFrameList);

  nsresult ReflowInlineFrames(nsIPresContext* aPresContext,
                              const nsHTMLReflowState& aReflowState,
                              InlineReflowState& rs,
                              nsHTMLReflowMetrics& aMetrics,
                              nsReflowStatus& aStatus);

  nsresult ReflowInlineFrame(nsIPresContext* aPresContext,
                             const nsHTMLReflowState& aReflowState,
                             InlineReflowState& rs,
                             nsIFrame* aFrame,
                             nsReflowStatus& aStatus);

  virtual nsIFrame* PullInlineFrame(nsIPresContext* aPresContext,
                                    InlineReflowState& rs,
                                    PRBool* aIsComplete);

  virtual void PushFrames(nsIPresContext* aPresContext,
                          nsIFrame* aFromChild,
                          nsIFrame* aPrevSibling);

  virtual void DrainOverflow(nsIPresContext* aPresContext);

  nsIFrame* PullAnyFrame(nsIPresContext* aPresContext, InlineReflowState& rs);

  nsresult ReflowBlockFrame(nsIPresContext* aPresContext,
                            const nsHTMLReflowState& aReflowState,
                            InlineReflowState& rs,
                            nsHTMLReflowMetrics& aMetrics,
                            nsReflowStatus& aStatus);
};

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

// Variation on inline-frame used to manage lines for line layout in
// special situations.
class nsFirstLineFrame : public nsInlineFrame {
public:
  friend nsresult NS_NewFirstLineFrame(nsIFrame** aNewFrame);

  NS_IMETHOD GetFrameName(nsString& aResult) const;
  NS_IMETHOD GetFrameType(nsIAtom** aType) const;
  NS_IMETHOD Reflow(nsIPresContext& aPresContext,
                    nsHTMLReflowMetrics& aDesiredSize,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus& aStatus);

#ifdef BLOCK_DOES_FIRST_LINE
  // AppendFrames/InsertFrames/RemoveFrame are implemented to forward
  // the method call to the parent frame.
#endif
  NS_IMETHOD AppendFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aFrameList);
  NS_IMETHOD InsertFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aPrevFrame,
                          nsIFrame* aFrameList);
  NS_IMETHOD RemoveFrame(nsIPresContext& aPresContext,
                         nsIPresShell& aPresShell,
                         nsIAtom* aListName,
                         nsIFrame* aOldFrame);

#ifdef BLOCK_DOES_FIRST_LINE
  // These methods are used by the parent frame to actually modify the
  // child frames of the line frame. These methods do not generate
  // reflow commands.
  nsresult AppendFrames2(nsIPresContext* aPresContext,
                         nsIFrame*       aFrameList);

  nsresult InsertFrames2(nsIPresContext* aPresContext,
                         nsIFrame*       aPrevFrame,
                         nsIFrame*       aFrameList);

  nsresult RemoveFrame2(nsIPresContext* aPresContext,
                        nsIFrame*       aOldFrame);

#endif
  // Take frames starting at aFrame until the end of the frame-list
  // away from this frame. The caller is presumed to keep them alive.
  void StealFramesFrom(nsIFrame* aFrame);

  // Take all of the frames away from this frame. The caller is
  // presumed to keep them alive.
  void StealAllFrames() {
    mFrames.SetFrames(nsnull);
  }

protected:
  nsFirstLineFrame();

  virtual nsIFrame* PullInlineFrame(nsIPresContext* aPresContext,
                                    InlineReflowState& rs,
                                    PRBool* aIsComplete);

  virtual void DrainOverflow(nsIPresContext* aPresContext);
};

extern nsresult NS_NewFirstLineFrame(nsIFrame** aNewFrame);


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

// Derived class created for relatively positioned inline-level elements
// that acts as a containing block for child absolutely positioned
// elements

class nsPositionedInlineFrame : public nsInlineFrame
{
public:
  NS_IMETHOD Destroy(nsIPresContext& aPresContext);
#ifdef DEBUG
  NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
#endif

  NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext,
                                 nsIAtom*        aListName,
                                 nsIFrame*       aChildList);
  NS_IMETHOD AppendFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aFrameList);
  NS_IMETHOD InsertFrames(nsIPresContext& aPresContext,
                          nsIPresShell& aPresShell,
                          nsIAtom* aListName,
                          nsIFrame* aPrevFrame,
                          nsIFrame* aFrameList);
  NS_IMETHOD RemoveFrame(nsIPresContext& aPresContext,
                         nsIPresShell& aPresShell,
                         nsIAtom* aListName,
                         nsIFrame* aOldFrame);

  NS_IMETHOD GetAdditionalChildListName(PRInt32   aIndex,
                                        nsIAtom** aListName) const;

  NS_IMETHOD FirstChild(nsIAtom* aListName, nsIFrame** aFirstChild) const;

  NS_IMETHOD Reflow(nsIPresContext&          aPresContext,
                    nsHTMLReflowMetrics&     aDesiredSize,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus&          aStatus);
  
  NS_IMETHOD GetFrameType(nsIAtom** aType) const;

protected:
  nsAbsoluteContainingBlock mAbsoluteContainer;
};

#endif /* nsInlineFrame_h___ */
