/*
 * 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.
 */

#include <fstream>


#include "otbMacro.h"

#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbVectorData.h"
#include "otbVectorDataFileReader.h"
#include "otbVectorDataFileWriter.h"

#include "otbVectorDataIntoImageProjectionFilter.h"


/*
* Tests prTvVectorDataIntoImageProjectionFilterTest_2 and
* prTvVectorDataIntoImageProjectionFilterTest_4 failed because of the bug http://bugs.orfeo-toolbox.org/view.php?id=333.
*/

/*int otbVectorDataIntoImageProjectionFilterTest(int itkNotUsed(argc), char * argv[])
{
  typedef float                                           PixelType;
  typedef otb::VectorImage<PixelType, 2>                  VectorImageType;
  typedef otb::ImageFileReader<VectorImageType>           ReaderType;

  // Vector Data
  typedef otb::VectorData<>                               VectorDataType;
  typedef otb::VectorDataFileReader<VectorDataType>       VectorDataReaderType;

  typedef otb::VectorDataIntoImageProjectionFilter
                 <VectorDataType, VectorImageType>        VectorDataIntoImageProjectionFilterType;

  std::string imageInputFilename = argv[1];
  std::string vectorDataInputFilename = argv[2];
  std::string demDirectory = argv[3];
  int expectedFeatures = atoi(argv[4]);

  otb::DEMHandler::Instance()->OpenDEMDirectory(demDirectory);

  // Read the image
  ReaderType::Pointer    reader  = ReaderType::New();
  reader->SetFileName(imageInputFilename);
  reader->UpdateOutputInformation();

  // Read the Vector Data
  VectorDataReaderType::Pointer vdReader = VectorDataReaderType::New();
  vdReader->SetFileName(vectorDataInputFilename);
  vdReader->Update();

  VectorDataIntoImageProjectionFilterType::Pointer vdReProjFilter
    = VectorDataIntoImageProjectionFilterType::New();
  vdReProjFilter->SetInputImage(reader->GetOutput());
  vdReProjFilter->SetInputVectorData(vdReader->GetOutput());
  vdReProjFilter->Update();

  int nbElem = 0;
  typedef itk::PreOrderTreeIterator<VectorDataType::DataTreeType> TreeIteratorType;
  TreeIteratorType itVector(vdReProjFilter->GetOutput()->GetDataTree());
  itVector.GoToBegin();
  while (!itVector.IsAtEnd())
    {
    if (   !itVector.Get()->IsRoot()
        && !itVector.Get()->IsFolder()
        && !itVector.Get()->IsDocument())
      {
      nbElem++;
      }
    ++itVector;
    }

  if (nbElem != expectedFeatures)
    {
    std::cerr << "Kept " << nbElem << " features, but expected " << expectedFeatures << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}*/


