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

  Program:   Visualization Toolkit
  Module:    vtkDiagonalMatrixSource.cxx

-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------

  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 "vtkDiagonalMatrixSource.h"
#include "vtkDenseArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkSparseArray.h"

// ----------------------------------------------------------------------

vtkStandardNewMacro(vtkDiagonalMatrixSource);

// ----------------------------------------------------------------------

vtkDiagonalMatrixSource::vtkDiagonalMatrixSource()
  : ArrayType(DENSE)
  , Extents(3)
  , Diagonal(1.0)
  , SuperDiagonal(0.0)
  , SubDiagonal(0.0)
  , RowLabel(nullptr)
  , ColumnLabel(nullptr)
{
  this->SetRowLabel("rows");
  this->SetColumnLabel("columns");

  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
}

// ----------------------------------------------------------------------

vtkDiagonalMatrixSource::~vtkDiagonalMatrixSource()
{
  this->SetRowLabel(nullptr);
  this->SetColumnLabel(nullptr);
}

// ----------------------------------------------------------------------

void vtkDiagonalMatrixSource::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "ArrayType: " << this->ArrayType << endl;
  os << indent << "Extents: " << this->Extents << endl;
  os << indent << "Diagonal: " << this->Diagonal << endl;
  os << indent << "SuperDiagonal: " << this->SuperDiagonal << endl;
  os << indent << "SubDiagonal: " << this->SubDiagonal << endl;
  os << indent << "RowLabel: " << (this->RowLabel ? this->RowLabel : "") << endl;
  os << indent << "ColumnLabel: " << (this->ColumnLabel ? this->ColumnLabel : "") << endl;
}

// ----------------------------------------------------------------------

int vtkDiagonalMatrixSource::RequestData(
  vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector)
{
  if (this->Extents < 1)
  {
    vtkErrorMacro(<< "Invalid matrix extents: " << this->Extents << "x" << this->Extents
                  << " array is not supported.");
    return 0;
  }

  vtkArray* array = nullptr;
  switch (this->ArrayType)
  {
    case DENSE:
      array = this->GenerateDenseArray();
      break;
    case SPARSE:
      array = this->GenerateSparseArray();
      break;
    default:
      vtkErrorMacro(<< "Invalid array type: " << this->ArrayType << ".");
      return 0;
  }

  vtkArrayData* const output = vtkArrayData::GetData(outputVector);
  output->ClearArrays();
  output->AddArray(array);
  array->Delete();

  return 1;
}

vtkArray* vtkDiagonalMatrixSource::GenerateDenseArray()
{
  vtkDenseArray<double>* const array = vtkDenseArray<double>::New();
  array->Resize(vtkArrayExtents::Uniform(2, this->Extents));
  array->SetDimensionLabel(0, this->RowLabel);
  array->SetDimensionLabel(1, this->ColumnLabel);

  array->Fill(0.0);

  if (this->Diagonal != 0.0)
  {
    for (vtkIdType i = 0; i != this->Extents; ++i)
      array->SetValue(vtkArrayCoordinates(i, i), this->Diagonal);
  }

  if (this->SuperDiagonal != 0.0)
  {
    for (vtkIdType i = 0; i + 1 != this->Extents; ++i)
      array->SetValue(vtkArrayCoordinates(i, i + 1), this->SuperDiagonal);
  }

  if (this->SubDiagonal != 0.0)
  {
    for (vtkIdType i = 0; i + 1 != this->Extents; ++i)
      array->SetValue(vtkArrayCoordinates(i + 1, i), this->SubDiagonal);
  }

  return array;
}

vtkArray* vtkDiagonalMatrixSource::GenerateSparseArray()
{
  vtkSparseArray<double>* const array = vtkSparseArray<double>::New();
  array->Resize(vtkArrayExtents::Uniform(2, this->Extents));
  array->SetDimensionLabel(0, this->RowLabel);
  array->SetDimensionLabel(1, this->ColumnLabel);

  if (this->Diagonal != 0.0)
  {
    for (vtkIdType i = 0; i != this->Extents; ++i)
      array->AddValue(vtkArrayCoordinates(i, i), this->Diagonal);
  }

  if (this->SuperDiagonal != 0.0)
  {
    for (vtkIdType i = 0; i + 1 != this->Extents; ++i)
      array->AddValue(vtkArrayCoordinates(i, i + 1), this->SuperDiagonal);
  }

  if (this->SubDiagonal != 0.0)
  {
    for (vtkIdType i = 0; i + 1 != this->Extents; ++i)
      array->AddValue(vtkArrayCoordinates(i + 1, i), this->SubDiagonal);
  }

  return array;
}
