// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkLSDynaSummaryParser.h"
#include "LSDynaMetaData.h"
#include "vtkObjectFactory.h"
#include "vtkStringScanner.h"

#include <string>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkLSDynaSummaryParser);

namespace
{
void vtkLSTrimWhitespace(std::string& line)
{
  std::string::size_type llen = line.length();
  while (llen &&
    (line[llen - 1] == ' ' || line[llen - 1] == '\t' || line[llen - 1] == '\r' ||
      line[llen - 1] == '\n'))
  {
    --llen;
  }

  std::string::size_type nameStart = 0;
  while (nameStart < llen && (line[nameStart] == ' ' || line[nameStart] == '\t'))
  {
    ++nameStart;
  }

  line = line.substr(nameStart, llen - nameStart);
}

}

//------------------------------------------------------------------------------
vtkLSDynaSummaryParser::vtkLSDynaSummaryParser()
  : MetaData(nullptr)
  , PartId(-1)
  , PartStatus(0)
  , PartMaterial(0)
  , InPart(0)
  , InDyna(0)
  , InName(0)
{
}

//------------------------------------------------------------------------------
void vtkLSDynaSummaryParser::StartElement(const char* name, const char** atts)
{
  int i;
  if (!strcmp(name, "part"))
  {
    if (!this->InDyna || this->InPart)
    { // can't have longer parts or parts that contain parts
      this->ReportUnknownElement(name);
    }
    else
    {
      this->InPart = 1;
      this->PartName = "";

      this->PartId = -1;
      this->PartStatus = 1;
      this->PartMaterial = -1;
      for (i = 0; atts[i] != nullptr; i += 2)
      {
        if (!strcmp(atts[i], "id"))
        {
          if (vtk::from_chars(atts[i + 1], this->PartId).ec != std::errc{})
          {
            this->PartId = -1;
            this->ReportBadAttribute(name, atts[i], atts[i + 1]);
          }
        }
        else if (!strcmp(atts[i], "material"))
        {
          if (vtk::from_chars(atts[i + 1], this->PartMaterial).ec != std::errc{})
          {
            this->PartMaterial = -1;
            this->ReportBadAttribute(name, atts[i], atts[i + 1]);
          }
        }
        else if (!strcmp(atts[i], "status"))
        {
          if (vtk::from_chars(atts[i + 1], this->PartStatus).ec != std::errc{})
          {
            this->PartStatus = 1;
            this->ReportBadAttribute(name, atts[i], atts[i + 1]);
          }
        }
      }
      if (this->PartId < 0)
      {
        this->ReportMissingAttribute(name, "id");
      }
    }
  }
  else if (!strcmp(name, "name"))
  {
    if (!this->InDyna || !this->InPart)
    { // name must be inside a part
      this->ReportUnknownElement(name);
    }
    else
    {
      this->InName = 1;
      this->PartName = "";
    }
  }
  else if (!strcmp(name, "database"))
  { // database must be inside the lsdyna tag, but not inside a part or name
    if (!this->InDyna || this->InPart || this->InName)
    {
      this->ReportUnknownElement(name);
    }
    else
    {
      const char* dbpath = nullptr;
      const char* dbname = nullptr;
      for (i = 0; atts[i] != nullptr; i += 2)
      {
        if (!strcmp(atts[i], "path"))
        {
          dbpath = atts[i + 1];
        }
        else if (!strcmp(atts[i], "name"))
        {
          dbname = atts[i + 1];
        }
      }
      if (dbpath && dbname)
      {
        this->MetaData->Fam.SetDatabaseDirectory(dbpath);
        this->MetaData->Fam.SetDatabaseBaseName(dbname);
      }
      else
      {
        this->ReportXmlParseError();
      }
    }
  }
  else if (!strcmp(name, "lsdyna"))
  {
    if (this->InPart || this->InName || this->InDyna)
    { // dyna must be outermost tag
      this->ReportUnknownElement(name);
    }
    else
    {
      this->InDyna = 1;
    }
  }
}

//------------------------------------------------------------------------------
void vtkLSDynaSummaryParser::EndElement(const char* name)
{
  if (!strcmp(name, "part"))
  {
    this->InPart = this->InName = 0;
    if (this->PartName.empty() || this->PartId <= 0 ||
      this->PartId > (int)this->MetaData->PartNames.size())
    { // missing a name or an id
      this->ReportXmlParseError();
    }
    else
    {
      vtkLSTrimWhitespace(this->PartName);
      this->MetaData->PartNames[this->PartId - 1] = this->PartName;
      this->MetaData->PartIds[this->PartId - 1] = this->PartId;
      this->MetaData->PartMaterials[this->PartId - 1] = this->PartMaterial;
      this->MetaData->PartStatus[this->PartId - 1] = this->PartStatus;
    }
  }
  else if (!strcmp(name, "name"))
  {
    this->InName = 0;
  }
  else if (!strcmp(name, "lsdyna"))
  {
    this->InDyna = this->InPart = this->InName = 0;
  }
}
//------------------------------------------------------------------------------
void vtkLSDynaSummaryParser::CharacterDataHandler(const char* data, int length)
{
  if (!this->InName)
  {
    return;
  }
  // skip leading whitespace
  int i = 0;
  while (this->PartName.empty() && i < length && vtkLSDynaSummaryParser::IsSpace(data[i]))
    ++i;

  if (i < length)
    this->PartName.append(data + i, length - i);
}

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

  os << indent << "PartName: \"" << this->PartName << "\"" << endl;
  os << indent << "PartId: " << this->PartId << endl;
  os << indent << "PartStatus: " << this->PartStatus << endl;
  os << indent << "PartMaterial: " << this->PartMaterial << endl;
  os << indent << "InPart: " << this->InPart << endl;
  os << indent << "InDyna: " << this->InDyna << endl;
  os << indent << "InName: " << this->InName << endl;
}
VTK_ABI_NAMESPACE_END
