/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef otbKmzProductWriter_h
#define otbKmzProductWriter_h


#include "itkObjectFactory.h"

// kmz creation
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "kml/engine/kmz_file.h"
#include "kml/base/file.h"
#pragma GCC diagnostic pop
#else
#include "kml/engine/kmz_file.h"
#include "kml/base/file.h"
#endif

// Image Tiling
#include "otbMultiChannelExtractROI.h"
#include "otbImageFileWriter.h"
#include "otbVectorRescaleIntensityImageFilter.h"
#include "otbGenericRSTransform.h"
#include "otbStreamingShrinkImageFilter.h"
#include "itkCastImageFilter.h"

// Possibility to includes vectordatas necessary includes
#include "otbVectorData.h"
#include "otbVectorDataFileWriter.h"
#include <string>


namespace otb
{

/** \class KmzProductWriter
 * \brief This class generate a Kmz from one or several input
 * images. The inputs must have a non empty metadata information.
 *
 * This class writes kmz files, supported by Google Earth. The input
 * must have metadata (a non empty keywordlist)
 *
 * The size of the tiles can be set via the method
 * SetTileSize(unsigned int), the user can also specify the name of
 * the output Kmz filename via SetPath().
 *
 *
 *
 * \ingroup IO
 *
 *
 * \ingroup OTBKMZWriter
 */
template <class TInputImage>
class ITK_EXPORT KmzProductWriter : public itk::ProcessObject
{
public:
  /** Standard class typedefs. */
  typedef KmzProductWriter              Self;
  typedef itk::ProcessObject            Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Method for creation through the object factory. */
  itkNewMacro(Self);

  /** Run-time type information (and related methods). */
  itkTypeMacro(KmzProductWriter, itk::ProcessObject);

  /** Some convenient typedefs. */
  typedef TInputImage                                InputImageType;
  typedef typename InputImageType::InternalPixelType InternalPixelType;
  typedef typename InputImageType::SizeType          SizeType;
  typedef typename InputImageType::IndexType         IndexType;
  typedef typename InputImageType::RegionType        RegionType;
  typedef unsigned char                              OutputPixelType;
  typedef typename InputImageType::Pointer           InputImagePointer;
  typedef typename InputImageType::PixelType         InputImagePixelType;

  typedef VectorData<double, 2> VectorDataType;
  typedef typename VectorDataType::DataNodeType DataNodeType;
  typedef typename DataNodeType::PolygonType    PolygonType;
  typedef typename PolygonType::VertexType      VertexType;

  typedef VectorDataFileWriter<VectorDataType> VectorDataFileWriterType;


  // Multi channels Extract ROI filter
  typedef MultiChannelExtractROI<InternalPixelType, OutputPixelType> VectorImageExtractROIFilterType;

  // Writer
  typedef ImageFileWriter<VectorImage<OutputPixelType>> VectorWriterType;

  // Resampler
  typedef StreamingShrinkImageFilter<InputImageType, InputImageType> StreamingShrinkImageFilterType;

  // Intensity Rescale
  typedef VectorRescaleIntensityImageFilter<InputImageType, InputImageType> VectorRescaleIntensityImageFilterType;

  // Transformer
  typedef GenericRSTransform<>           TransformType;
  typedef TransformType::InputPointType  InputPointType;
  typedef TransformType::OutputPointType OutputPointType;

  // Cast Image Filter
  typedef itk::CastImageFilter<InputImageType, VectorImage<OutputPixelType>> CastFilterType;

  // std::pair description <-> legend (image)
  typedef std::pair<std::string, InputImagePointer> LegendPairType;
  typedef std::vector<LegendPairType> LegendVectorType;

  /** Dimension of input image. */
  itkStaticConstMacro(InputImageDimension, unsigned int, InputImageType::ImageDimension);

  using Superclass::SetInput;
  virtual void SetInput(const InputImageType* image);
  virtual void SetInput(unsigned int, const TInputImage* image);
  const InputImageType* GetInput(void);
  const InputImageType* GetInput(unsigned int idx);

  itkSetStringMacro(Path);

  /** Update Method */
  void Update() override
  {
    this->Write();
  }

  /* Method to Add Logo */
  itkSetMacro(Logo, InputImagePointer);

  /** Method to store the legend and their description in the
    * legendVector
    */
  void AddLegend(const std::string& description, const InputImagePointer legend)
  {
    LegendPairType legendPair;
    legendPair.first  = description;
    legendPair.second = legend;
    m_LegendVector.push_back(legendPair);
  }

  /** Method to add legend with empty description */
  void AddLegend(const InputImagePointer legend)
  {
    std::string emptyString = "";
    this->AddLegend(emptyString, legend);
  }

