//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt 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.
//=========================================================================

#include "pqCMBUniformGrid.h"

#include "pqApplicationCore.h"
#include "pqDataRepresentation.h"
#include "pqDataRepresentation.h"
#include "pqObjectBuilder.h"
#include "pqOutputPort.h"
#include "pqPipelineRepresentation.h"
#include "pqPipelineSource.h"
#include "pqRenderView.h"
#include "pqSMAdaptor.h"
#include "pqServer.h"

#include "vtkGeometryRepresentation.h"
#include "vtkImageData.h"
#include "vtkPVDataInformation.h"
#include "vtkPVLASOutputBlockInformation.h"
#include "vtkPVSceneGenObjectInformation.h"
#include "vtkSMNewWidgetRepresentationProxy.h"
#include "vtkSMProxyManager.h"
#include "vtkSMRepresentationProxy.h"
#include <QFileInfo>
#include <QVariant>
#include <vtkProcessModule.h>
#include <vtkSMDataSourceProxy.h>
#include <vtkSMDoubleVectorProperty.h>
#include <vtkSMIdTypeVectorProperty.h>
#include <vtkSMIntVectorProperty.h>
#include <vtkSMPropertyHelper.h>
#include <vtkSMProxyProperty.h>
#include <vtkSMRenderViewProxy.h>
#include <vtkSMRepresentationProxy.h>
#include <vtkSMSourceProxy.h>
#include <vtkTransform.h>
#include <vtkXMLDataSetWriter.h>

#include "pqRepresentationHelperFunctions.h"
#include "vtkDataObject.h"

pqCMBUniformGrid::pqCMBUniformGrid()
  : pqCMBSceneObjectBase()
{
  HasInvalidValue = false;
}

pqCMBUniformGrid::pqCMBUniformGrid(pqPipelineSource* source, pqRenderView* view, pqServer* server,
  const char* filename, bool updateRep)
  : pqCMBSceneObjectBase(source)
{
  this->FileName = filename;
  QFileInfo finfo(filename);
  this->prepGridObject(server, view, updateRep, finfo.completeSuffix().toLower() != "tif");
  HasInvalidValue = false;
}

pqCMBUniformGrid::pqCMBUniformGrid(
  const char* filename, pqServer* server, pqRenderView* view, bool updateRep)
{
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();
  QStringList files;
  files << filename;

  builder->blockSignals(true);
  pqPipelineSource* source;
  QFileInfo finfo(filename);
  if (this->isRawDEM(filename))
  {
    source = builder->createReader("sources", "RawDEMReader", files, server);
    vtkSMPropertyHelper(source->getProxy(), "OutputImageData").Set(1);
  }
  else if (finfo.completeSuffix().toLower() == "dem" || finfo.completeSuffix().toLower() == "tif")
  {
    //TEST_DEM_TO_MESH(filename);
    source = builder->createReader("sources", "GDALRasterReader", files, server);
    HasInvalidValue = true;
  }
  else
  {
    source = builder->createReader("sources", "XMLImageDataReader", files, server);
  }
  pqPipelineSource* mesh = builder->createFilter("filters", "cmbStructedToMesh", source);
  vtkSMPropertyHelper(mesh->getProxy(), "UseScalerForZ").Set(0);
  builder->blockSignals(false);
  this->Source = mesh;
  this->ImageSource = source;
  this->setFileName(filename);
  this->prepGridObject(server, view, updateRep, true);
}

void pqCMBUniformGrid::prepGridObject(
  pqServer* server, pqRenderView* view, bool updateRep, bool transferColor)
{
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();
  // this->Representation =
  //   builder->createDataRepresentation(
  //   this->Source->getOutputPort(0), view, "UniformGridRepresentation");
  this->setRepresentation(builder->createDataRepresentation(this->Source->getOutputPort(0), view));
  // If there is an elevation field on the points then use it.
  RepresentationHelperFunctions::CMB_COLOR_REP_BY_ARRAY(
    this->getRepresentation()->getProxy(), "Elevation", vtkDataObject::POINT);

  // use CMB Elevation 2 Color Table
  if (transferColor)
  {
    vtkSMProxy* lut =
      builder->createProxy("lookup_tables", "PVLookupTable", server, "transfer_functions");
    vtkSMTransferFunctionProxy::ApplyPreset(lut, "CMB Elevation Map 2", false);
    lut->UpdateVTKObjects();
    pqSMAdaptor::setProxyProperty(
      this->getRepresentation()->getProxy()->GetProperty("LookupTable"), lut);
    vtkSMPropertyHelper(this->getRepresentation()->getProxy(), "SelectionVisibility").Set(0);
  }
  else
  {
    vtkSMPropertyHelper(this->getRepresentation()->getProxy(), "MapScalars").Set(0);
  }
  this->Source->getProxy()->UpdateVTKObjects();
  if (updateRep)
  {
    this->getRepresentation()->getProxy()->UpdateVTKObjects();
  }
}

