/*
* Author:          Patrick-Christopher Mattulat
* Co-Author:       Claude Sonnet 4.6 (LLM)
* Company:         Lynar Studios
* E-Mail:          webmaster@lynarstudios.com
* Created:         2023-02-14
* Changed:         2026-06-23
*
* */

#include <ls-std/core/evaluator/NullPointerArgumentEvaluator.hpp>
#include <ls-std/io/section-pair/evaluator/SectionPairSectionArgumentEvaluator.hpp>
#include <ls-std/io/section-pair/model/SectionPairRow.hpp>
#include <ls-std/io/section-pair/model/SectionPairSection.hpp>
#include <ls-std/io/section-pair/serialization/SerializableSectionPairSection.hpp>

using ls::standard::core::Class;
using ls::standard::core::NullPointerArgumentEvaluator;
using ls::standard::core::type::byte_field;
using ls::standard::io::section_pair_row_list_element;
using ls::standard::io::SectionPairRow;
using ls::standard::io::SectionPairRowEnumType;
using ls::standard::io::SectionPairSection;
using ls::standard::io::SectionPairSectionArgumentEvaluator;
using ls::standard::io::SerializableSectionPairParameter;
using ls::standard::io::SerializableSectionPairSection;
using std::dynamic_pointer_cast;
using std::make_shared;
using std::shared_ptr;
using std::string;
using std::string_view;

SerializableSectionPairSection::SerializableSectionPairSection(const SerializableSectionPairParameter &_parameter) : Class("SerializableSectionPairSection"), parameter(_parameter)
{
  const string message = this->getClassName() + ": model reference is null!";
  NullPointerArgumentEvaluator(_parameter.getValue(), message).evaluate();
}

SerializableSectionPairSection::~SerializableSectionPairSection() noexcept = default;

shared_ptr<Class> SerializableSectionPairSection::getValue() const
{
  return this->parameter.getValue();
}

byte_field SerializableSectionPairSection::marshal()
{
  byte_field serializedSection{};

  serializedSection += this->_marshalSectionId();
  serializedSection += this->_marshalRows();

  return serializedSection;
}

void SerializableSectionPairSection::unmarshal(const byte_field &_data)
{
  SectionPairSectionArgumentEvaluator{_data}.evaluate();
  const size_t sectionHeaderSize = this->_unmarshalSectionHeader(_data);
  this->_unmarshalRows(_data.substr(sectionHeaderSize));
}

byte_field SerializableSectionPairSection::_collectSectionRow(const byte_field &_currentRows, SectionPairRowEnumType &_type) const
{
  string row{};
  const string newLine = this->parameter.getNewLine();
  const string firstRow = _currentRows.substr(0, _currentRows.find(newLine) + newLine.size());

  if (_isSingleValueRow(firstRow))
  {
    row = _collectSectionSingleValueRow(firstRow, _type);
  }

  if (_isListValueRow(firstRow))
  {
    row = this->_collectSectionListValueRow(_currentRows, _type);
  }

  return row;
}

byte_field SerializableSectionPairSection::_collectSectionListValueRow(const byte_field &_currentRows, SectionPairRowEnumType &_type) const
{
  byte_field currentRows = _currentRows;
  byte_field currentRow{};
  byte_field row{};
  const string newLine = this->parameter.getNewLine();
  _type = SectionPairRowEnumType::SECTION_PAIR_ROW_LIST_VALUE;
  size_t iterations{};
  bool isStillListRow{};

  do
  {
    if (currentRows.empty() && iterations > 1)
    {
      break;
    }

    ++iterations;
    currentRow = currentRows.substr(0, currentRows.find(newLine) + newLine.size());
    currentRows = currentRows.substr(currentRow.size());
    isStillListRow = !SerializableSectionPairSection::_isStartingValueRow(currentRow) || iterations == 1;

    if (isStillListRow)
    {
      row += currentRow;
    }
  } while (isStillListRow);

  return row;
}

byte_field SerializableSectionPairSection::_collectSectionSingleValueRow(const byte_field &_firstRow, SectionPairRowEnumType &_type)
{
  _type = SectionPairRowEnumType::SECTION_PAIR_ROW_SINGLE_VALUE;
  return _firstRow;
}

size_t SerializableSectionPairSection::_getNthSubStringPosition(const string_view _text, const string_view _subText)
{
  size_t position = string::npos;
  size_t amount{};

  for (size_t index = 0; index < (_text.size() - _subText.size()); index++)
  {
    if (_text.substr(index, _subText.size()) == _subText)
    {
      ++amount;
    }

    if (amount == 2)
    {
      position = index;
      break;
    }
  }

  return position;
}

byte_field SerializableSectionPairSection::_getSectionHeader(const byte_field &_data) const
{
  byte_field sectionHeader{};
  const string newLine = this->parameter.getNewLine();

  if (const size_t position = _getNthSubStringPosition(_data, newLine); position != string::npos)
  {
    sectionHeader = _data.substr(0, position + 2 * newLine.size());
  }

  return sectionHeader;
}

byte_field SerializableSectionPairSection::_getSectionId(const string_view _sectionHeader)
{
  auto sectionId = byte_field{_sectionHeader.substr(_sectionHeader.find('[') + 1)};
  return sectionId.substr(0, sectionId.find(']'));
}

bool SerializableSectionPairSection::_isListValueRow(const string_view _currentRow)
{
  return _currentRow.find(':') != string::npos;
}

bool SerializableSectionPairSection::_isStartingValueRow(const string &_currentRow)
{
  const bool isSingleValue = _isSingleValueRow(_currentRow);
  const bool isListValue = _isListValueRow(_currentRow);

  return isSingleValue || isListValue;
}

bool SerializableSectionPairSection::_isSingleValueRow(const string_view _currentRow)
{
  return _currentRow.find('=') != string::npos;
}

byte_field SerializableSectionPairSection::_marshalRows() const
{
  byte_field serializedSectionRows{};

  for (const auto &_row : dynamic_pointer_cast<SectionPairSection>(this->parameter.getValue())->getList())
  {
    _row->reserveNewLine(this->parameter.getNewLine());
    serializedSectionRows += _row->marshal();
  }

  return serializedSectionRows;
}

byte_field SerializableSectionPairSection::_marshalSectionId() const
{
  const string newLine = this->parameter.getNewLine();
  return newLine + "[" + dynamic_pointer_cast<SectionPairSection>(this->parameter.getValue())->getSectionId() + "]" + newLine + newLine;
}

void SerializableSectionPairSection::_unmarshalRow(const string &_sectionRow, SectionPairRowEnumType _type) const
{
  const auto row = make_shared<SectionPairRow>("tmp-dir", _type);
  row->reserveNewLine(this->parameter.getNewLine());
  row->unmarshal(_sectionRow);
  dynamic_pointer_cast<SectionPairSection>(this->parameter.getValue())->add(row);
}

void SerializableSectionPairSection::_unmarshalRows(const byte_field &_serializedRows) const
{
  string currentRows = _serializedRows;
  SectionPairRowEnumType type{};

  while (!currentRows.empty())
  {
    string sectionRow = this->_collectSectionRow(currentRows, type);
    this->_unmarshalRow(sectionRow, type);
    currentRows = currentRows.substr(sectionRow.size());
  }
}

size_t SerializableSectionPairSection::_unmarshalSectionHeader(const byte_field &_data) const
{
  const byte_field sectionHeader = this->_getSectionHeader(_data);
  dynamic_pointer_cast<SectionPairSection>(this->parameter.getValue())->setSectionId(SerializableSectionPairSection::_getSectionId(sectionHeader));

  return sectionHeader.size();
}
