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

  Program:   Visualization Toolkit
  Module:    vtkExodusIIWriter.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.

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

/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/

#include "vtkPExodusIIWriter.h"
#include "vtkArrayIteratorIncludes.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCompositeDataIterator.h"
#include "vtkCompositeDataSet.h"
#include "vtkDataObject.h"
#include "vtkDoubleArray.h"
#include "vtkFieldData.h"
#include "vtkFloatArray.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkIntArray.h"
#include "vtkModelMetadata.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkThreshold.h"
#include "vtkUnstructuredGrid.h"

#include "vtkMultiProcessController.h"

#include "vtk_exodusII.h"
#include <cctype>
#include <ctime>

vtkStandardNewMacro(vtkPExodusIIWriter);

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

vtkPExodusIIWriter::vtkPExodusIIWriter() = default;

vtkPExodusIIWriter::~vtkPExodusIIWriter() = default;

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

//------------------------------------------------------------------------------
int vtkPExodusIIWriter::CheckParameters()
{
  vtkMultiProcessController* c = vtkMultiProcessController::GetGlobalController();
  int numberOfProcesses = c ? c->GetNumberOfProcesses() : 1;
  int myRank = c ? c->GetLocalProcessId() : 0;

  if (this->GhostLevel > 0)
  {
    vtkWarningMacro(<< "ExodusIIWriter ignores ghost level request");
  }

  return this->Superclass::CheckParametersInternal(numberOfProcesses, myRank);
}

//------------------------------------------------------------------------------
int vtkPExodusIIWriter::RequestUpdateExtent(
  vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  this->Superclass::RequestUpdateExtent(request, inputVector, outputVector);
  vtkMultiProcessController* c = vtkMultiProcessController::GetGlobalController();
  if (c)
  {
    int numberOfProcesses = c->GetNumberOfProcesses();
    int myRank = c->GetLocalProcessId();

    vtkInformation* info = inputVector[0]->GetInformationObject(0);
    info->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), myRank);
    info->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), numberOfProcesses);
  }

  return 1;
}

void vtkPExodusIIWriter::CheckBlockInfoMap()
{
  // if we're multiprocess we need to make sure the block info map matches
  if (this->NumberOfProcesses > 1)
  {
    int maxId = -1;
    std::map<int, Block>::const_iterator iter;
    for (iter = this->BlockInfoMap.begin(); iter != this->BlockInfoMap.end(); ++iter)
    {
      if (iter->first > maxId)
      {
        maxId = iter->first;
      }
    }
    vtkMultiProcessController* c = vtkMultiProcessController::GetGlobalController();
    int globalMaxId;
    c->AllReduce(&maxId, &globalMaxId, 1, vtkCommunicator::MAX_OP);
    maxId = globalMaxId;
    for (int i = 1; i <= maxId; i++)
    {
      Block& b = this->BlockInfoMap[i]; // ctor called (init all to 0/-1) if not preset
      int globalType;
      c->AllReduce(&b.Type, &globalType, 1, vtkCommunicator::MAX_OP);
      if (b.Type != 0 && b.Type != globalType)
      {
        vtkWarningMacro(<< "The type associated with ID's across processors doesn't match");
      }
      else
      {
        b.Type = globalType;
      }
      int globalNodes;
      c->AllReduce(&b.NodesPerElement, &globalNodes, 1, vtkCommunicator::MAX_OP);
      if (b.NodesPerElement != globalNodes &&
        // on a processor with no data, b.NodesPerElement == 0.
        b.NodesPerElement != 0)
      {
        vtkWarningMacro(<< "NodesPerElement associated with ID's across "
                           "processors doesn't match: "
                        << b.NodesPerElement << " != " << globalNodes);
      }
      else
      {
        b.NodesPerElement = globalNodes;
      }
    }
  }
}

//------------------------------------------------------------------------------
int vtkPExodusIIWriter::GlobalContinueExecuting(int localContinue)
{
  vtkMultiProcessController* c = vtkMultiProcessController::GetGlobalController();
  int globalContinue = localContinue;
  if (c)
  {
    c->AllReduce(&localContinue, &globalContinue, 1, vtkCommunicator::MIN_OP);
  }
  return globalContinue;
}

//------------------------------------------------------------------------------
unsigned int vtkPExodusIIWriter::GetMaxNameLength()
{
  unsigned int maxName = this->Superclass::GetMaxNameLength();

  vtkMultiProcessController* c = vtkMultiProcessController::GetGlobalController();
  unsigned int globalMaxName = 0;
  c->AllReduce(&maxName, &globalMaxName, 1, vtkCommunicator::MAX_OP);
  return maxName;
}
