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

#include "vtkBoundingBox.h"
#include "vtkChooserPainter.h"
#include "vtkCMBModelSelectionPainter.h"
#include "vtkCommand.h"
#include "vtkCompositeDataIterator.h"
#include "vtkCompositeDataPipeline.h"
#include "vtkCompositeDataSet.h"
#include "vtkDisplayListPainter.h"
#include "vtkHardwareSelector.h"
#include "vtkInformation.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGL.h"
#include "vtkPolyData.h"
#include "vtkPolyDataPainter.h"
#include "vtkProperty.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkSmartPointer.h"
#include "vtkTexture.h"
#include "vtkTimerLog.h"

#include "vtkDiscreteModelWrapper.h"
#include "vtkDiscreteModel.h"
#include "vtkDiscreteModelEdge.h"
#include "vtkModelGeometricEntity.h"

vtkStandardNewMacro(vtkCMBModelMapper);
vtkCxxSetObjectMacro(vtkCMBModelMapper, CMBModel, vtkDiscreteModelWrapper);

//----------------------------------------------------------------------------
vtkCMBModelMapper::vtkCMBModelMapper()
{
  // Insert the vtkCMBModelSelectionPainter in the selection pipeline, so that the
  // selection painter can handle cmb model datasets as well.
  vtkCMBModelSelectionPainter* modelPainter = vtkCMBModelSelectionPainter::New();
  if(this->SelectionPainter)
    {
    modelPainter->SetDelegatePainter(
      this->SelectionPainter->GetDelegatePainter() ?
      this->SelectionPainter->GetDelegatePainter() :
      this->SelectionPainter);
    }
  this->SetSelectionPainter(modelPainter);
  modelPainter->FastDelete();
  this->CMBModel = NULL;
  this->SetNumberOfOutputPorts(1);
  this->ShowEdgePoints = false;
  this->ModelDisplayListId = 0;
}

//----------------------------------------------------------------------------
vtkCMBModelMapper::~vtkCMBModelMapper()
{
if (this->LastWindow)
  {
  this->ReleaseGraphicsResources(this->LastWindow);
  }
  this->SetCMBModel(0);
  this->SetSelectionPainter(0);
}

