#ifndef __DW_OOFFLOATSMGR_HH__
#define __DW_OOFFLOATSMGR_HH__

#include "outofflowmgr.hh"

namespace dw {

namespace oof {

/**
 * \brief OutOfFlowMgr implementation dealing with floats.
 *
 * Note: The identifiers and comments of this class still refer to
 * "Textblock" instead of "OOFAwareWidget"; should be cleaned up some
 * day. (OTOH, these widgets are always textblocks.)
 */
class OOFFloatsMgr: public OutOfFlowMgr
{
   friend class WidgetInfo;

private:
   enum Side { LEFT, RIGHT };

   OOFAwareWidget *container;
   int oofmIndex;

   core::Allocation containerAllocation;

   class WidgetInfo: public lout::object::Object
   {
   private:
      OOFFloatsMgr *oofm;
      core::Widget *widget;

   protected:
      OOFFloatsMgr *getOOFFloatsMgr () { return oofm; }

   public:
      WidgetInfo (OOFFloatsMgr *oofm, core::Widget *widget);

      inline core::Widget *getWidget () { return widget; }
   };

   class Float: public WidgetInfo
   {
   public:
      class ComparePosition: public lout::object::Comparator
      {
      public:
         int compare (Object *o1, Object *o2);
      };

      class CompareSideSpanningIndex: public lout::object::Comparator
      {
      public:
         int compare (Object *o1, Object *o2);
      };

      class CompareGBAndExtIndex: public lout::object::Comparator
      {
      private:
         OOFFloatsMgr *oofm;

      public:
         CompareGBAndExtIndex (OOFFloatsMgr *oofm)
         { this->oofm = oofm; }
         int compare(Object *o1, Object *o2);
      };

      OOFAwareWidget *generator;
      int externalIndex;
      int index; // TODO Needed after SRDOP?
      int yReq, yReal; // relative to container
      int sideSpanningIndex;
      core::Requisition size;
      bool dirty;

      Float (OOFFloatsMgr *oofm, core::Widget *widget,
             OOFAwareWidget *generatingBlock, int externalIndex);

      void intoStringBuffer(lout::misc::StringBuffer *sb);

      bool covers (int y, int h);
   };

   /**
    * This list is kept sorted.
    *
    * To prevent accessing methods of the base class in an
    * uncontrolled way, the inheritance is private, not public; this
    * means that all methods must be delegated (see iterator(), size()
    * etc. below.)
    *
    * TODO Update comment: still sorted, but ...
    *
    * More: add() and change() may check order again.
    */
   class SortedFloatsVector: private lout::container::typed::Vector<Float>
   {
   private:
      OOFFloatsMgr *oofm;
      Side side;

   public:
      inline SortedFloatsVector (OOFFloatsMgr *oofm, Side side,
                                 bool ownerOfObjects) :
         lout::container::typed::Vector<Float> (1, ownerOfObjects)
      { this->oofm = oofm; this->side = side; }

      int findFloatIndex (OOFAwareWidget *lastGB, int lastExtIndex);
      int find (int y, int start, int end);
      int findFirst (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex,
                     int *lastReturn);
      int findLastBeforeSideSpanningIndex (int sideSpanningIndex);
      void put (Float *vloat);

      inline lout::container::typed::Iterator<Float> iterator()
      { return lout::container::typed::Vector<Float>::iterator (); }
      inline int size ()
      { return lout::container::typed::Vector<Float>::size (); }
      inline Float *get (int pos)
      { return lout::container::typed::Vector<Float>::get (pos); }
      inline void clear ()
      { lout::container::typed::Vector<Float>::clear (); }
   };

   class TBInfo: public WidgetInfo
   {
   public:
      class ComparePosition: public lout::object::Comparator
      {
      private:
         int oofmIndex;
      
      public:
         inline ComparePosition (int oofmIndex) { this->oofmIndex = oofmIndex; }
         int compare (Object *o1, Object *o2);
      };

      int index; // position within "tbInfos"
      int y;     // used for sorting

      TBInfo *parent;
      int parentExtIndex;

      // These two lists store all floats of a generator, in the order
      // in which they are defined. Used for optimization
      lout::container::typed::Vector<Float> *leftFloats, *rightFloats;

      TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock,
              TBInfo *parent, int parentExtIndex);
      ~TBInfo ();

      inline OOFAwareWidget *getOOFAwareWidget ()
      { return (OOFAwareWidget*)getWidget (); }
   };

   SortedFloatsVector *leftFloats, *rightFloats;

   lout::container::typed::HashTable<lout::object::TypedPointer
                                     <dw::core::Widget>, Float> *floatsByWidget;

   lout::container::typed::Vector<TBInfo> *tbInfos;
   lout::container::typed::HashTable<lout::object::TypedPointer<OOFAwareWidget>,
                                     TBInfo> *tbInfosByOOFAwareWidget;