  /** Set the size of the tiles to produce */
  void SetTileSize(unsigned int tileSize);

protected:
  KmzProductWriter();
  ~KmzProductWriter() override;
  void PrintSelf(std::ostream& os, itk::Indent indent) const override;

  /**Method for Tiling the input image*/
  virtual void Tiling();

  /** Method for writing */
  virtual void Write();

private:
  KmzProductWriter(const Self&) = delete;
  void operator=(const Self&) = delete;

  /** Mehtod to initialize the variables*/
  virtual void Initialize();

  /**
     * Close the root kml by adding the last flags and close the
     * std::ofstream
     */
  void CloseRootKML();

  /** method to  generate the bounding box kml*/
  void BoundingBoxKmlProcess(double north, double south, double east, double west);

  /** Method to generate the root kml*/
  void RootKmlProcess(double north, double south, double east, double west);

  /** Add Roi to kmz*/
  //   void RegionOfInterestProcess();

  // Add Legends to the root kml
  void AddLegendToRootKml(double north, double south, double east, double west);

  /** KML root generate */
  void GenerateKMLRoot(const std::string& title, double north, double south, double east, double west, bool extended);

  /** KML generate  Filename - PathName - tile number - North - South - East - West */
  void GenerateKML(const std::string& pathname, int depth, int x, int y, double north, double south, double east, double west);

  void GenerateKMLExtended(const std::string& pathname, int depth, int x, int y, OutputPointType lowerLeft, OutputPointType lowerRight,
                           OutputPointType upperRight, OutputPointType upperLeft);

  /** KML with link generate */
  void GenerateKMLWithLink(const std::string& pathname, int depth, int x, int y, int tileStartX, int tileStartY, double north, double south, double east,
                           double west, double centerLong, double centerLat);
  void GenerateKMLExtendedWithLink(const std::string& pathname, int depth, int x, int y, int tileStartX, int tileStartY, OutputPointType lowerLeft,
                                   OutputPointType lowerRight, OutputPointType upperRight, OutputPointType upperLeft, double centerLong, double centerLat);

  /** Method to create the bounding kml of the "iteration" th product */
  void GenerateBoundingKML(double north, double south, double east, double west);


  /**
    * Add a networkLink <NetworkLink> ....  </NetworkLink>
    * to root kml (used to handle several image in the same kmz)
    */
  void AddNetworkLinkToRootKML(double north, double south, double east, double west, const std::string& directory, bool addRegion, unsigned int pos);

  /**
   * Add file to KMZ : return the number of error when writing the
   * file in the kmz
   */
  virtual int AddFileToKMZ(const std::ostringstream& absolutePath, const std::ostringstream& kmz_in_path);

  /**Cut the image file name to built the directory name*/
  std::string GetCuttenFileName(const std::string& description, unsigned int idx);

  /** Method to add logo if any */
  void AddLogo();

  /** Method to add Legend if any */
  void ProcessLegends();

  std::string       m_Path;
  bool              m_UseExtendMode;
  InputImagePointer m_VectorImage;
  InputImagePointer m_ResampleVectorImage;

  // Extract ROI
  typename VectorImageExtractROIFilterType::Pointer m_VectorImageExtractROIFilter;

  // Writer
  typename VectorWriterType::Pointer m_VectorWriter;

  // Resampler
  typename StreamingShrinkImageFilterType::Pointer m_StreamingShrinkImageFilter;

  // Rescale intensity
  typename VectorRescaleIntensityImageFilterType::Pointer m_VectorRescaleIntensityImageFilter;

  // Transformer
  typename TransformType::Pointer m_Transform;

  // VectorData Pointer Type for tileindex shapefile
  typename VectorDataType::Pointer m_VectorDataIndexTile;
  typename DataNodeType::Pointer   m_Polygon;
  typename DataNodeType::Pointer   m_MultiPolygon;
  typename DataNodeType::Pointer   m_Folder;

  // Tile size
  unsigned int m_TileSize;
  int          m_MaxDepth;
  int          m_CurrentDepth;
  unsigned int m_CurIdx;

  // KMZ file

  kmlengine::KmzFilePtr m_KmzFile;

  // KMZ file name
  std::ostringstream m_KmzFileName;

  // the kml root ofstream
  std::ofstream m_RootKmlFile;
  std::ofstream m_TempRootKmlFile;

  // File and path name
  std::string m_FileName;
  std::string m_CurrentImageName;

  // Convenience string
  std::string m_KmzExtension;
  std::string m_KmlExtension;

  // Logo
  InputImagePointer m_Logo;

  // Vector used to store legend and description
  LegendVectorType m_LegendVector;
};

} // end namespace otb

#ifndef OTB_MANUAL_INSTANTIATION
#include "otbKmzProductWriter.hxx"
#endif

#endif