//----------------------------------------------------------------------------
int vtkCMBModelMapper::FillInputPortInformation(
  int port, vtkInformation* info)
{
  if (port == 0)
    {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
    info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
    info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCMBModelMapper");
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
int vtkCMBModelMapper::FillOutputPortInformation(
  int vtkNotUsed(port), vtkInformation* info)
{
  info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkDiscreteModelWrapper");
  return 1;
}

// Release the graphics resources used by this mapper.  In this case, release
// the display list if any.
//-----------------------------------------------------------------------------
void vtkCMBModelMapper::ReleaseGraphicsResources(vtkWindow *win)
{
  if (this->ModelDisplayListId && win && win->GetMapped())
    {
    win->MakeCurrent();
    glDeleteLists(this->ModelDisplayListId,1);
    }
  this->ModelDisplayListId = 0;
  this->Superclass::ReleaseGraphicsResources(win);
}

//-----------------------------------------------------------------------------
void vtkCMBModelMapper::RenderPiece(vtkRenderer* ren, vtkActor* act)
{
  vtkDataObject *input= this->GetInputDataObject(0, 0);
  //
  // make sure that we've been properly initialized
  //
  if (ren->GetRenderWindow()->CheckAbortStatus())
    {
    return;
    }

  if ( input == NULL )
    {
    vtkErrorMacro(<< "No input!");
    return;
    }
  else
    {
    this->InvokeEvent(vtkCommand::StartEvent,NULL);
    if (!this->Static)
      {
      this->GetInputAlgorithm()->Update();
      }
    this->InvokeEvent(vtkCommand::EndEvent,NULL);
    }
  if ( this->CMBModel == NULL )
    {
    vtkErrorMacro(<< "No cmb model!");
    return;
    }

  // Update Painter information if obsolete.
  if (this->PainterUpdateTime < this->GetMTime())
    {
    // turn on Immediate rendering so that the down stream
    // painters do not use their own display list. This mappping is handling
    // its own display list.
    this->ImmediateModeRendering = 1;
    this->UpdatePainterInformation();
    this->PainterUpdateTime.Modified();
    }

  // make sure our window is current
  ren->GetRenderWindow()->MakeCurrent();
  this->TimeToDraw = 0.0;

  // If we are rendering in selection mode, then we use the selection painter
  // instead of the standard painter.
  if (this->SelectionPainter && ren->GetSelector())
    {
    this->SelectionPainter->SetInput(this->CMBModel);
    this->SelectionPainter->Render(ren, act, 0xff,
      (this->ForceCompileOnly==1));
    this->TimeToDraw = this->SelectionPainter->GetTimeToDraw();
    }
  else if (this->SelectionPainter && this->SelectionPainter != this->Painter)
    {
    this->SelectionPainter->ReleaseGraphicsResources(ren->GetRenderWindow());
    }

  if (this->Painter && ren->GetSelector() == 0)
    {
    this->RenderInternal(input, ren, act,
      0xff,this->ForceCompileOnly==1);
    //this->TimeToDraw = this->Painter->GetTimeToDraw();
    this->TimeToDraw = this->Timer->GetElapsedTime();
    }

  // If the timer is not accurate enough, set it to a small
  // time so that it is not zero
  if ( this->TimeToDraw == 0.0 )
    {
    this->TimeToDraw = 0.0001;
    }

  this->UpdateProgress(1.0);
}

//----------------------------------------------------------------------------
void vtkCMBModelMapper::RenderInternal( vtkDataObject* inputObj,
  vtkRenderer* renderer, vtkActor* actor,
  unsigned long typeflags, bool forceCompileOnly)
{
  vtkCompositeDataSet* input = vtkCompositeDataSet::SafeDownCast(
    inputObj);
  if (!input)
    {
    vtkErrorMacro(<< "Expecting composite data input of CMB model!");
    return;
    }
  vtkDiscreteModelWrapper *model= this->CMBModel;
  if (!model)
    {
    return;
    }
  vtkProperty* actorProperty = actor->GetProperty();
  vtkProperty* backFaceProperty = actor->GetBackfaceProperty();
  // Time the actual drawing
  this->Timer->StartTimer();

  if ( this->GetMTime() > this->BuildTime ||
    input->GetMTime() > this->BuildTime ||
    actorProperty->GetMTime() > this->BuildTime ||
    renderer->GetRenderWindow() != this->LastWindow)
    {
    this->ReleaseGraphicsResources(renderer->GetRenderWindow());

    // get a unique display list id
     this->ModelDisplayListId = glGenLists(1);
     glNewList(this->ModelDisplayListId, GL_COMPILE);

    //actorProperty->SetOpacity(1.0);
    // cache what's original in model actor, since
    // these property may be changed depending on model entity types
    double actorOpacity = actorProperty->GetOpacity();
    int actorRep = actorProperty->GetRepresentation();
    int actorEdgeVis = actorProperty->GetEdgeVisibility();
    double actorPtSzie = actorProperty->GetPointSize();
    double actorLineWidth = actorProperty->GetLineWidth();
    vtkTexture* texture = actor->GetTexture();

    unsigned int curr_idx;
    vtkProperty* pProp;
    vtkModelGeometricEntity* entity;
    vtkDiscreteModelEdge* edgeEntity;
    vtkDataObject* dobj;
    int isFaceType;
    vtkCompositeDataIterator* iter = input->NewIterator();
    for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
      {
      curr_idx = iter->GetCurrentFlatIndex();
      entity = vtkModelGeometricEntity::SafeDownCast(
        model->GetEntityObjectByFlatIndex(curr_idx));
      if(!entity || !entity->GetVisibility())
        {
        continue;
        }

      pProp = entity->GetDisplayProperty();
      if(!pProp)
        {
        continue;
        }

      // we always use color from model entity
      actorProperty->SetColor(pProp->GetColor());

      isFaceType  = entity->IsA("vtkDiscreteModelFace");
      actorProperty->SetRepresentation(isFaceType ? actorRep :
        pProp->GetRepresentation());
      actorProperty->SetEdgeVisibility(isFaceType ? actorEdgeVis :
        pProp->GetEdgeVisibility());
      actorProperty->SetPointSize(isFaceType ? actorPtSzie :
        pProp->GetPointSize());
      actorProperty->SetOpacity(isFaceType ? actorOpacity :
        pProp->GetOpacity());
      actorProperty->SetLineWidth(isFaceType ? actorLineWidth :
        pProp->GetLineWidth());

      actorProperty->Render(actor, renderer);

      if(backFaceProperty)
        {
        backFaceProperty->Render(actor, renderer);
        }
      if(texture && entity->GetShowTexture())
        {
        texture->Render(renderer);
        }

      if(entity->IsA("vtkDiscreteModelEdge") && this->ShowEdgePoints)
        {
        edgeEntity = vtkDiscreteModelEdge::SafeDownCast(entity);
        dobj = vtkDataObject::SafeDownCast(
          edgeEntity->GetLineAndPointsGeometry());
        }
      else
        {
        dobj = iter->GetCurrentDataObject();
        }

      this->Painter->SetInput(dobj);
      this->Painter->Render(renderer, actor, typeflags, forceCompileOnly);
      }
    iter->Delete();

    glEndList();
    this->BuildTime.Modified();
    this->LastWindow = renderer->GetRenderWindow();
    glCallList(this->ModelDisplayListId);

    }
  else
    {
    // Pass input.
    glCallList(this->ModelDisplayListId);
    }

  this->Timer->StopTimer();
}

//-----------------------------------------------------------------------------
void vtkCMBModelMapper::ComputeBounds()
{
  if(this->BuildTime < this->BoundsMTime &&
    this->GetMTime() < this->BoundsMTime &&
    this->CMBModel->GetMTime() < this->BoundsMTime )
    {
    return;
    }
  vtkMath::UninitializeBounds(this->Bounds);
  vtkCompositeDataSet *input = vtkCompositeDataSet::SafeDownCast(
    this->GetInputDataObject(0, 0));

  // If we don't have hierarchical data, test to see if we have
  // plain old polydata. In this case, the bounds are simply
  // the bounds of the input polydata.
  if (!input)
    {
    this->Superclass::ComputeBounds();
    return;
    }

  vtkBoundingBox bbox;
  unsigned int curr_idx;
  vtkModelGeometricEntity* entity;
  vtkCompositeDataIterator* iter = input->NewIterator();
  for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
    {
    curr_idx = iter->GetCurrentFlatIndex();
    entity = vtkModelGeometricEntity::SafeDownCast(
      this->CMBModel->GetEntityObjectByFlatIndex(curr_idx));
    if(!entity || !entity->GetVisibility())
      {
      continue;
      }
    vtkPolyData *pd = vtkPolyData::SafeDownCast(iter->GetCurrentDataObject());
    if (pd)
      {
      double bounds[6];
      pd->GetBounds(bounds);
      bbox.AddBounds(bounds);
      }
    }
  iter->Delete();
  bbox.GetBounds(this->Bounds);
  this->BoundsMTime.Modified();
}

//----------------------------------------------------------------------------
void vtkCMBModelMapper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
