// SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkPVSystemConfigInformation.h"

#include "vtkClientServerStream.h"
#include "vtkObjectFactory.h"
#include "vtkProcessModule.h"

#include <sstream>
using std::ostringstream;
#include <algorithm>
using std::sort;
#include <iostream>

#include <vtksys/SystemInformation.hxx>

// #define vtkPVSystemConfigInformationDEBUG

#define vtkVerifyParseMacro(_call, _field)                                                         \
  if (!(_call))                                                                                    \
  {                                                                                                \
    vtkErrorMacro("Error parsing " _field ".");                                                    \
    return;                                                                                        \
  }

void vtkPVSystemConfigInformation::ConfigInfo::Print()
{
  std::cerr << "OSDescriptor=" << this->OSDescriptor << endl
            << "CPUDescriptor=" << this->CPUDescriptor << endl
            << "MemDescriptor=" << this->MemDescriptor << endl
            << "HostName=" << this->HostName << endl
            << "ProcessType=" << this->ProcessType << endl
            << "SystemType=" << this->SystemType << endl
            << "Rank=" << this->Rank << endl
            << "Pid=" << this->Pid << endl
            << "HostMemoryTotal=" << this->HostMemoryTotal << endl
            << "HostMemoryAvailable=" << this->HostMemoryAvailable << endl
            << "ProcMemoryAvailable=" << this->ProcMemoryAvailable << endl
            << "ProcMemoryAvailable=" << this->ProcMemoryAvailable << endl;
}

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPVSystemConfigInformation);

//----------------------------------------------------------------------------
#ifdef vtkPVSystemConfigInformationDEBUG
vtkPVSystemConfigInformation::vtkPVSystemConfigInformation()
{
  std::cerr << "=====vtkPVSystemConfigInformation::vtkPVSystemConfigInformation" << endl;
}
#else
vtkPVSystemConfigInformation::vtkPVSystemConfigInformation() = default;
#endif

//----------------------------------------------------------------------------
#ifdef vtkPVSystemConfigInformationDEBUG
vtkPVSystemConfigInformation::~vtkPVSystemConfigInformation()
{
  std::cerr << "=====vtkPVSystemConfigInformation::~vtkPVSystemConfigInformation" << endl;
}
#else
vtkPVSystemConfigInformation::~vtkPVSystemConfigInformation() = default;
#endif

//----------------------------------------------------------------------------
void vtkPVSystemConfigInformation::CopyFromObject(vtkObject* obj)
{
#ifdef vtkPVSystemConfigInformationDEBUG
  std::cerr << "=====vtkPVSystemConfigInformation::CopyFromObject" << endl;
#endif

  (void)obj;

  this->Configs.clear();

  ConfigInfo info;

  vtksys::SystemInformation sysInfo;
  sysInfo.RunCPUCheck();
  sysInfo.RunOSCheck();
  sysInfo.RunMemoryCheck();

  info.OSDescriptor = sysInfo.GetOSDescription();
  info.CPUDescriptor = sysInfo.GetCPUDescription();
  info.MemDescriptor = sysInfo.GetMemoryDescription();
  info.HostName = sysInfo.GetHostname();
  info.ProcessType = vtkProcessModule::GetProcessType();
  info.SystemType = sysInfo.GetOSIsWindows();
  info.Rank = vtkProcessModule::GetProcessModule()->GetPartitionId();
  info.Pid = sysInfo.GetProcessId();
  info.HostMemoryTotal = sysInfo.GetHostMemoryTotal();
  // the folloowing envornment variables are querried, if set they
  // are the means of reporting limits that are applied in a non-
  // standard way.
  info.HostMemoryAvailable = sysInfo.GetHostMemoryAvailable("PV_HOST_MEMORY_LIMIT");
  info.ProcMemoryAvailable =
    sysInfo.GetProcMemoryAvailable("PV_HOST_MEMORY_LIMIT", "PV_PROC_MEMORY_LIMIT");

#ifdef vtkPVSystemConfigInformationDEBUG
  info.Print();
#endif

  this->Configs.push_back(info);
}

