/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

#include <string>
#include <vector>

namespace sdi_toolBox::console
{
//--------------------------------------------------------------
class ConsoleTable
{
  public:
  using Row          = std::vector<std::string>;
  using ColumnWidths = std::vector<size_t>;

  public:
  ConsoleTable()                                         = default;     // Constructor
  virtual ~ConsoleTable()                                = default;     // Destructor
  ConsoleTable(const ConsoleTable &other)                = delete;      // Copy constructor
  ConsoleTable(ConsoleTable &&other) noexcept            = delete;      // Move constructor
  ConsoleTable &operator=(const ConsoleTable &other)     = delete;      // Copy assignment
  ConsoleTable &operator=(ConsoleTable &&other) noexcept = delete;      // Move assignment

  void setHeaders(const Row &headers);     // Set table headers
  void addRow(const Row &row);             // Add a row to the table

  [[nodiscard]] std::string render() const;     // Render the table as a string

  protected:
  Row              m_headers;         // Table headers
  std::vector<Row> m_rows;            // Table rows
  size_t           m_padding = 2;     // Padding between columns

  private:
  [[nodiscard]] ColumnWidths       calculateColumnWidths() const;                       // Calculate column widths
  [[nodiscard]] static std::string drawSeparator(const ColumnWidths &columnWidths);     // Draw a separator line (+---+---+---+)
  [[nodiscard]] static std::string drawRow(const Row          &row,                     // Draw a single row
                                           const ColumnWidths &columnWidths);
};
//--------------------------------------------------------------
/* Set table headers */
inline void ConsoleTable::setHeaders(const Row &headers)
{
  m_headers = headers;
}
//--------------------------------------------------------------
/* Add a row to the table */
inline void ConsoleTable::addRow(const Row &row)
{
  m_rows.push_back(row);
}
//--------------------------------------------------------------
/* Render the table as a string */
inline std::string ConsoleTable::render() const
{
  // Calculate column widths
  const auto columnWidths = calculateColumnWidths();

  // Initialize the output string
  std::string output;

  // Draw the top separator
  output += drawSeparator(columnWidths);

  // Draw the headers
  output += drawRow(m_headers, columnWidths);

  // Draw the separator between headers and data
  output += drawSeparator(columnWidths);

  // Draw the data rows
  for (const auto &row : m_rows)
  {
    output += drawRow(row, columnWidths);
  }

  // Draw the bottom separator
  output += drawSeparator(columnWidths);

  return output;
}
//--------------------------------------------------------------
/* Calculate column widths */
inline ConsoleTable::ColumnWidths ConsoleTable::calculateColumnWidths() const
{
  // Determine the number of columns (based on headers or first row)
  const auto numColumns = m_headers.empty() ? (m_rows.empty() ? 0 : m_rows[0].size()) : m_headers.size();

  // Initialize column widths
  ColumnWidths widths(numColumns, 0);

  // Calculate widths based on headers
  for (size_t i = 0; i < m_headers.size() && i < numColumns; ++i)
  {
    widths[i] = std::max(widths[i], m_headers[i].length() + m_padding);
  }

  // Calculate widths based on rows
  for (const auto &row : m_rows)
  {
    for (size_t i = 0; i < row.size() && i < numColumns; ++i)
    {
      widths[i] = std::max(widths[i], row[i].length() + m_padding);
    }
  }

  return widths;
}
//--------------------------------------------------------------
/* Draw a separator line (+---+---+---+) */
inline std::string ConsoleTable::drawSeparator(const ColumnWidths &columnWidths)
{
  std::string output;
  output += "+";
  for (const auto &width : columnWidths)
  {
    output += std::string(width, '-') + "+";
  }
  output += "\n";
  return output;
}
//--------------------------------------------------------------
/* Draw a single row */
inline std::string ConsoleTable::drawRow(const Row &row, const ColumnWidths &columnWidths)
{
  std::string output;
  output += "|";
  for (size_t i = 0; i < columnWidths.size(); ++i)
  {
    const auto cellContent = (i < row.size()) ? row[i] : "";
    output += " " + cellContent;
    // Add padding spaces
    const size_t paddingSpaces = columnWidths[i] - cellContent.length() - 1;
    output += std::string(paddingSpaces, ' ');
    output += "|";
  }
  output += "\n";
  return output;
}
//--------------------------------------------------------------
}     // namespace sdi_toolBox::console
