#include "edgefem/io/touchstone.hpp"

#include <cmath>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <stdexcept>

namespace edgefem {

namespace {

std::string format_string(TouchstoneFormat fmt) {
  switch (fmt) {
  case TouchstoneFormat::RI:
    return "RI";
  case TouchstoneFormat::MA:
    return "MA";
  case TouchstoneFormat::DB:
    return "DB";
  }
  return "RI";
}

void write_complex_value(std::ostream &os, std::complex<double> val,
                         TouchstoneFormat fmt) {
  switch (fmt) {
  case TouchstoneFormat::RI:
    os << val.real() << ' ' << val.imag();
    break;
  case TouchstoneFormat::MA: {
    double mag = std::abs(val);
    double ang = std::arg(val) * 180.0 / M_PI;
    os << mag << ' ' << ang;
    break;
  }
  case TouchstoneFormat::DB: {
    double mag_db = 20.0 * std::log10(std::max(std::abs(val), 1e-20));
    double ang = std::arg(val) * 180.0 / M_PI;
    os << mag_db << ' ' << ang;
    break;
  }
  }
}

} // namespace

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
void write_touchstone(const std::string &path, const std::vector<double> &freq,
                      const std::vector<SParams2> &data) {
  std::ofstream ofs(path);
  ofs << "# Hz S RI R 50\n";
  ofs << std::setprecision(12);
  for (size_t i = 0; i < freq.size(); ++i) {
    const auto &s = data[i];
    ofs << freq[i] << ' ' << s.s11.real() << ' ' << s.s11.imag() << ' '
        << s.s21.real() << ' ' << s.s21.imag() << ' ' << s.s12.real() << ' '
        << s.s12.imag() << ' ' << s.s22.real() << ' ' << s.s22.imag() << '\n';
  }
}
#pragma GCC diagnostic pop

void write_touchstone_nport(const std::string &path,
                            const std::vector<double> &freq,
                            const std::vector<Eigen::MatrixXcd> &S_matrices,
                            const TouchstoneOptions &opts) {
  if (freq.empty() || S_matrices.empty()) {
    throw std::runtime_error("Empty frequency or S-parameter data");
  }

  int num_ports = static_cast<int>(S_matrices[0].rows());
  if (num_ports < 1 || num_ports > 99) {
    throw std::runtime_error("Invalid number of ports: " +
                             std::to_string(num_ports));
  }

  for (size_t i = 0; i < S_matrices.size(); ++i) {
    if (S_matrices[i].rows() != num_ports ||
        S_matrices[i].cols() != num_ports) {
      throw std::runtime_error("S-matrix dimension mismatch at frequency " +
                               std::to_string(i));
    }
  }

  if (freq.size() != S_matrices.size()) {
    throw std::runtime_error("Frequency and S-matrix count mismatch");
  }

  std::ofstream ofs(path);
  if (!ofs) {
    throw std::runtime_error("Cannot open file for writing: " + path);
  }

  ofs << std::setprecision(12);

  // Write comment header
  ofs << "! Touchstone file generated by EdgeFEM\n";
  ofs << "! Number of ports: " << num_ports << "\n";

  // Write option line
  ofs << "# Hz S " << format_string(opts.format) << " R " << opts.z0 << "\n";

  // Write data
  for (size_t fi = 0; fi < freq.size(); ++fi) {
    const auto &S = S_matrices[fi];

    if (num_ports <= 2) {
      // 1-port and 2-port: all data on one line
      ofs << freq[fi];
      for (int i = 0; i < num_ports; ++i) {
        for (int j = 0; j < num_ports; ++j) {
          ofs << ' ';
          write_complex_value(ofs, S(i, j), opts.format);
        }
      }
      ofs << '\n';
    } else {
      // N-port (N>2): S-parameters organized by row
      // Each row may span multiple lines with continuation
      ofs << freq[fi];

      int values_per_line = 4; // Standard: 4 complex values per line
      int value_count = 0;

      for (int i = 0; i < num_ports; ++i) {
        for (int j = 0; j < num_ports; ++j) {
          if (value_count > 0 && value_count % values_per_line == 0) {
            ofs << '\n'; // Continuation line (no frequency)
          }
          ofs << ' ';
          write_complex_value(ofs, S(i, j), opts.format);
          value_count++;
        }
      }
      ofs << '\n';
    }
  }
}

std::string touchstone_extension(int num_ports) {
  if (num_ports < 1 || num_ports > 99) {
    throw std::runtime_error("Invalid number of ports: " +
                             std::to_string(num_ports));
  }
  std::ostringstream ss;
  ss << ".s" << num_ports << "p";
  return ss.str();
}

} // namespace edgefem
