// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-FileCopyrightText: Copyright (c) Sandia Corporation
// SPDX-License-Identifier: BSD-3-Clause
#ifndef pqHierarchicalGridLayout_h
#define pqHierarchicalGridLayout_h

#include <QLayout>
#include <QScopedPointer> // for ivar

#include "pqWidgetsModule.h" // for export macros

/**
 * @class pqHierarchicalGridLayout
 * @brief QLayout specialization that supports a hierarchical layout.
 *
 * pqHierarchicalGridLayout supports a layout where the widgets added to the
 * layout can be treated as laid out in a binary tree with each non-leaf node
 * splitting the viewport horizontally or vertically.
 *
 * If one adds widgets to the layout using QLayout API e.g. `addWidget` then the
 * layout will automatically build the binary tree by adding splits in the
 * layout as needed.
 *
 * Another way to explicitly provide an arrangement of widgets is to use
 * `rearrange` to add/remove widgets from the layout and place them in
 * particular fashion.
 *
 * If pqHierarchicalGridLayout is added to a pqHierarchicalGridWidget then it is
 * possible to allow the user to interactively resize individual widgets.
 *
 * @note Caveat
 *
 * pqHierarchicalGridLayout currently does not support adding non QWidget items
 * to the layout e.g. another QLayout or QSpacerItem etc. Support for these can
 * be added in future when needed.
 *
 */
class PQWIDGETS_EXPORT pqHierarchicalGridLayout : public QLayout
{
  Q_OBJECT
  typedef QLayout Superclass;

public:
  pqHierarchicalGridLayout(QWidget* parent = nullptr);
  ~pqHierarchicalGridLayout() override;

  ///@{
  /**
   * pure virtual methods from QLayout
   */
  void addItem(QLayoutItem* item) override;
  QLayoutItem* itemAt(int index) const override;
  QLayoutItem* takeAt(int index) override;
  int count() const override;
  ///@}

  ///@{
  /**
   * recommended overrides from QLayout
   */
  QSize minimumSize() const override;
  void setGeometry(const QRect& rect) override;
  QSize sizeHint() const override;
  ///@}

  ///@{
  /**
   * Returns true if the location points to a valid reachable location.
   */
  bool isLocationValid(int location) const;
  ///@}

  ///@{
  /**
   * Adds a splitter item. \c location specified must be valid and reachable.
   */
  void split(int location, Qt::Orientation direction, double splitFraction);
  void splitAny(int location, double splitFraction);
  void splitVertical(int location, double splitFraction)
  {
    this->split(location, Qt::Vertical, splitFraction);
  }
  void splitHorizontal(int location, double splitFraction)
  {
    this->split(location, Qt::Horizontal, splitFraction);
  }
  ///@}

  /**
   * Specify the split fraction for a location. \c location must be valid and
   * reachable.
   */
  void setSplitFraction(int location, double splitFraction);
  ///@}

  ///@{
  /**
   * Maximize any cell location. Set to 0 to return to default un-maximized state.
   * Invalid location i.e. negative or referring to a non-existent or
   * unreachable cell will be ignored with a warning message.
   */
  void maximize(int location);
  ///@}

  class Item
  {
  public:
    Item(Qt::Orientation direction, double fraction)
      : Direction(direction)
      , Fraction(fraction)
    {
    }
    Item(QWidget* widget = nullptr)
      : Widget(widget)
    {
    }

  private:
    QWidget* Widget = nullptr;
    Qt::Orientation Direction = Qt::Horizontal;
    double Fraction = -1.0;
    friend class pqHierarchicalGridLayout;
  };

  /**
   * API to rearrange the widgets in this layout. The argument is simply a sequential representation
   * for a binary tree were for a split node at index `i`, the first child node is at index `2*i +
   * 1` and the second child node is at index `2*i + 2`.
   *
   * Any widgets previously added to the layout no longer present in the arrangement are removed
   * from the layout and returned.
   *
   * Any widgets not already present in the layout are added to the layout using `addWidget`.
   *
   */
  QVector<QWidget*> rearrange(const QVector<Item>& layout);

private:
  Q_DISABLE_COPY(pqHierarchicalGridLayout);

  class pqInternals;
  QScopedPointer<pqInternals> Internals;
};

#endif