//----------------------------------------------------------------------------
void vtkPVSystemConfigInformation::AddInformation(vtkPVInformation* pvinfo)
{
#ifdef vtkPVSystemConfigInformationDEBUG
  std::cerr << "=====vtkPVSystemConfigInformation::AddInformation" << endl;
#endif
  vtkPVSystemConfigInformation* info = dynamic_cast<vtkPVSystemConfigInformation*>(pvinfo);

  if (!info)
  {
    return;
  }

  this->Configs.insert(this->Configs.end(), info->Configs.begin(), info->Configs.end());
}

//----------------------------------------------------------------------------
void vtkPVSystemConfigInformation::CopyToStream(vtkClientServerStream* css)
{
#ifdef vtkPVSystemConfigInformationDEBUG
  std::cerr << "=====vtkPVSystemConfigInformation::CopyToStream" << endl;
#endif

  css->Reset();

  size_t numberOfConfigs = this->Configs.size();

  *css << vtkClientServerStream::Reply << numberOfConfigs;

  for (size_t i = 0; i < numberOfConfigs; ++i)
  {
    *css << this->Configs[i].OSDescriptor.c_str() << this->Configs[i].CPUDescriptor.c_str()
         << this->Configs[i].MemDescriptor.c_str() << this->Configs[i].HostName.c_str()
         << this->Configs[i].ProcessType << this->Configs[i].SystemType << this->Configs[i].Rank
         << this->Configs[i].Pid << this->Configs[i].HostMemoryTotal
         << this->Configs[i].HostMemoryAvailable << this->Configs[i].ProcMemoryAvailable;
  }

  *css << vtkClientServerStream::End;
}

//----------------------------------------------------------------------------
void vtkPVSystemConfigInformation::CopyFromStream(const vtkClientServerStream* css)
{
#ifdef vtkPVSystemConfigInformationDEBUG
  std::cerr << "=====vtkPVSystemConfigInformation::CopyFromStream" << endl;
#endif

  int offset = 0;
  size_t numberOfConfigs = 0;

  vtkVerifyParseMacro(css->GetArgument(0, offset, &numberOfConfigs), "numberOfConfigs");
  ++offset;

  this->Configs.resize(numberOfConfigs);

  for (size_t i = 0; i < numberOfConfigs; ++i)
  {
    char* osDescr;
    vtkVerifyParseMacro(css->GetArgument(0, offset, &osDescr), "OSDescriptor");
    ++offset;
    this->Configs[i].OSDescriptor = osDescr;

    char* cpuDescr;
    vtkVerifyParseMacro(css->GetArgument(0, offset, &cpuDescr), "CPUDescriptor");
    ++offset;
    this->Configs[i].CPUDescriptor = cpuDescr;

    char* memDescr;
    vtkVerifyParseMacro(css->GetArgument(0, offset, &memDescr), "MemDescriptor");
    ++offset;
    this->Configs[i].MemDescriptor = memDescr;

    char* hostName;
    vtkVerifyParseMacro(css->GetArgument(0, offset, &hostName), "HostName");
    ++offset;
    this->Configs[i].HostName = hostName;

    vtkVerifyParseMacro(css->GetArgument(0, offset, &this->Configs[i].ProcessType), "ProcessType");
    ++offset;

    vtkVerifyParseMacro(css->GetArgument(0, offset, &this->Configs[i].SystemType), "SystemType");
    ++offset;

    vtkVerifyParseMacro(css->GetArgument(0, offset, &this->Configs[i].Rank), "Rank");
    ++offset;

    vtkVerifyParseMacro(css->GetArgument(0, offset, &this->Configs[i].Pid), "Pid");
    ++offset;

    vtkVerifyParseMacro(
      css->GetArgument(0, offset, &this->Configs[i].HostMemoryTotal), "HostMemoryTotal");
    ++offset;

    vtkVerifyParseMacro(
      css->GetArgument(0, offset, &this->Configs[i].HostMemoryAvailable), "HostMemoryAvailable");
    ++offset;

    vtkVerifyParseMacro(
      css->GetArgument(0, offset, &this->Configs[i].ProcMemoryAvailable), "ProcMemoryAvailable");
    ++offset;
  }
}

//----------------------------------------------------------------------------
void vtkPVSystemConfigInformation::Sort()
{
#ifdef vtkPVSystemConfigInformationDEBUG
  std::cerr << "=====vtkPVSystemConfigInformation::Sort" << endl;
#endif

  sort(this->Configs.begin(), this->Configs.end());
}

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