///|
struct AxisLayout {
  y_min : Float
  y_max : Float
  ticks : Array[Float]
  tick_labels : Array[String]
}

///|
fn nice_step(range : Float, max_ticks : Int) -> Float {
  let rough_step = range / Float::from_int(max_ticks)
  let rough = rough_step
  if rough >= 1.0 {
    if rough <= 2.0 {
      1.0
    } else if rough <= 5.0 {
      2.0
    } else if rough <= 10.0 {
      5.0
    } else if rough <= 20.0 {
      10.0
    } else if rough <= 50.0 {
      20.0
    } else if rough <= 100.0 {
      50.0
    } else if rough <= 200.0 {
      100.0
    } else if rough <= 500.0 {
      200.0
    } else if rough <= 1000.0 {
      500.0
    } else {
      1000.0
    }
  } else if rough >= 0.5 {
    0.5
  } else if rough >= 0.2 {
    0.2
  } else if rough >= 0.1 {
    0.1
  } else {
    0.05
  }
}

///|
fn floor_float(x : Float) -> Float {
  let int_part = x.to_int()
  let as_float = Float::from_int(int_part)
  if as_float > x {
    Float::from_int(int_part - 1)
  } else {
    as_float
  }
}

///|
fn ceil_float(x : Float) -> Float {
  let int_part = x.to_int()
  let as_float = Float::from_int(int_part)
  if as_float < x {
    Float::from_int(int_part + 1)
  } else {
    as_float
  }
}

///|
pub fn compute_y_axis(
  data_min : Float,
  data_max : Float,
  _height : Float,
  _margin_top : Float,
  _margin_bottom : Float,
) -> AxisLayout {
  let max_ticks = 8
  let range = data_max - data_min
  if range == 0.0 {
    let step : Float = 1.0
    let y_min : Float = data_min - step
    let y_max : Float = data_max + step
    AxisLayout::{ y_min, y_max, ticks: [], tick_labels: [] }
  } else {
    let step = nice_step(range, max_ticks)
    let y_min = floor_float(data_min / step) * step
    let y_max = (ceil_float(data_max / step) + 1.0) * step
    let ticks = build_ticks(y_min, y_max, step)
    let tick_labels = format_labels(ticks)
    AxisLayout::{ y_min, y_max, ticks, tick_labels }
  }
}

///|
fn build_ticks(current : Float, max : Float, step : Float) -> Array[Float] {
  if current > max + step * 0.5 {
    []
  } else {
    let rest = build_ticks(current + step, max, step)
    let result : Array[Float] = [current]
    for i = 0; i < rest.length(); i = i + 1 {
      result.push(rest[i])
    }
    result
  }
}

///|
fn format_labels(ticks : Array[Float]) -> Array[String] {
  let labels : Array[String] = []
  for i = 0; i < ticks.length(); i = i + 1 {
    labels.push(ticks[i].to_string())
  }
  labels
}

///|
pub fn y_to_svg(
  value : Float,
  y_min : Float,
  y_max : Float,
  height : Float,
  margin_top : Float,
  margin_bottom : Float,
) -> Float {
  let chart_h = height - margin_top - margin_bottom
  margin_top + chart_h - (value - y_min) / (y_max - y_min) * chart_h
}

///|
pub fn x_to_svg(
  index : Float,
  count : Float,
  width : Float,
  margin_left : Float,
  margin_right : Float,
) -> Float {
  let chart_w = width - margin_left - margin_right
  margin_left + index / count * chart_w
}