pqCMBUniformGrid::~pqCMBUniformGrid()
{
}

void pqCMBUniformGrid::updatePolyDataStats()
{
}

pqCMBUniformGrid::enumObjectType pqCMBUniformGrid::getType() const
{
  return pqCMBSceneObjectBase::UniformGrid;
}

pqCMBSceneObjectBase* pqCMBUniformGrid::duplicate(
  pqServer* server, pqRenderView* view, bool updateRep)
{
  pqCMBUniformGrid* dup = new pqCMBUniformGrid(this->FileName.c_str(), server, view, false);
  if (this->isRawDEM())
  {
    dup->setOnRatio(this->getOnRatio());
    vtkIdType re[2], ce[2];
    this->getExtents(re, ce);
    dup->setExtents(re, ce);
    dup->setReadGroupOfFiles(this->getReadGroupOfFiles());
  }
  if (updateRep)
  {
    dup->getRepresentation()->getProxy()->UpdateVTKObjects();
  }

  return dup;
}

bool pqCMBUniformGrid::isRawDEM(const QString& filename)
{
  QFileInfo finfo(filename);
  if (finfo.completeSuffix().toLower() == "flt" || finfo.completeSuffix().toLower() == "ftw" ||
    finfo.completeSuffix().toLower() == "hdr")
  {
    return true;
  }
  return false;
}

bool pqCMBUniformGrid::isRawDEM() const
{
  return isRawDEM(QString(this->FileName.c_str()));
}

void pqCMBUniformGrid::getDimensions(vtkIdType dims[2])
{
  this->ImageSource->getProxy()->InvokeCommand("GatherDimensions");
  this->ImageSource->getProxy()->UpdatePropertyInformation();
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "Dimensions").Get(dims, 2);
}

void pqCMBUniformGrid::setReadGroupOfFiles(bool mode)
{
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "ReadSetOfFiles").Set(mode);
  this->Source->getProxy()->UpdateVTKObjects();
}

bool pqCMBUniformGrid::getReadGroupOfFiles() const
{
  int mode;
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "ReadSetOfFiles").Get(&mode, 1);
  return (mode != 0);
}

void pqCMBUniformGrid::setOnRatio(int r)
{
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "OnRatio").Set(r);
  this->Source->getProxy()->UpdateVTKObjects();
}

int pqCMBUniformGrid::getOnRatio() const
{
  int r;
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "OnRatio").Get(&r, 1);
  return r;
}

void pqCMBUniformGrid::setExtents(vtkIdType rowExtents[2], vtkIdType columnExtents[2])
{
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "RowReadExtents").Set(rowExtents, 2);
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "ColumnReadExtents").Set(columnExtents, 2);
  this->Source->getProxy()->UpdateVTKObjects();
}

void pqCMBUniformGrid::getExtents(vtkIdType rowExtents[2], vtkIdType columnExtents[2]) const
{
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "RowReadExtents").Get(rowExtents, 2);
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "ColumnReadExtents").Get(columnExtents, 2);
}

void pqCMBUniformGrid::getAreaStats(double* areaStats)
{
  // Calculate the number of cells
  double n = static_cast<double>(this->getNumberOfPoints());
  double a = this->getSurfaceArea();
  if (n != 0.0)
  {
    areaStats[0] = areaStats[1] = areaStats[2] = a / n;
  }
  else
  {
    areaStats[0] = areaStats[1] = areaStats[2] = 0.0;
  }
}

void pqCMBUniformGrid::getGeometryBounds(double* geoBounds) const
{
  this->getBounds(geoBounds);
}

void pqCMBUniformGrid::getPolySideStats(double* polySide)
{
  polySide[0] = polySide[1] = polySide[2] = 4;
}

double pqCMBUniformGrid::getSurfaceArea()
{
  double b[6];
  this->getBounds(b);
  double a = (b[1] - b[0]) * (b[3] - b[2]);
  return a;
}

vtkIdType pqCMBUniformGrid::getNumberOfPoints()
{
  vtkPVDataInformation* imageInfo = this->Source->getOutputPort(0)->getDataInformation();
  return imageInfo->GetNumberOfPoints();
}

vtkIdType pqCMBUniformGrid::getNumberOfPolygons()
{
  vtkPVDataInformation* imageInfo = this->Source->getOutputPort(0)->getDataInformation();
  return imageInfo->GetNumberOfCells();
}

bool pqCMBUniformGrid::hasInvalidValue()
{
  return HasInvalidValue;
}

double pqCMBUniformGrid::invalidValue()
{
  double v;
  vtkSMPropertyHelper(this->ImageSource->getProxy(), "InvalidValue").Get(&v, 1);
  return v;
}
