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

  Program:   Visualization Toolkit
  Module:    TestGPURayCastCompositeMask.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 "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkGPUVolumeRayCastMapper.h"
#include "vtkImageCheckerboard.h"
#include "vtkImageData.h"
#include "vtkImageGridSource.h"
#include "vtkPiecewiseFunction.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTestUtilities.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include "vtkXMLImageDataReader.h"

int TestGPURayCastCompositeMask(int argc, char* argv[])
{
  cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl;
  char* cfname = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/vase_1comp.vti");

  vtkXMLImageDataReader* reader = vtkXMLImageDataReader::New();
  reader->SetFileName(cfname);
  delete[] cfname;

  reader->Update();
  vtkImageData* input = reader->GetOutput();

  int dim[3];
  double spacing[3];
  input->GetSpacing(spacing);
  input->GetDimensions(dim);

  vtkGPUVolumeRayCastMapper* mapper = vtkGPUVolumeRayCastMapper::New();
  vtkVolume* volume = vtkVolume::New();
  mapper->SetInputConnection(reader->GetOutputPort());
  mapper->SetAutoAdjustSampleDistances(0);

  // assume the scalar field is a set of samples taken from a
  // contiguous band-limited volumetric field.
  // assume max frequency is present:
  // min spacing divided by 2. Nyquist-Shannon theorem says so.
  // sample distance could be bigger if we compute the actual max frequency
  // in the data.
  double distance = spacing[0];
  if (distance > spacing[1])
  {
    distance = spacing[1];
  }
  if (distance > spacing[2])
  {
    distance = spacing[2];
  }
  distance = distance / 2.0;

  // This does not take the screen size of a cell into account.
  // distance has to be smaller: min(nyquis,screensize)

  mapper->SetSampleDistance(static_cast<float>(distance));

  vtkColorTransferFunction* colorFun = vtkColorTransferFunction::New();
  vtkPiecewiseFunction* opacityFun = vtkPiecewiseFunction::New();

  // Create the property and attach the transfer functions
  vtkVolumeProperty* property = vtkVolumeProperty::New();
  property->SetIndependentComponents(true);
  property->SetColor(colorFun);
  property->SetScalarOpacity(opacityFun);
  property->SetInterpolationTypeToLinear();

  // connect up the volume to the property and the mapper
  volume->SetProperty(property);
  volume->SetMapper(mapper);

  double opacityLevel = 120;
  double opacityWindow = 240;

  colorFun->AddRGBSegment(opacityLevel - 0.5 * opacityWindow, 0.0, 0.0, 0.0,
    opacityLevel + 0.5 * opacityWindow, 1.0, 1.0, 1.0);
  opacityFun->AddSegment(opacityLevel - 0.5 * opacityWindow, 0.0, // 0.0, 0.01
    opacityLevel + 0.5 * opacityWindow, 1.0);                     // 1.0, 0.01
  mapper->SetBlendModeToComposite();
  property->ShadeOff();

  // Make the mask
  vtkImageGridSource* grid = vtkImageGridSource::New();
  grid->SetDataScalarTypeToUnsignedChar();
  grid->SetDataExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);
  grid->SetLineValue(1); // mask value
  grid->SetFillValue(0);
  grid->SetGridSpacing(5, 5, 5);
  grid->Update();
  mapper->SetMaskInput(grid->GetOutput());

  vtkImageGridSource* grid2 = vtkImageGridSource::New();
  grid2->SetDataScalarTypeToUnsignedChar();
  grid2->SetDataExtent(0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1);
  grid2->SetLineValue(2); // mask value
  grid2->SetFillValue(0);
  grid2->SetGridSpacing(6, 6, 6);
  grid2->Update();

  vtkImageCheckerboard* checkerboard = vtkImageCheckerboard::New();
  checkerboard->SetInputConnection(0, grid->GetOutputPort());

  checkerboard->SetInputConnection(1, grid2->GetOutputPort());
  grid->Delete();
  grid2->Delete();
  checkerboard->Update();
  mapper->SetMaskInput(checkerboard->GetOutput());
  checkerboard->Delete();

  // Add color transfer functions for the masks
  vtkColorTransferFunction* mask1colorFun = vtkColorTransferFunction::New();
  property->SetLabelColor(1, mask1colorFun);
  property->SetLabelScalarOpacity(1, opacityFun);
  mask1colorFun->Delete();

  // yellow.
  mask1colorFun->AddRGBSegment(opacityLevel - 0.5 * opacityWindow, 0.0, 1.0, 0.0,
    opacityLevel + 0.5 * opacityWindow, 1.0, 1.0, 0.0);

  vtkColorTransferFunction* mask2colorFun = vtkColorTransferFunction::New();
  property->SetLabelColor(2, mask2colorFun);
  property->SetLabelScalarOpacity(2, opacityFun);
  mask2colorFun->Delete();

  // red
  mask2colorFun->AddRGBSegment(opacityLevel - 0.5 * opacityWindow, 0.5, 0.0, 0.0,
    opacityLevel + 0.5 * opacityWindow, 1.0, 0.0, 0.0);

  vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::New();
  vtkRenderWindow* renWin = vtkRenderWindow::New();
  renWin->SetSize(300, 300);
  iren->SetRenderWindow(renWin);

  vtkRenderer* ren1 = vtkRenderer::New();
  renWin->AddRenderer(ren1);

  renWin->Render();

  int valid = mapper->IsRenderSupported(renWin, property);

  int retVal;
  if (valid)
  {
    ren1->AddViewProp(volume);
    iren->Initialize();
    ren1->SetBackground(0.1, 0.4, 0.2);
    ren1->ResetCamera();
    ren1->GetActiveCamera()->Zoom(1.5);
    renWin->Render();

    retVal = vtkTesting::Test(argc, argv, renWin, 75);
    if (retVal == vtkRegressionTester::DO_INTERACTOR)
    {
      iren->Start();
    }
  }
  else
  {
    retVal = vtkTesting::PASSED;
    cout << "Required extensions not supported." << endl;
  }

  volume->Delete();
  mapper->Delete();
  colorFun->Delete();
  opacityFun->Delete();
  property->Delete();
  ren1->Delete();
  renWin->Delete();
  iren->Delete();
  reader->Delete();

  if ((retVal == vtkTesting::PASSED) || (retVal == vtkTesting::DO_INTERACTOR))
  {
    return 0;
  }
  else
  {
    return 1;
  }
}
