///|
pub fn quantity_text(quantity : Quantity) -> String {
  quantity.amount.to_string() + " " + unit_text(quantity.unit)
}

///|
pub fn canonical_amount(quantity : Quantity) -> Int {
  quantity.amount * canonical_factor(quantity.unit)
}

///|
pub fn same_measure_kind(left : Quantity, right : Quantity) -> Bool {
  unit_kind(left.unit) == unit_kind(right.unit)
}

///|
pub fn convert_quantity(quantity : Quantity, unit : MeasureUnit) -> Quantity {
  if unit_kind(quantity.unit) != unit_kind(unit) {
    return Quantity(amount=quantity.amount, unit=quantity.unit)
  }
  let base = canonical_amount(quantity)
  Quantity(amount=base / canonical_factor(unit), unit~)
}

///|
pub fn add_quantity(left : Quantity, right : Quantity) -> Quantity {
  if !same_measure_kind(left, right) {
    return left
  }
  let base = canonical_amount(left) + canonical_amount(right)
  Quantity(amount=base / canonical_factor(left.unit), unit=left.unit)
}

///|
pub fn subtract_quantity(left : Quantity, right : Quantity) -> Quantity {
  if !same_measure_kind(left, right) {
    return left
  }
  let base = canonical_amount(left) - canonical_amount(right)
  let safe = if base < 0 { 0 } else { base }
  Quantity(amount=safe / canonical_factor(left.unit), unit=left.unit)
}

///|
pub fn quantity_missing(needed : Quantity, available : Quantity) -> Quantity {
  if !same_measure_kind(needed, available) {
    return needed
  }
  let missing = canonical_amount(needed) - canonical_amount(available)
  if missing <= 0 {
    Quantity(amount=0, unit=needed.unit)
  } else {
    Quantity(amount=missing / canonical_factor(needed.unit), unit=needed.unit)
  }
}

///|
pub fn has_enough(available : Quantity, needed : Quantity) -> Bool {
  same_measure_kind(available, needed) &&
  canonical_amount(available) >= canonical_amount(needed)
}

///|
pub fn scale_quantity(
  quantity : Quantity,
  from_servings : Int,
  to_servings : Int,
) -> Quantity {
  if from_servings <= 0 {
    return quantity
  }
  Quantity(
    amount=quantity.amount * to_servings / from_servings,
    unit=quantity.unit,
  )
}

///|
pub fn scale_nutrition(
  nutrition : Nutrition,
  from_servings : Int,
  to_servings : Int,
) -> Nutrition {
  if from_servings <= 0 {
    return nutrition
  }
  Nutrition(
    kcal=nutrition.kcal * to_servings / from_servings,
    protein_g=nutrition.protein_g * to_servings / from_servings,
    fat_g=nutrition.fat_g * to_servings / from_servings,
    carb_g=nutrition.carb_g * to_servings / from_servings,
    fiber_g=nutrition.fiber_g * to_servings / from_servings,
    sodium_mg=nutrition.sodium_mg * to_servings / from_servings,
  )
}

///|
pub fn scale_ingredient(
  ingredient : Ingredient,
  from_servings : Int,
  to_servings : Int,
) -> Ingredient {
  let divisor = if from_servings <= 0 { 1 } else { from_servings }
  Ingredient(
    name=ingredient.name,
    group=ingredient.group,
    quantity=scale_quantity(ingredient.quantity, from_servings, to_servings),
    nutrition=scale_nutrition(ingredient.nutrition, from_servings, to_servings),
    cost_cents=ingredient.cost_cents * to_servings / divisor,
    allergens=ingredient.allergens,
    tags=ingredient.tags,
  )
}

///|
pub fn scale_recipe(recipe : Recipe, servings : Int) -> Recipe {
  let scaled = Array::new(capacity=recipe.ingredients.length())
  for ingredient in recipe.ingredients {
    scaled.push(scale_ingredient(ingredient, recipe.servings, servings))
  }
  Recipe(
    title=recipe.title,
    servings~,
    ingredients=scaled,
    steps=recipe.steps,
    notes=recipe.notes,
  )
}

///|
pub fn nutrition_add(left : Nutrition, right : Nutrition) -> Nutrition {
  Nutrition(
    kcal=left.kcal + right.kcal,
    protein_g=left.protein_g + right.protein_g,
    fat_g=left.fat_g + right.fat_g,
    carb_g=left.carb_g + right.carb_g,
    fiber_g=left.fiber_g + right.fiber_g,
    sodium_mg=left.sodium_mg + right.sodium_mg,
  )
}

///|
pub fn total_nutrition(recipe : Recipe) -> Nutrition {
  let mut total = Nutrition()
  for ingredient in recipe.ingredients {
    total = nutrition_add(total, ingredient.nutrition)
  }
  total
}

///|
pub fn total_cost(recipe : Recipe) -> Int {
  let mut total = 0
  for ingredient in recipe.ingredients {
    total += ingredient.cost_cents
  }
  total
}