/*=========================================================================

  Program:   ParaView
  Module:    $RCSfile$

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
// .NAME vtkSMViewLayoutProxy - vtkSMViewLayoutProxy is used by ParaView to layout
// multiple views in a 2D KD-Tree layout.
//
// .SECTION Description
// vtkSMViewLayoutProxy is used by ParaView to layout multiple views in a 2D
// KD-Tree layout. This is proxy, hence can be registered with the proxy manager
// just like other regular proxies; participates in xml state saving/restoring,
// undo-redo, etc. Users can affects the GUI layout using this proxy instance
// from Python as well.
//
// Every time the vtkSMViewLayoutProxy changes so that it would affect the UI,
// this class fires vtkCommand::ConfigureEvent.
//
// View proxies that are to laid out in an layout should be "assigned" to a
// particular cell in a vtkSMViewLayoutProxy instance. vtkSMViewLayoutProxy
// takes over the responsibility of updating the view's \c Position property
// correctly. 
//
// Although, currently, there are no safe guards against assigning a view to
// more than one layout, this is strictly prohibited and can cause unexpected
// problems at run-time.

#ifndef vtkSMViewLayoutProxy_h
#define vtkSMViewLayoutProxy_h

#include "vtkPVServerManagerRenderingModule.h" //needed for exports
#include "vtkSMProxy.h"

class vtkSMViewProxy;
class vtkImageData;

class VTKPVSERVERMANAGERRENDERING_EXPORT vtkSMViewLayoutProxy : public vtkSMProxy
{
public:
  static vtkSMViewLayoutProxy* New();
  vtkTypeMacro(vtkSMViewLayoutProxy, vtkSMProxy);
  void PrintSelf(ostream& os, vtkIndent indent);

  enum Direction
    {
    NONE,
    VERTICAL,
    HORIZONTAL
    };

  // Description:
  // Split a frame at the given \c location. Location must point to an existing cell
  // that's not split. If the location does not exist or is already split, then
  // returns -1, to indicate failure. Otherwise returns the index of the left (or
  // top) child node. The index for the sibling can be computed as (ret_val +
  // 1). \c fraction indicates a value in the range [0.0, 1.0] at which the cell
  // is split. If a View is set at the given location, it will be moved to the
  // left (or top) child after the split.
  int Split(int location, int direction, double fraction);
  int SplitVertical(int location, double fraction)
    { return this->Split(location, VERTICAL, fraction); }
  int SplitHorizontal(int location, double fraction)
    { return this->Split(location, HORIZONTAL, fraction); }

  // Description:
  // Assign a view at a particular location. Note that the view's position may
  // be changed by Split() calls. Returns true on success.
  bool AssignView(int location, vtkSMViewProxy* view);

  // Description:
  // Similar to AssignView() except that is location specified is not available,
  // then this method treats the location merely as a hint and tries to find a
  // suitable place. First, if any empty cell is available, then that is used.
  // Second, if no empty cell is available and \c location is a valid cell, then
  // we either split the cell or traverse down the sub-tree from the cell and
  // split a cell to make room for the view. Thus, this method will always
  // assign the view to a frame. Returns the assigned location.
  int AssignViewToAnyCell(vtkSMViewProxy* view, int location_hint);

  // Description:
  // Removes a view. Returns the location of the cell emptied by the view, if
  // any, otherwise -1.
  int RemoveView(vtkSMViewProxy* view);
  bool RemoveView(int index);

  // Description:
  // Collapses a cell. Only leaf cells without any assigned views can be collapsed.
  // If the cell has a sibling, then that sibling is assigned to the parent
  // node and the sibling cell is destroyed as well. Returns true on success,
  // else false.
  bool Collapse(int location);

  // Description:
  // Swaps the cells at the two locations. Both locations must be leaf locations
  // i.e. cannot be split-cells.
  bool SwapCells(int location1, int location2);

  // Description:
  // Update the split fraction for a split cell. If IsSplitCell(location)
  // returns false, this method does not update the fraction.
  bool SetSplitFraction(int location, double fraction);

  // Description:
  // One can maximize a particular (non-split) cell. Note the maximized state is
  // restored as soon as the layout is changed or when RestoreMaximizedState()
  // is called. Returns false if the cell at the location cannot be maximized
  // since it's a split cell or invalid cell, true otherwise.
  bool MaximizeCell(int location);

  // Description:
  // Restores the maximized state.
  void RestoreMaximizedState();

  // Description:
  // Returns the maximized cell, if any. Returns -1 if no cell is currently
  // maximized.
  vtkGetMacro(MaximizedCell, int);

  // Description:
  // Returns true if the cell identified by the location is a split cell.
  bool IsSplitCell(int location);

  // Description:
  // Returns the split direction for a split cell at the given location.
  Direction GetSplitDirection(int location);

  // Description:
  // Returns the split-fraction for a split cell at the given location.
  double GetSplitFraction(int location);

  // Description:
  // Returns the index for the first child of the given location. This does not
  // do any validity checks for the location, nor that of the child.
  static int GetFirstChild(int location)
    { return 2*location + 1; }

  // Description:
  // Returns the index for the second child of the given location. This does not
  // do any validity checks for the location, nor that of the child.
  static int GetSecondChild(int location)
    { return 2*location + 2; }

  // Description:
  // Returns the parent index.
  static int GetParent(int location)
    { return (location > 0?  ( (location - 1) / 2 ) : -1); }

  // Description:
  // Returns the view, if any, assigned to the given cell location.
  vtkSMViewProxy* GetView(int location);

  // Description:
  // Returns the location for the view, of any. Returns -1 if the view is not
  // found.
  int GetViewLocation(vtkSMViewProxy*);

  // Description:
  // Updates positions for all views using the layout and current sizes.
  // This method is called automatically when the layout changes or the
  // "ViewSize" property on the assigned views changes.
  void UpdateViewPositions();

  // Description:
  // When in tile-display configuration, only 1 view-layout is shown on the
  // tile-display (for obvious reasons). To show any particular layout on the
  // tile display, simply call this method.
  void ShowViewsOnTileDisplay();

  // Description:
  // Captures an image from the layout (including all the views in the layout.
  vtkImageData* CaptureWindow(int magnification);

  // Description:
  // Overridden to save custom XML state.
  virtual vtkPVXMLElement* SaveXMLState(vtkPVXMLElement* root)
    { return this->Superclass::SaveXMLState(root); }
  virtual vtkPVXMLElement* SaveXMLState(
    vtkPVXMLElement* root, vtkSMPropertyIterator* iter);

  // Description:
  // Overridden to load custom XML state.
  virtual int LoadXMLState(vtkPVXMLElement* element, vtkSMProxyLocator* locator);

  // Description:
  // Resets the layout.
  void Reset();

  // Description:
  // Helper method to locate a layout, if any that contains the specified view
  // proxy.
  static vtkSMViewLayoutProxy* FindLayout(vtkSMViewProxy*, const char* reggroup="layouts");

  // Description:
  // Set border size/color to use when capturing multiview images.
  static void SetMultiViewImageBorderColor(double r, double g, double b);
  static void SetMultiViewImageBorderWidth(int width);
  static const double* GetMultiViewImageBorderColor();
  static void GetMultiViewImageBorderColor(unsigned char rgb[3]);
  static void GetMultiViewImageBorderColor(double rgb[3]);
  static int GetMultiViewImageBorderWidth();

protected:
  vtkSMViewLayoutProxy();
  ~vtkSMViewLayoutProxy();

  // Description:
  // Called to load state from protobuf message.
  virtual void LoadState(
    const vtkSMMessage* message, vtkSMProxyLocator* locator);

  // Description:
  // Although this class is a proxy, it's not really a proxy in the traditional
  // sense. So instead of using UpdateVTKObjects() to push state changes to the
  // server (or undo-redo stack), this new method is provided.
  virtual void UpdateState();

  // Description:
  // Starting with the cell-index, tries to find an empty cell in the sub-tree.
  // Returns -1 if none found. May return \c root, if root is indeed an empty
  // cell. Note this assumes that root is valid.
  int GetEmptyCell(int root);

  // Description:
  // Starting with the root, finds a splittable cell. Assumes \c root is valid.
  int GetSplittableCell(int root, Direction& suggested_direction);

  int MaximizedCell;
private:
  vtkSMViewLayoutProxy(const vtkSMViewLayoutProxy&) VTK_DELETE_FUNCTION;
  void operator=(const vtkSMViewLayoutProxy&) VTK_DELETE_FUNCTION;

  class vtkInternals;
  vtkInternals* Internals;

  bool SetBlockUpdate(bool new_value)
    {
    bool temp = this->BlockUpdate;
    this->BlockUpdate = new_value;
    return temp;
    }

  bool BlockUpdate;

  static double MultiViewImageBorderColor[3];
  static int MultiViewImageBorderWidth;

};

#endif