   int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark;
   bool SizeChanged;

   void moveExternalIndices (lout::container::typed::Vector<Float> *list,
                             int oldStartIndex, int diff);
   Float *findFloatByWidget (core::Widget *widget);
   void updateGenerators (Float *vloat);
   int findTBInfo (int y);

   void sizeAllocateFloats (Side side);
   int getGBWidthForAllocation (Float *vloat);
   int calcFloatX (Float *vloat);

   void drawFloats (SortedFloatsVector *list, core::View *view,
                    core::Rectangle *area, core::DrawingContext *context);
   core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y,
                                        core::GettingWidgetAtPointContext
                                        *context);

   bool collidesV (Float *vloat, Float *other, int *yReal);
   bool collidesH (Float *vloat, Float *other);

   void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame,
                               SortedFloatsVector **listOpp, Side *side);

   void getFloatsSize (core::Requisition *cbReq, Side side, int *width,
                       int *height);
   void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth,
                           int *maxWidth);

   TBInfo *getOOFAwareWidget (OOFAwareWidget *widget);
   TBInfo *getOOFAwareWidgetWhenRegistered (OOFAwareWidget *widget);
   inline bool isOOFAwareWidgetRegistered (OOFAwareWidget *widget)
   { return getOOFAwareWidgetWhenRegistered (widget) != NULL; }

   int getBorder (Side side, int y, int h, OOFAwareWidget *lastGB,
                  int lastExtIndex);
   bool hasFloat (Side side, int y, int h, OOFAwareWidget *lastGB,
                  int lastExtIndex);
   int getFloatHeight (Side side, int y, int h, OOFAwareWidget *lastGB,
                       int lastExtIndex);

   int getClearPosition (OOFAwareWidget *widget, Side side);

   void ensureFloatSize (Float *vloat);

   inline static int createSubRefLeftFloat (int index) { return index << 1; }
   inline static int createSubRefRightFloat (int index)
   { return (index << 1) | 1; }

   inline static bool isSubRefLeftFloat (int ref)
   { return ref != -1 && (ref & 1) == 0; }
   inline static bool isSubRefRightFloat (int ref)
   { return ref != -1 && (ref & 1) == 1; }

   inline static int getFloatIndexFromSubRef (int ref)
   { return ref == -1 ? ref : (ref >> 1); }

public:
   OOFFloatsMgr (OOFAwareWidget *container, int oofmIndex);
   ~OOFFloatsMgr ();

   void sizeAllocateStart (OOFAwareWidget *caller,
                           core::Allocation *allocation);
   void sizeAllocateEnd (OOFAwareWidget *caller);
   void containerSizeChangedForChildren ();
   void draw (core::View *view, core::Rectangle *area,
              core::DrawingContext *context);

   void markSizeChange (int ref);
   void markExtremesChange (int ref);
   core::Widget *getWidgetAtPoint (int x, int y,
                                   core::GettingWidgetAtPointContext *context);

   void addWidgetInFlow (OOFAwareWidget *textblock, OOFAwareWidget *parentBlock,
                         int externalIndex);
   int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generatingBlock,
                     int externalIndex);
   void calcWidgetRefSize (core::Widget *widget,core::Requisition *size);
   void moveExternalIndices (OOFAwareWidget *generatingBlock, int oldStartIndex,
                             int diff);

   void tellPosition1 (core::Widget *widget, int x, int y);
   void tellPosition2 (core::Widget *widget, int x, int y);
   void tellIncompletePosition1 (core::Widget *generator, core::Widget *widget,
                                 int x, int y);
   void tellIncompletePosition2 (core::Widget *generator, core::Widget *widget,
                                 int x, int y);

   void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight);
   bool containerMustAdjustExtraSpace ();
   void getExtremes (core::Extremes *cbExtr,
                     int *oofMinWidth, int *oofMaxWidth);

   int getLeftBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
   int getRightBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);

   bool hasFloatLeft (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
   bool hasFloatRight (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);

   int getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGB,
                           int lastExtIndex);
   int getRightFloatHeight (int y, int h, OOFAwareWidget *lastGB,
                            int lastExtIndex);

   bool affectsLeftBorder (core::Widget *widget);
   bool affectsRightBorder (core::Widget *widget);
   bool mayAffectBordersAtAll ();

   int getClearPosition (OOFAwareWidget *textblock);

   bool dealingWithSizeOfChild (core::Widget *child);
   int getAvailWidthOfChild (core::Widget *child, bool forceValue);
   int getAvailHeightOfChild (core::Widget *child, bool forceValue);

   int getNumWidgets ();
   core::Widget *getWidget (int i);
};

} // namespace oof

} // namespace dw

#endif // __DW_OOFFLOATSMGR_HH__
