///|
/// Parameters for deterministic synthetic CVRP/VRPTW instances.
pub struct GeneratorConfig {
  name : String
  customer_count : Int
  coordinate_limit : Double
  vehicle_capacity : Double
  fleet_size : Int
  max_demand : Int
  horizon : Double
  service_time : Double
  seed : Int
} derive(Debug, ToJson)

///|
pub fn GeneratorConfig::default() -> GeneratorConfig {
  {
    name: "generated-cvrp",
    customer_count: 24,
    coordinate_limit: 100.0,
    vehicle_capacity: 40.0,
    fleet_size: 6,
    max_demand: 8,
    horizon: 500.0,
    service_time: 5.0,
    seed: 17,
  }
}

///|
pub fn GeneratorConfig::tune(
  self : GeneratorConfig,
  name? : String = self.name,
  customer_count? : Int = self.customer_count,
  coordinate_limit? : Double = self.coordinate_limit,
  vehicle_capacity? : Double = self.vehicle_capacity,
  fleet_size? : Int = self.fleet_size,
  max_demand? : Int = self.max_demand,
  horizon? : Double = self.horizon,
  service_time? : Double = self.service_time,
  seed? : Int = self.seed,
) -> GeneratorConfig {
  {
    name,
    customer_count,
    coordinate_limit,
    vehicle_capacity,
    fleet_size,
    max_demand,
    horizon,
    service_time,
    seed,
  }
}

///|
pub fn GeneratorConfig::name(self : GeneratorConfig) -> String {
  self.name
}

///|
pub fn GeneratorConfig::customer_count(self : GeneratorConfig) -> Int {
  self.customer_count
}

///|
pub fn GeneratorConfig::coordinate_limit(self : GeneratorConfig) -> Double {
  self.coordinate_limit
}

///|
pub fn GeneratorConfig::vehicle_capacity(self : GeneratorConfig) -> Double {
  self.vehicle_capacity
}

///|
pub fn GeneratorConfig::fleet_size(self : GeneratorConfig) -> Int {
  self.fleet_size
}

///|
pub fn GeneratorConfig::max_demand(self : GeneratorConfig) -> Int {
  self.max_demand
}

///|
pub fn GeneratorConfig::horizon(self : GeneratorConfig) -> Double {
  self.horizon
}

///|
pub fn GeneratorConfig::service_time(self : GeneratorConfig) -> Double {
  self.service_time
}

///|
pub fn GeneratorConfig::seed(self : GeneratorConfig) -> Int {
  self.seed
}

///|
fn safe_count(value : Int) -> Int {
  value.max(0).min(5_000)
}

///|
fn safe_limit(value : Double) -> Double {
  value.max(1.0)
}

///|
fn generated_depot(config : GeneratorConfig) -> @core.Depot {
  let center = safe_limit(config.coordinate_limit) / 2.0
  @core.Depot::new(center, center, due_time=config.horizon.max(1.0))
}

///|
fn generated_vehicle(config : GeneratorConfig) -> @core.VehicleProfile {
  @core.VehicleProfile::new(
    config.vehicle_capacity.max(0.1),
    fleet_size=config.fleet_size.max(1),
    max_route_duration=config.horizon.max(1.0),
  )
}

///|
fn generated_customer(
  rng : @rng.Rng,
  config : GeneratorConfig,
  index : Int,
) -> @core.Customer {
  let limit = safe_limit(config.coordinate_limit)
  let demand_limit = config.max_demand.max(1)
  let x = rng.next_double() * limit
  let y = rng.next_double() * limit
  let demand = Double::from_int(1 + rng.next_int(demand_limit))
  @core.Customer::new(
    index + 1,
    x,
    y,
    demand,
    service_time=config.service_time.max(0.0),
  )
}

///|
/// Creates a deterministic CVRP instance for load testing and tutorials.
pub fn generate_cvrp(config : GeneratorConfig) -> @core.VrpInstance {
  let rng = @rng.Rng::new(config.seed)
  let customers : Array[@core.Customer] = []
  for index in 0.. (Double, Double) {
  let horizon = config.horizon.max(10.0)
  let ready = rng.next_double() * horizon * 0.65
  let width = horizon * 0.25 + rng.next_double() * horizon * 0.15
  (ready, Double::min(horizon, ready + width))
}

///|
/// Creates a deterministic Solomon-style VRPTW instance.
pub fn generate_vrptw(config : GeneratorConfig) -> @core.VrpInstance {
  let rng = @rng.Rng::new(config.seed)
  let customers : Array[@core.Customer] = []
  for index in 0.. String {
  let (minimum, maximum, average) = instance.distance_summary()
  [
    instance.instance_name(),
    instance.variant().name(),
    "customers=\{instance.customer_count()}",
    "fleet=\{instance.vehicle().fleet_size()}",
    "capacity=\{instance.vehicle().capacity()}",
    "min=\{minimum}",
    "max=\{maximum}",
    "avg=\{average}",
  ].join("|")
}