// Data models and configuration structs for simulation parameters.

///|
/// Optical pulse initial shape enumeration
pub enum PulseShape {
  Gaussian
  Sech
  SuperGaussian(power~ : Int)
  ChirpedGaussian(chirp~ : Double)
  ChirpedSech(chirp~ : Double)
  DualPulse(delay~ : Double, ratio~ : Double)
  CustomWaveform
} derive(Debug, Eq)

///|
/// PulseShape constructor functions
pub fn PulseShape::gaussian() -> PulseShape {
  Gaussian
}

///|
pub fn PulseShape::sech() -> PulseShape {
  Sech
}

///|
pub fn PulseShape::super_gaussian(power : Int) -> PulseShape {
  SuperGaussian(power~)
}

///|
pub fn PulseShape::chirped_gaussian(chirp : Double) -> PulseShape {
  ChirpedGaussian(chirp~)
}

///|
pub fn PulseShape::chirped_sech(chirp : Double) -> PulseShape {
  ChirpedSech(chirp~)
}

///|
pub fn PulseShape::dual_pulse(delay : Double, ratio : Double) -> PulseShape {
  DualPulse(delay~, ratio~)
}

///|
pub fn PulseShape::custom_waveform() -> PulseShape {
  CustomWaveform
}

///|
/// Show implementation for PulseShape
pub impl Show for PulseShape with fn output(self, logger) {
  match self {
    Gaussian => logger.write_string("Gaussian")
    Sech => logger.write_string("Sech")
    SuperGaussian(power=p) =>
      logger.write_string("SuperGaussian(power=" + p.to_string() + ")")
    ChirpedGaussian(chirp=c) =>
      logger.write_string("ChirpedGaussian(chirp=" + c.to_string() + ")")
    ChirpedSech(chirp=c) =>
      logger.write_string("ChirpedSech(chirp=" + c.to_string() + ")")
    DualPulse(delay=d, ratio=r) =>
      logger.write_string(
        "DualPulse(delay=" + d.to_string() + ", ratio=" + r.to_string() + ")",
      )
    CustomWaveform => logger.write_string("CustomWaveform")
  }
}

///|
/// Pulse specifications
pub struct PulseConfig {
  shape : PulseShape
  peak_power : Double // W
  fwhm_duration : Double // s
  central_wavelength : Double // m
  chirp : Double
  order : Double // Soliton order N
  phase_offset : Double // rad
  time_window : Double // s (full temporal window width)
  num_points : Int // N_t grid points (power of 2)
} derive(Debug, Eq)

///|
/// Constructor for PulseConfig
pub fn PulseConfig::new(
  shape? : PulseShape = Sech,
  peak_power? : Double = 100.0,
  fwhm_duration? : Double = 1.0e-12,
  central_wavelength? : Double = WAVELENGTH_1550NM,
  chirp? : Double = 0.0,
  order? : Double = 1.0,
  phase_offset? : Double = 0.0,
  time_window? : Double = 100.0e-12,
  num_points? : Int = 2048,
) -> PulseConfig {
  {
    shape,
    peak_power,
    fwhm_duration,
    central_wavelength,
    chirp,
    order,
    phase_offset,
    time_window,
    num_points,
  }
}

///|
/// Default pulse parameters
pub fn PulseConfig::default() -> PulseConfig {
  PulseConfig::new()
}

///|
/// Fiber dispersion profile up to 10th order
pub struct DispersionProfile {
  alpha_db_per_km : Double // Attenuation in dB/km
  beta2 : Double // s^2/m (GVD)
  beta3 : Double // s^3/m (TOD)
  beta4 : Double // s^4/m (FOD)
  beta5 : Double // s^5/m
  beta6 : Double // s^6/m
  beta7 : Double // s^7/m
  beta8 : Double // s^8/m
  beta9 : Double // s^9/m
  beta10 : Double // s^10/m
} derive(Debug, Eq)

///|
/// Constructor for DispersionProfile
pub fn DispersionProfile::new(
  alpha_db_per_km? : Double = 0.2,
  beta2? : Double = -2.17e-26,
  beta3? : Double = 1.0e-40,
  beta4? : Double = 0.0,
  beta5? : Double = 0.0,
  beta6? : Double = 0.0,
  beta7? : Double = 0.0,
  beta8? : Double = 0.0,
  beta9? : Double = 0.0,
  beta10? : Double = 0.0,
) -> DispersionProfile {
  {
    alpha_db_per_km,
    beta2,
    beta3,
    beta4,
    beta5,
    beta6,
    beta7,
    beta8,
    beta9,
    beta10,
  }
}

