///|
pub enum OperationMode {
  Nominal
  Safe
  Contingency
  Simulation
} derive(Debug, Eq)

///|
pub fn all_operation_modes() -> Array[OperationMode] {
  [Nominal, Safe, Contingency, Simulation]
}

///|
pub struct OperationStep {
  index : Int
  name : String
  mode : OperationMode
  duration_s : Double
  resource_cost : Double
  completed : Bool
} derive(Debug, Eq)

///|
pub struct OperationPlan {
  name : String
  steps : Array[OperationStep]
  total_duration_s : Double
  total_resource_cost : Double
} derive(Debug, Eq)

///|
pub fn OperationStep::new(
  index : Int,
  name : String,
  mode : OperationMode,
  duration_s : Double,
  resource_cost : Double,
) -> OperationStep {
  {
    index,
    name,
    mode,
    duration_s: duration_s.max(0.0),
    resource_cost: resource_cost.max(0.0),
    completed: false,
  }
}

///|
pub fn OperationPlan::new(name : String) -> OperationPlan {
  { name, steps: [], total_duration_s: 0.0, total_resource_cost: 0.0 }
}

///|
pub fn OperationPlan::append(
  plan : OperationPlan,
  step : OperationStep,
) -> OperationPlan {
  let steps = plan.steps.copy()
  steps.push(step)
  {
    ..plan,
    steps,
    total_duration_s: plan.total_duration_s + step.duration_s,
    total_resource_cost: plan.total_resource_cost + step.resource_cost,
  }
}

///|
pub fn OperationPlan::complete_step(
  plan : OperationPlan,
  index : Int,
) -> OperationPlan {
  let steps = plan.steps.copy()
  for i in 0.. Double {
  if plan.steps.length() == 0 {
    0.0
  } else {
    Double::from_int(plan.steps.filter(step => step.completed).length()) /
    Double::from_int(plan.steps.length())
  }
}

///|
pub fn OperationPlan::remaining_steps(
  plan : OperationPlan,
) -> Array[OperationStep] {
  plan.steps.filter(step => !step.completed)
}

///|
pub fn OperationPlan::is_ready(plan : OperationPlan) -> Bool {
  (plan.steps.length() > 0 && plan.steps[0].completed) ||
  plan.steps.length() == 0
}

///|
pub fn OperationPlan::resource_remaining(
  plan : OperationPlan,
  resource_limit : Double,
) -> Double {
  resource_limit - plan.total_resource_cost
}

///|
pub fn nominal_step(
  index : Int,
  name : String,
  duration_s : Double,
  resource_cost : Double,
) -> OperationStep {
  OperationStep::new(index, name, Nominal, duration_s, resource_cost)
}

///|
pub fn contingency_step(
  index : Int,
  name : String,
  duration_s : Double,
  resource_cost : Double,
) -> OperationStep {
  OperationStep::new(index, name, Contingency, duration_s, resource_cost)
}

///|
pub fn simulate_plan(
  plan : OperationPlan,
  available_resource : Double,
) -> OperationPlan {
  let mut result = plan
  for step in result.steps {
    if result.resource_remaining(available_resource) >= step.resource_cost {
      result = result.complete_step(step.index)
    }
  }
  result
}