defmodule Microwaveprop.Propagation.Grid do
  @moduledoc "CONUS grid definition for propagation scoring. 0.125 degree resolution (~14 km)."

  @lat_min 25.0
  @lat_max 50.0
  @lon_min -125.0
  @lon_max -66.0
  @step 0.125

  @doc "Returns all grid points as `{lat, lon}` tuples covering CONUS at 0.125 degree spacing."
  @spec conus_points() :: [{float(), float()}]
  def conus_points do
    for lat <- float_range(@lat_min, @lat_max, @step),
        lon <- float_range(@lon_min, @lon_max, @step) do
      {Float.round(lat, 3), Float.round(lon, 3)}
    end
  end

  @doc "Returns the grid step size in degrees."
  @spec step() :: float()
  def step, do: @step

  @doc "Returns the CONUS bounding box as a map."
  @spec bounds() :: %{lat_min: float(), lat_max: float(), lon_min: float(), lon_max: float()}
  def bounds, do: %{lat_min: @lat_min, lat_max: @lat_max, lon_min: @lon_min, lon_max: @lon_max}

  @doc "Returns the grid specification for wgrib2 -lola extraction."
  @spec wgrib2_grid_spec() :: %{
          lon_start: float(),
          lon_count: non_neg_integer(),
          lon_step: float(),
          lat_start: float(),
          lat_count: non_neg_integer(),
          lat_step: float()
        }
  def wgrib2_grid_spec do
    lon_count = round((@lon_max - @lon_min) / @step) + 1
    lat_count = round((@lat_max - @lat_min) / @step) + 1

    %{
      lon_start: @lon_min,
      lon_count: lon_count,
      lon_step: @step,
      lat_start: @lat_min,
      lat_count: lat_count,
      lat_step: @step
    }
  end

  defp float_range(start, stop, step) do
    count = round((stop - start) / step) + 1
    Enum.map(0..(count - 1), fn i -> start + i * step end)
  end
end