///|
/// Default dispersion profile (SMF-28 at 1550 nm)
pub fn DispersionProfile::smf28() -> DispersionProfile {
  DispersionProfile::new()
}

///|
/// Fiber nonlinear parameters and Raman model choices
pub struct NonlinearProfile {
  gamma : Double // 1/(W*m)
  self_steepening : Bool // Include s = 1/omega0 * d/dt
  tau_s : Double // Self-steepening timescale (s)
  enable_raman : Bool
  fr : Double // Raman fractional response (typically 0.18 for silica)
  tau1 : Double // Raman parameter tau1 (fs)
  tau2 : Double // Raman parameter tau2 (fs)
} derive(Debug, Eq)

///|
/// Constructor for NonlinearProfile
pub fn NonlinearProfile::new(
  gamma? : Double = 0.0013,
  self_steepening? : Bool = true,
  tau_s? : Double = 0.0,
  enable_raman? : Bool = true,
  fr? : Double = 0.18,
  tau1? : Double = 12.2e-15,
  tau2? : Double = 32.0e-15,
) -> NonlinearProfile {
  { gamma, self_steepening, tau_s, enable_raman, fr, tau1, tau2 }
}

///|
/// Default silica fiber nonlinear properties
pub fn NonlinearProfile::default_silica() -> NonlinearProfile {
  NonlinearProfile::new()
}

///|
/// Fiber segment configuration
pub struct FiberSegment {
  name : String
  length : Double // m
  dispersion : DispersionProfile
  nonlinearity : NonlinearProfile
  gain_db_per_m : Double // Small-signal gain (dB/m) for active fiber
  saturation_energy : Double // Gain saturation energy Esat (J)
} derive(Debug, Eq)

///|
/// Create standard fiber segment
pub fn FiberSegment::new(
  name : String,
  length : Double,
  dispersion : DispersionProfile,
  nonlinearity : NonlinearProfile,
  gain_db_per_m? : Double = 0.0,
  saturation_energy? : Double = 1.0e-9,
) -> FiberSegment {
  { name, length, dispersion, nonlinearity, gain_db_per_m, saturation_energy }
}

///|
/// SSFM algorithm solver options
pub enum SolverMethod {
  StandardSSFM
  SymmetricSSFM
  RK4IP
} derive(Debug, Eq)

///|
/// SolverMethod constructors
pub fn SolverMethod::standard_ssfm() -> SolverMethod {
  StandardSSFM
}

///|
pub fn SolverMethod::symmetric_ssfm() -> SolverMethod {
  SymmetricSSFM
}

///|
pub fn SolverMethod::rk4ip() -> SolverMethod {
  RK4IP
}

///|
/// Show implementation for SolverMethod
pub impl Show for SolverMethod with fn output(self, logger) {
  match self {
    StandardSSFM => logger.write_string("StandardSSFM")
    SymmetricSSFM => logger.write_string("SymmetricSSFM")
    RK4IP => logger.write_string("RK4IP")
  }
}

///|
/// Simulation numerical settings
pub struct SimulationConfig {
  solver_method : SolverMethod
  total_length : Double // m
  num_steps : Int // Fixed step count
  adaptive_step : Bool
  tolerance : Double // Local truncation error tolerance
  max_step_size : Double // m
  min_step_size : Double // m
  save_history_steps : Int // Save field slice every N steps
} derive(Debug, Eq)

///|
/// Constructor for SimulationConfig
pub fn SimulationConfig::new(
  solver_method? : SolverMethod = RK4IP,
  total_length? : Double = 100.0,
  num_steps? : Int = 1000,
  adaptive_step? : Bool = true,
  tolerance? : Double = 1.0e-6,
  max_step_size? : Double = 1.0,
  min_step_size? : Double = 1.0e-6,
  save_history_steps? : Int = 10,
) -> SimulationConfig {
  {
    solver_method,
    total_length,
    num_steps,
    adaptive_step,
    tolerance,
    max_step_size,
    min_step_size,
    save_history_steps,
  }
}

///|
/// Default simulation options
pub fn SimulationConfig::default() -> SimulationConfig {
  SimulationConfig::new()
}