use "board"
use "std/grid"
use "std/random"
use "std/vectors"

def source? [cells: Vector size: Int group: Vector index: Int] -> Bool
  if (= (get cells index) "")
    false
    not
      empty?
        filter
          fn [neighbor]
            vectors.contains? group neighbor
          grid.neighbors size size index

def sources [cells: Vector size: Int group: Vector] -> Vector
  map
    fn [index]
      dict
        :index index
        :color (get cells index)
    filter
      fn [index]
        if (vectors.contains? group index)
          false
          source? cells size group index
      range 0 (* size size)

def distance [size: Int group: Vector cell: Int source: Dict] -> Result
  grid.distance size size (append group source.index) cell source.index

def source-distances [size: Int group: Vector cell: Int boundary: Vector] -> Vector
  reduce
    fn [pairs source]
      let measured
        distance size group cell source
      match measured
        Ok value
          append pairs
            dict
              :source source
              :distance value
        Error problem
          pairs
    []
    boundary

def minimum-distance [pairs: Vector] -> Result
  vectors.minimum
    map
      fn [pair]
        pair.distance
      pairs

def colors-at [pairs: Vector distance: Int] -> Vector
  vectors.unique
    map
      fn [pair]
        pair.source.color
      filter
        fn [pair]
          = pair.distance distance
        pairs

def tied-colors [size: Int group: Vector cell: Int boundary: Vector] -> Vector
  let pairs
    source-distances size group cell boundary
  let nearest
    minimum-distance pairs
  match nearest
    Ok value
      colors-at pairs value
    Error problem
      []

def choose-color [colors: Vector seed: Int cell: Int] -> String
  if (empty? colors)
    ""
    get colors (random.int-at seed cell 0 (length colors))

def group-fill [size: Int group: Vector boundary: Vector seed: Int cell: Int] -> Dict
  let pairs
    source-distances size group cell boundary
  let nearest
    minimum-distance pairs
  match nearest
    Ok value
      dict
        :color (choose-color (colors-at pairs value) seed cell)
        :step value
    Error problem
      dict
        :color ""
        :step 1

def fill-for [cells: Vector size: Int group: Vector boundary: Vector seed: Int cell: Int] -> Dict
  if (vectors.contains? group cell)
    group-fill size group boundary seed cell
    dict
      :color (get cells cell)
      :step 0

def resolve-valid [cells: Vector size: Int group: Vector seed: Int] -> Result
  let boundary
    sources cells size group
  let fills
    map
      fn [cell]
        fill-for cells size group boundary seed cell
      range 0 (* size size)
  Ok
    dict
      :cells
        map
          fn [fill]
            fill.color
          fills
      :steps
        map
          fn [fill]
            fill.step
          fills

def resolve [cells: Vector size: Int group: Vector seed: Int] -> Result
  if (not (= (length cells) (* size size)))
    Error "refill cells must match the grid size"
    if (not (grid.valid-indices? size size group))
      Error "refill group contains an invalid index"
      resolve-valid cells size group seed

def apply [cells: Vector size: Int group: Vector seed: Int] -> Vector
  let resolved
    resolve cells size group seed
  match resolved
    Ok value
      value.cells
    Error problem
      cells

def steps [cells: Vector size: Int group: Vector] -> Vector
  let resolved
    resolve cells size group 1
  match resolved
    Ok value
      value.steps
    Error problem
      map
        fn [index]
          0
        range 0 (length cells)
