//=========================================================================
//  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 "vtkCMBPolyDataProvider.h"

#include "vtkDiscreteModel.h"
#include "vtkDiscreteModelWrapper.h"
#include "vtkDiscreteModelEdge.h"
#include "vtkDiscreteModelVertex.h"
#include "vtkCellArray.h"
#include "vtkCommand.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkTrivialProducer.h"

vtkStandardNewMacro(vtkCMBPolyDataProvider);
vtkCxxSetObjectMacro(vtkCMBPolyDataProvider, ModelWrapper, vtkDiscreteModelWrapper);

//----------------------------------------------------------------------------
vtkCMBPolyDataProvider::vtkCMBPolyDataProvider()
{
  // by default assume filters have one input and one output
  // subclasses that deviate should modify this setting
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
  this->ModelWrapper = 0;
  this->ItemType = -1;
  this->ItemTypeIsSet = 0;
  this->EntityId = -1;
  this->EntityIdIsSet = 0;
  this->CreateEdgePointVerts = false;
}

//----------------------------------------------------------------------------
vtkCMBPolyDataProvider::~vtkCMBPolyDataProvider()
{
  this->SetModelWrapper(0);
}

//----------------------------------------------------------------------------
void vtkCMBPolyDataProvider::SetItemType(int itemType)
{
  this->ItemType = itemType;
  this->ItemTypeIsSet = 1;
}

//----------------------------------------------------------------------------
void vtkCMBPolyDataProvider::SetEntityId(vtkIdType Id)
{
  this->EntityId = Id;
  this->EntityIdIsSet = 1;
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkCMBPolyDataProvider::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "ModelWrapper: " << this->ModelWrapper << endl;
  os << indent << "ItemType: " << this->ItemType << endl;
  os << indent << "ItemTypeIsSet: " << this->ItemTypeIsSet << endl;
  os << indent << "EntityId: " << this->EntityId << endl;
  os << indent << "EntityIdIsSet: " << this->EntityIdIsSet << endl;
  os << indent << "CreateEdgePointVerts: " << this->CreateEdgePointVerts << endl;
}

//----------------------------------------------------------------------------
int vtkCMBPolyDataProvider::RequestUpdateExtent(
  vtkInformation *,
  vtkInformationVector **,
  vtkInformationVector *outputVector)
{
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  int piece, numPieces, ghostLevel;

  piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
  numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
  ghostLevel = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());

  // make sure piece is valid
  if (piece < 0 || piece >= numPieces)
    {
    return 1;
    }

  if (ghostLevel < 0)
    {
    return 1;
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkCMBPolyDataProvider::RequestData(
  vtkInformation* /*request*/,
  vtkInformationVector** vtkNotUsed( inputVector ),
  vtkInformationVector* outputVector)
{
  if(!this->ModelWrapper ||
     (this->EntityIdIsSet==0 && this->ItemTypeIsSet == 0) ||
     (this->EntityIdIsSet == 0 && this->ItemTypeIsSet != 0 &&
      this->ItemType != vtkModelType) )
    {
    vtkErrorMacro("Improper input to filter.");
    return 0;
    }

  vtkInformation *outInfo = outputVector->GetInformationObject(0);
  vtkPolyData *output = vtkPolyData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  vtkModelEntity* Entity;
  vtkDiscreteModel* Model = this->ModelWrapper->GetModel();
  const DiscreteMesh& MasterGrid = Model->GetMesh();

  if(this->ItemTypeIsSet && this->ItemType == vtkModelType)
    {
    if(!MasterGrid.IsValid())
      {
      vtkWarningMacro("Could not get CMBModel polydata.");
      return 0;
      }

    //polydata represent the whole model
    //so we shallow copy
    output->ShallowCopy(MasterGrid.ShallowCopyFaceData());
    }
  else
    {
    if(this->ItemTypeIsSet)
      {
      // the faster search method
      Entity = Model->GetModelEntity(this->ItemType, this->EntityId);
      }
    else
      {
      Entity = this->ModelWrapper->GetModel()->GetModelEntity(this->EntityId);
      }
    vtkModelGeometricEntity* GeomEntity = vtkModelGeometricEntity::SafeDownCast(Entity);
    if(!GeomEntity)
      {
      vtkWarningMacro("Input is for a non-geometric model entity.");
      return 0;
      }
    if(!GeomEntity->GetGeometry())
      {
      vtkWarningMacro("Input does not have valid geometry.");
      return 0;
      }

    // If this is an model edge, we want to add vertex to the polydata, so that
    // the edge points can be shown.
    vtkDiscreteModelEdge* EdgeEntity = vtkDiscreteModelEdge::SafeDownCast(Entity);
     // model edge with vertexes for points
    if(EdgeEntity && this->CreateEdgePointVerts)
      {
      vtkDiscreteModelVertex* vertex1 = vtkDiscreteModelVertex::SafeDownCast(
        EdgeEntity->GetAdjacentModelVertex(0));
      vtkDiscreteModelVertex* vertex2 = vtkDiscreteModelVertex::SafeDownCast(
        EdgeEntity->GetAdjacentModelVertex(1));
      vtkSmartPointer<vtkIdList> VertexPointIds =
        vtkSmartPointer<vtkIdList>::New();
      if(vertex1)
        {
        VertexPointIds->InsertUniqueId(vertex1->GetPointId());
        }
      if(vertex2)
        {
        VertexPointIds->InsertUniqueId(vertex2->GetPointId());
        }
      vtkPolyData* edgePoly = vtkPolyData::SafeDownCast(GeomEntity->GetGeometry());
      vtkIdType numCells = edgePoly->GetNumberOfCells();
      vtkIdType npts, *cellPnts;
      vtkSmartPointer<vtkIdList> PointIds =
        vtkSmartPointer<vtkIdList>::New();
      for(vtkIdType i=0; i<numCells; i++)
        {
        edgePoly->GetCellPoints(i, npts, cellPnts);
        for (vtkIdType ptIndex = 0; ptIndex < npts; ptIndex++)
          {
          vtkIdType pid = cellPnts[ptIndex];
          if(VertexPointIds->IsId(pid)<0)
            {
            PointIds->InsertUniqueId(pid);
            }
          }
        }
      vtkIdType NumberOfPointIds = PointIds->GetNumberOfIds();
      vtkCellArray* Verts = vtkCellArray::New();
      Verts->Allocate(NumberOfPointIds);
      for(vtkIdType i=0;i<NumberOfPointIds;i++)
        {
        vtkIdType PointId = PointIds->GetId(i);
        Verts->InsertNextCell(1, &PointId);
        }
      output->Initialize();
      output->SetPoints(MasterGrid.SharePointsPtr());
      output->SetLines(vtkPolyData::SafeDownCast(GeomEntity->GetGeometry())->GetLines());
      if(Verts->GetNumberOfCells()>0)
        {
        output->SetVerts(Verts);
        }
      Verts->Delete();
      EdgeEntity->SetLineAndPointsGeometry(
        this->CreateEdgePointVerts ? output : NULL);
      }
    else
      {
      output->ShallowCopy(vtkDataObject::SafeDownCast(GeomEntity->GetGeometry()));
      output->SetPoints(MasterGrid.SharePointsPtr());
      }
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkCMBPolyDataProvider::FillOutputPortInformation(
  int, vtkInformation* info)
{
  info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
  return 1;
}

