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

  Program:   Visualization Toolkit
  Module:    vtkFixedPointRayCastImage.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 "vtkFixedPointRayCastImage.h"
#include "vtkObjectFactory.h"

vtkStandardNewMacro(vtkFixedPointRayCastImage);

// Construct a new vtkFixedPointRayCastImage with default values
vtkFixedPointRayCastImage::vtkFixedPointRayCastImage()
{
  this->ImageViewportSize[0] = 0;
  this->ImageViewportSize[1] = 0;

  this->ImageMemorySize[0]   = 0;
  this->ImageMemorySize[1]   = 0;

  this->ImageInUseSize[0]    = 0;
  this->ImageInUseSize[1]    = 0;

  this->ImageOrigin[0]       = 0;
  this->ImageOrigin[1]       = 0;

  this->ImageSampleDistance  = 0;

  this->ZBufferMemorySize    = 0;

  this->ZBufferSize[0]       = 0;
  this->ZBufferSize[1]       = 0;

  this->ZBufferOrigin[0]     = 0;
  this->ZBufferOrigin[1]     = 0;

  this->UseZBuffer           = 0;

  this->Image                = nullptr;
  this->ZBuffer              = nullptr;
}

// Destruct a vtkFixedPointRayCastImage - clean up any memory used
vtkFixedPointRayCastImage::~vtkFixedPointRayCastImage()
{
  delete [] this->Image;
  delete [] this->ZBuffer;
}

// Allocate memory
void vtkFixedPointRayCastImage::AllocateImage()
{
  delete [] this->Image;
  this->Image = nullptr;

  if ( this->ImageMemorySize[0] > 0 &&
       this->ImageMemorySize[1] > 0 )
  {
    this->Image = new unsigned short [ ( 4 *
                                         this->ImageMemorySize[0] *
                                         this->ImageMemorySize[1] )];
  }
}

// Clear image to 0
void vtkFixedPointRayCastImage::ClearImage()
{
  unsigned short *ucptr = this->Image;

  for ( int i = 0; i < this->ImageMemorySize[0]*this->ImageMemorySize[1]; i++ )
  {
    *(ucptr++) = 0;
    *(ucptr++) = 0;
    *(ucptr++) = 0;
    *(ucptr++) = 0;
  }
}

void vtkFixedPointRayCastImage::AllocateZBuffer()
{
  // If we already have a buffer big enough, don't
  // bother to do anything
  if ( this->ZBufferSize[0]*this->ZBufferSize[1] >
       this->ZBufferMemorySize )
  {
    // If our current buffer is not large enough, delete it
    delete [] this->ZBuffer;
    this->ZBuffer = nullptr;

    // Try out a size equal to the viewport in pixels
    this->ZBufferMemorySize =
      this->ImageViewportSize[0] * this->ImageViewportSize[1];

    // This shouldn't ever happen, but just in case....
    // If this happens it means that somehow our viewport
    // size in pixels is smaller than the zbuffer we are
    // requesting - which will probably not make OpenGL
    // very happy.
    if ( this->ZBufferMemorySize <
         this->ZBufferSize[0]*this->ZBufferSize[1] )
    {
      this->ZBufferMemorySize =
        this->ZBufferSize[0] * this->ZBufferSize[1];
    }

    // Allocate the memory
    this->ZBuffer = new float [this->ZBufferMemorySize];
  }
}

float vtkFixedPointRayCastImage::GetZBufferValue(int x, int y)
{
  if ( !this->UseZBuffer )
  {
    return 1.0;
  }

  int xPos, yPos;

  xPos = static_cast<int>(static_cast<float>(x) * this->ImageSampleDistance);
  yPos = static_cast<int>(static_cast<float>(y) * this->ImageSampleDistance);

  xPos = (xPos >= this->ZBufferSize[0])?(this->ZBufferSize[0]-1):(xPos);
  yPos = (yPos >= this->ZBufferSize[1])?(this->ZBufferSize[1]-1):(yPos);

  return *(this->ZBuffer + yPos*this->ZBufferSize[0] + xPos);
}

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

  os << indent << "Image Viewport Size: "
     << this->ImageViewportSize[0] << " "
     << this->ImageViewportSize[1] << endl;

  os << indent << "Image Memory Size: "
     << this->ImageMemorySize[0] << " "
     << this->ImageMemorySize[1] << endl;

  os << indent << "Image In Use Size: "
     << this->ImageInUseSize[0] << " "
     << this->ImageInUseSize[1] << endl;

  os << indent << "Image Origin: "
     << this->ImageOrigin[0] << " "
     << this->ImageOrigin[1] << endl;

  os << indent << "Image Sample Distance: "
     << this->ImageSampleDistance << endl;

  os << indent << "Use ZBuffer: "
     << (this->UseZBuffer ? "On" : "Off") << endl;

  os << indent << "ZBuffer Origin: "
     << this->ZBufferOrigin[0] << " "
     << this->ZBufferOrigin[1] << endl;

  os << indent << "ZBuffer Size: "
     << this->ZBufferSize[0] << " "
     << this->ZBufferSize[1] << endl;


}