int otbVectorDataIntoImageProjectionFilterCompareImplTest(int itkNotUsed(argc), char* argv[])
{
  typedef float PixelType;
  typedef otb::VectorImage<PixelType, 2> VectorImageType;
  typedef otb::Image<PixelType, 2>       ImageType;
  typedef otb::ImageFileReader<VectorImageType> ReaderType;

  // Vector Data
  typedef otb::VectorData<>                         VectorDataType;
  typedef otb::VectorDataFileReader<VectorDataType> VectorDataReaderType;
  typedef otb::VectorDataFileWriter<VectorDataType> VectorDataWriterType;

  //
  typedef otb::VectorDataIntoImageProjectionFilter<VectorDataType, VectorImageType> VectorDataReProjFilter;

  typedef otb::VectorDataProjectionFilter<VectorDataType, VectorDataType> VectorDataProjectionFilterType;
  typedef otb::VectorDataExtractROI<VectorDataType> VectorDataExtractROIType;
  typedef VectorDataExtractROIType::RegionType      RemoteSensingRegionType;

  std::string imageInputFilename        = argv[1];
  std::string vectorDataInputFilename   = argv[2];
  std::string demDirectory              = argv[3];
  std::string vectorDataOutputFilename  = argv[4];
  std::string vectorDataOutputFilename2 = argv[5];

  std::cout << imageInputFilename << "\n"
            << vectorDataInputFilename << "\n"
            << demDirectory << "\n"
            << vectorDataOutputFilename << "\n"
            << vectorDataOutputFilename2 << std::endl;

  if (!demDirectory.empty())
  {
    otb::DEMHandler::Instance()->OpenDEMDirectory(demDirectory);
  }

  // Read the image
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(imageInputFilename);
  reader->UpdateOutputInformation();

  // read the Vector Data
  VectorDataReaderType::Pointer vdReader = VectorDataReaderType::New();
  vdReader->SetFileName(vectorDataInputFilename);
  vdReader->Update();

  VectorDataReProjFilter::Pointer vdReProjFilter = VectorDataReProjFilter::New();
  vdReProjFilter->SetInputImage(reader->GetOutput());
  vdReProjFilter->SetInputVectorData(vdReader->GetOutput());

  std::string stateOutput = "";
  if (atoi(argv[6]) == 1)
  {
    stateOutput = "true";
    vdReProjFilter->SetUseOutputSpacingAndOriginFromImage(true);
  }
  else
  {
    stateOutput = "false";
    vdReProjFilter->SetUseOutputSpacingAndOriginFromImage(false);
  }

  //----------
  // WRITE
  // vdReProjFilter->Update();

  VectorDataWriterType::Pointer vdwriter = VectorDataWriterType::New();
  vdwriter->SetFileName(vectorDataOutputFilename);
  vdwriter->SetInput(vdReProjFilter->GetOutput());
  vdwriter->Update();

  //-------
  // do the same with old code

  VectorDataProjectionFilterType::Pointer vproj;
  VectorDataExtractROIType::Pointer       vdextract;

  // Extract The part of the VectorData that actually overlaps with
  // the image extent
  vdextract = VectorDataExtractROIType::New();
  vdextract->SetInput(vdReader->GetOutput());

  // Find the geographic region of interest

  // Ge the index of the corner of the image
  ImageType::IndexType ul, ur, ll, lr;
  ImageType::PointType pul, pur, pll, plr;
  ul = reader->GetOutput()->GetLargestPossibleRegion().GetIndex();
  ur = ul;
  ll = ul;
  lr = ul;
  ur[0] += reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];
  lr[0] += reader->GetOutput()->GetLargestPossibleRegion().GetSize()[0];
  lr[1] += reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1];
  ll[1] += reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1];

  // Transform to physical point
  reader->GetOutput()->TransformIndexToPhysicalPoint(ul, pul);
  reader->GetOutput()->TransformIndexToPhysicalPoint(ur, pur);
  reader->GetOutput()->TransformIndexToPhysicalPoint(ll, pll);
  reader->GetOutput()->TransformIndexToPhysicalPoint(lr, plr);

  // Build the cartographic region
  RemoteSensingRegionType            rsRegion;
  RemoteSensingRegionType::IndexType rsOrigin;
  RemoteSensingRegionType::SizeType  rsSize;
  rsOrigin[0] = std::min(pul[0], plr[0]);
  rsOrigin[1] = std::min(pul[1], plr[1]);
  rsSize[0]   = std::abs(pul[0] - plr[0]);
  rsSize[1]   = std::abs(pul[1] - plr[1]);

  rsRegion.SetOrigin(rsOrigin);
  rsRegion.SetSize(rsSize);
  rsRegion.SetRegionProjection(reader->GetOutput()->GetProjectionRef());
  rsRegion.SetKeywordList(reader->GetOutput()->GetImageKeywordlist());

  // Set the cartographic region to the extract roi filter
  vdextract->SetRegion(rsRegion);

  // Reproject VectorData in image projection
  vproj = VectorDataProjectionFilterType::New();
  vproj->SetInput(vdextract->GetOutput());
  vproj->SetInputProjectionRef(vdReader->GetOutput()->GetProjectionRef());
  vproj->SetOutputKeywordList(reader->GetOutput()->GetImageKeywordlist());
  vproj->SetOutputProjectionRef(reader->GetOutput()->GetProjectionRef());
  vproj->SetOutputOrigin(reader->GetOutput()->GetOrigin());
  vproj->SetOutputSpacing(reader->GetOutput()->GetSignedSpacing());

  //----------
  // WRITE
  VectorDataWriterType::Pointer vdwriter2 = VectorDataWriterType::New();
  vdwriter2->SetFileName(vectorDataOutputFilename2);
  vdwriter2->SetInput(vproj->GetOutput());
  vdwriter2->Update();


  return EXIT_SUCCESS;
}
