defmodule Grizzly.ZWave.CommandClasses.SensorBinary do
  @moduledoc """
  Deprecated command class for triggered/not triggered sensors.
  """

  @behaviour Grizzly.ZWave.CommandClass

  alias Grizzly.ZWave.DecodeError
  import Grizzly.ZWave.Encoding

  @type sensor_type() ::
          :general_purpose
          | :smoke
          | :co
          | :co2
          | :heat
          | :water
          | :freeze
          | :tamper
          | :aux
          | :door_window
          | :tilt
          | :motion
          | :glass_break

  @impl Grizzly.ZWave.CommandClass
  def byte(), do: 0x30

  @impl Grizzly.ZWave.CommandClass
  def name(), do: :sensor_binary

  @doc """
  Encode the type value for the sensor report
  """
  @spec encode_type(sensor_type()) :: byte()
  def encode_type(:general_purpose), do: 0x01
  def encode_type(:smoke), do: 0x02
  def encode_type(:co), do: 0x03
  def encode_type(:co2), do: 0x04
  def encode_type(:heat), do: 0x05
  def encode_type(:water), do: 0x06
  def encode_type(:freeze), do: 0x07
  def encode_type(:tamper), do: 0x08
  def encode_type(:aux), do: 0x09
  def encode_type(:door_window), do: 0x0A
  def encode_type(:tilt), do: 0x0B
  def encode_type(:motion), do: 0x0C
  def encode_type(:glass_break), do: 0x0D

  @doc """
  Parse the type value from a byte
  """
  @spec decode_type(byte()) :: {:ok, sensor_type()} | {:error, DecodeError.t()}
  def decode_type(0x01), do: {:ok, :general_purpose}
  def decode_type(0x02), do: {:ok, :smoke}
  def decode_type(0x03), do: {:ok, :co}
  def decode_type(0x04), do: {:ok, :co2}
  def decode_type(0x05), do: {:ok, :heat}
  def decode_type(0x06), do: {:ok, :water}
  def decode_type(0x07), do: {:ok, :freeze}
  def decode_type(0x08), do: {:ok, :tamper}
  def decode_type(0x09), do: {:ok, :aux}
  def decode_type(0x0A), do: {:ok, :door_window}
  def decode_type(0x0B), do: {:ok, :tilt}
  def decode_type(0x0C), do: {:ok, :motion}
  def decode_type(0x0D), do: {:ok, :glass_break}

  def decode_type(byte),
    do: {:error, %DecodeError{value: byte, param: :sensor_type, command: :sensor_binary}}

  @doc """
  Decode a list of sensor types from a bitmap.

  ## Examples

      iex> decode_sensor_types(<<0x40, 0x00>>)
      {:ok, sensor_types: [:water]}
  """
  @spec decode_sensor_types(binary) :: {:ok, [sensor_types: [atom]]} | {:error, DecodeError.t()}
  def decode_sensor_types(binary) do
    sensor_types =
      binary
      |> decode_bitmask()
      |> Enum.map(fn value ->
        case decode_type(value) do
          {:ok, type} -> type
          {:error, _error} -> nil
        end
      end)
      |> Enum.reject(&is_nil/1)

    {:ok, [sensor_types: sensor_types]}
  end

  @doc """
  Encode a list of sensor types as a bitmap.

  ## Examples

      iex> encode_sensor_types([:water])
      <<0x40, 0x00>>
  """
  @spec encode_sensor_types([atom]) :: binary
  def encode_sensor_types(sensor_types) do
    sensor_types
    |> Enum.map(&encode_type/1)
    |> encode_bitmask(min_bytes: 2)
  end
end
