/*=========================================================================

Program:   Visualization Toolkit
Module:    vtkDirect3DRenderer.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkDirect3DRenderer.h"

#include "vtkDirect3DHelper.h"

#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkHardwareSelector.h"
#include "vtkLight.h"
#include "vtkLightCollection.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
//#include "vtkDirect3DCamera.h"
#include "vtkDirect3DRenderWindow.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
//#include "vtkPolyDataMapper2D.h"
#include "vtkRenderPass.h"
#include "vtkRenderState.h"
#include "vtkTexture.h"
//#include "vtkTextureObject.h"
#include "vtkTexturedActor2D.h"
#include "vtkTimerLog.h"
//#include "vtkTranslucentPass.h"
#include "vtkTrivialProducer.h"
#include "vtkUnsignedCharArray.h"

#include <cmath>
#include <cassert>
#include <cstdlib>
#include <list>
#include <string>

vtkStandardNewMacro(vtkDirect3DRenderer);

vtkDirect3DRenderer::vtkDirect3DRenderer()
{
  this->PickedZ = 0;

  this->DepthPeelingHigherLayer=0;

  this->BackgroundTexture = 0;
}

// Ask lights to load themselves into graphics pipeline.
int vtkDirect3DRenderer::UpdateLights ()
{
  vtkLight *light;
  float status;
  int count = 0;

  vtkCollectionSimpleIterator sit;
  for(this->Lights->InitTraversal(sit);
      (light = this->Lights->GetNextLight(sit)); )
  {
    status = light->GetSwitch();
    if (status > 0.0)
    {
      count++;
    }
  }

  if( !count )
  {
    vtkDebugMacro(<<"No lights are on, creating one.");
    this->CreateLight();
  }

  for(this->Lights->InitTraversal(sit);
      (light = this->Lights->GetNextLight(sit)); )
  {
    status = light->GetSwitch();

    // if the light is on then define it and bind it.
    if (status > 0.0)
    {
      light->Render(this,0);
    }
  }

  return count;
}

// ----------------------------------------------------------------------------
// Description:
// Is rendering at translucent geometry stage using depth peeling and
// rendering a layer other than the first one? (Boolean value)
// If so, the uniform variables UseTexture and Texture can be set.
// (Used by vtkDirect3DProperty or vtkDirect3DTexture)
int vtkDirect3DRenderer::GetDepthPeelingHigherLayer()
{
  return this->DepthPeelingHigherLayer;
}

// ----------------------------------------------------------------------------
// Concrete render method.
void vtkDirect3DRenderer::DeviceRender(void)
{
  vtkTimerLog::MarkStartEvent("Direct3D Dev Render");

  // set some ivars we use alot
  vtkDirect3DRenderWindow *rw =
   static_cast<vtkDirect3DRenderWindow *>(this->GetRenderWindow());

   this->ImmediateContext = rw->GetImmediateContext();
   this->RenderTargetView = rw->GetRenderTargetView();
   this->DepthStencilView = rw->GetDepthStencilView();

  if(this->Pass!=0)
  {
    vtkRenderState s(this);
    s.SetPropArrayAndCount(this->PropArray, this->PropArrayCount);
    s.SetFrameBuffer(0);
    this->Pass->Render(&s);
  }
  else
  {
    // Do not remove this MakeCurrent! Due to Start / End methods on
    // some objects which get executed during a pipeline update,
    // other windows might get rendered since the last time
    // a MakeCurrent was called.
    this->RenderWindow->MakeCurrent();

    this->UpdateCamera();
    this->UpdateLightGeometry();
    this->UpdateLights();
    this->UpdateGeometry();

  }

  vtkTimerLog::MarkEndEvent("Direct3D Dev Render");
}

// Ask actors to render themselves. As a side effect will cause
// visualization network to update.
int vtkDirect3DRenderer::UpdateGeometry()
{
  int        i;

  this->NumberOfPropsRendered = 0;

  if ( this->PropArrayCount == 0 )
  {
    return 0;
  }

  if (this->Selector)
  {
    // When selector is present, we are performing a selection,
    // so do the selection rendering pass instead of the normal passes.
    // Delegate the rendering of the props to the selector itself.
    this->NumberOfPropsRendered = this->Selector->Render(this,
      this->PropArray, this->PropArrayCount);
    this->RenderTime.Modified();
    vtkDebugMacro("Rendered " << this->NumberOfPropsRendered << " actors" );
    return this->NumberOfPropsRendered;
  }

  // Opaque geometry first:
  this->DeviceRenderOpaqueGeometry();

  // do the render library specific stuff about translucent polygonal geometry.
  // As it can be expensive, do a quick check if we can skip this step
  int hasTranslucentPolygonalGeometry=0;
  for ( i = 0; !hasTranslucentPolygonalGeometry && i < this->PropArrayCount;
        i++ )
  {
    hasTranslucentPolygonalGeometry=
      this->PropArray[i]->HasTranslucentPolygonalGeometry();
  }
  if(hasTranslucentPolygonalGeometry)
  {
    this->DeviceRenderTranslucentPolygonalGeometry();
  }

  // loop through props and give them a chance to
  // render themselves as volumetric geometry.
  for ( i = 0; i < this->PropArrayCount; i++ )
  {
    this->NumberOfPropsRendered +=
      this->PropArray[i]->RenderVolumetricGeometry(this);
  }

  // loop through props and give them a chance to
  // render themselves as an overlay (or underlay)
  for ( i = 0; i < this->PropArrayCount; i++ )
  {
    this->NumberOfPropsRendered +=
      this->PropArray[i]->RenderOverlay(this);
  }

  this->RenderTime.Modified();

  vtkDebugMacro( << "Rendered " <<
                    this->NumberOfPropsRendered << " actors" );

  return  this->NumberOfPropsRendered;
}

// ----------------------------------------------------------------------------
void vtkDirect3DRenderer::DeviceRenderOpaqueGeometry()
{
  this->Superclass::DeviceRenderOpaqueGeometry();
}

// ----------------------------------------------------------------------------
// Description:
// Render translucent polygonal geometry. Default implementation just call
// UpdateTranslucentPolygonalGeometry().
// Subclasses of vtkRenderer that can deal with depth peeling must
// override this method.
void vtkDirect3DRenderer::DeviceRenderTranslucentPolygonalGeometry()
{
  if(!this->UseDepthPeeling)
  {
    // just alpha blending
    this->UpdateTranslucentPolygonalGeometry();
  }

}


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

  os << indent << "PickedZ " << this->PickedZ << "\n";
}


void vtkDirect3DRenderer::Clear(void)
{
  if (! this->Transparent())
  {
    float color[4];
    if (this->IsPicking)
    {
      color[0] = 0.0;
      color[1] = 0.0;
      color[2] = 0.0;
      color[3] = 0.0;
    }
    else
    {
      color[0] = this->Background[0];
      color[1] = this->Background[1];
      color[2] = this->Background[2];
      color[3] = 0.0;
    }

    // Setup the viewport
    int lowerLeft[2];
    int usize, vsize;
    this->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft+1);
    D3D11_VIEWPORT vp;
    vp.Width = static_cast<float>(usize);
    vp.Height = static_cast<float>(vsize);
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = lowerLeft[0];
    vp.TopLeftY = lowerLeft[1];
    this->ImmediateContext->RSSetViewports( 1, &vp );

    this->ImmediateContext->ClearRenderTargetView(
      this->RenderTargetView,
      color );
    }

  if (!this->GetPreserveDepthBuffer())
  {
    this->ImmediateContext->ClearDepthStencilView(
      this->DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );
  }
}


void vtkDirect3DRenderer::StartPick(unsigned int vtkNotUsed(pickFromSize))
{
  // Do not remove this MakeCurrent! Due to Start / End methods on
  // some objects which get executed during a pipeline update,
  // other windows might get rendered since the last time
  // a MakeCurrent was called.
  this->RenderWindow->MakeCurrent();
  this->RenderWindow->IsPickingOn();
  this->IsPicking = 1;

  this->Clear();
}

void vtkDirect3DRenderer::ReleaseGraphicsResources(vtkWindow *w)
{
  if (w && this->Pass)
  {
    this->Pass->ReleaseGraphicsResources(w);
  }
  this->Superclass::ReleaseGraphicsResources(w);
}

void vtkDirect3DRenderer::UpdatePickId()
{
  this->CurrentPickId++;
}


void vtkDirect3DRenderer::DevicePickRender()
{
  // Do not remove this MakeCurrent! Due to Start / End methods on
  // some objects which get executed during a pipeline update,
  // other windows might get rendered since the last time
  // a MakeCurrent was called.
  this->RenderWindow->MakeCurrent();

  this->UpdateCamera();
  this->UpdateLightGeometry();
  this->UpdateLights();

  this->PickGeometry();
}


void vtkDirect3DRenderer::DonePick()
{
  this->RenderWindow->IsPickingOff();
  this->IsPicking = 0;
}

double vtkDirect3DRenderer::GetPickedZ()
{
  return this->PickedZ;
}

unsigned int vtkDirect3DRenderer::GetPickedId()
{
  return 0;
}

vtkDirect3DRenderer::~vtkDirect3DRenderer()
{
  if(this->Pass != NULL)
  {
    this->Pass->UnRegister(this);
    this->Pass = NULL;
  }

}

unsigned int vtkDirect3DRenderer::GetNumPickedIds()
{
  return 0;
}

int vtkDirect3DRenderer::GetPickedIds(unsigned int atMost,
                                    unsigned int *callerBuffer)
{
  return 0;
}
