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

  Program:   Visualization Toolkit
  Module:    vtkTexturePackingFilter.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm 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.

=========================================================================*/
/**
 * @class   vtkTexturePackingFilter
 * @brief   Pack multiple polydata as descibed in Section 3 of LSCM paper.
 *
 * LSCM paper:
 * Lévy, Bruno, Sylvain Petitjean, Nicolas Ray, and Jérome Maillot. "Least squares conformal
 * maps for automatic texture atlas generation." In ACM transactions on graphics (TOG), vol. 21,
 * no. 3, pp. 362-371. ACM, 2002.
 */

#ifndef vtk_aeva_ext_vtkTexturePackingFilter_h
#define vtk_aeva_ext_vtkTexturePackingFilter_h

#include "vtk/aeva/ext/AEVAExtModule.h"
#include "vtkPolyDataAlgorithm.h"
#include "vtkSetGet.h"
#include "vtkeigen/eigen/Geometry"

using namespace Eigen;
using namespace std;

class AEVAEXT_EXPORT vtkTexturePackingFilter : public vtkPolyDataAlgorithm
{
public:
  vtkTypeMacro(vtkTexturePackingFilter, vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  vtkTexturePackingFilter(const vtkTexturePackingFilter&) = delete;
  vtkTexturePackingFilter& operator=(const vtkTexturePackingFilter&) = delete;

  /**
   * Construct object with
   */
  static vtkTexturePackingFilter* New();

  /// Used to index output datasets
  enum InputPorts
  {
    ATLAS = 0,   //!< The input texture atlas.
    BOUNDARY = 1 //!< Shared input chart boundaries.
  };

  /// Used to index output datasets
  enum OutputPorts
  {
    UNIATLAS = 0, //!< The output texture atlas.
    HORIZON = 1   //!< Global horizon after packing.
  };

  //@{
  /**
   * Specify the number of texels to pad charts in all directions in the parameterization
   * space so that texel colors from one chart do not bleed into neighboring charts.
   */
  vtkSetMacro(BoundaryTexel, int);
  vtkGetMacro(BoundaryTexel, int);
  //@}

  //@{
  /**
   * Specify the texel size while discretizing PolyData into texels.
   */
  vtkSetMacro(TexelSize, double);
  vtkGetMacro(TexelSize, double);
  //@}

  //@{
  /**
   * Specify the width of the packed texture map in number of texels.
   */
  vtkSetMacro(TextureMapWidth, int);
  vtkGetMacro(TextureMapWidth, int);
  //@}

  //@{
  /**
   * Specify step size for searching optimal packing position per chart.
   */
  vtkSetMacro(StepSize, int);
  vtkGetMacro(StepSize, int);
  //@}

private:
  //@{
  /**
   * Get the two points that are fartest from each other within a given polydata.
   */
  static void GetFarthestPoints(const vtkSmartPointer<vtkPolyData>& curPD1,
    pair<vtkIdType, vtkIdType>& pointIds,
    Vector3d& distanceVector);
  //@}

  /*
   * texelize charts using parameterized surface boundary
   */
  class TexelizedChart
  {
  public:
    void initialize(const vtkSmartPointer<vtkPolyData>& curPD1, double texelSize, int boundaryTexel)
    {
      CurPD1 = curPD1;
      TexelSize = texelSize;
      BoundaryTexel = boundaryTexel;
    }

    vtkSmartPointer<vtkPolyData> CurPD1;
    double TexelSize;
    VectorXi TopHorizon;
    VectorXi BottomHorizon;
    int LeftBound = 0;
    int RightBound = 0;
    int NumberOfTexel = 1;
    int BoundaryTexel = 1;

    int Compute();
  };

protected:
  vtkTexturePackingFilter();
  ~vtkTexturePackingFilter() override = default;

  int RequestData(vtkInformation* request,
    vtkInformationVector** inputVector,
    vtkInformationVector* outputVector) override;
  int FillInputPortInformation(int port, vtkInformation* info) override;

private:
  int BoundaryTexel = 1;
  int StepSize = 1;
  double TexelSize = 1;
  int TextureMapWidth = 580;
  //int TextureMapHeight = 100;
};
#endif
