defmodule Grizzly.ZWave.CommandClasses.ThermostatSetback do
  @moduledoc """
  "ThermostatSetback" Command Class

  The Thermostat Setback Command Class is used to change the current state of a non-schedule setback
  thermostat.
  """

  @behaviour Grizzly.ZWave.CommandClass

  alias Grizzly.ZWave.DecodeError

  @type type :: :no_override | :temporary_override | :permanent_override
  @type state :: number | :frost_protection | :energy_saving

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

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

  @spec encode_type(type) :: byte
  def encode_type(:no_override), do: 0x00
  def encode_type(:temporary_override), do: 0x01
  def encode_type(:permanent_override), do: 0x02

  @spec decode_type(byte) :: {:ok, type} | {:error, Grizzly.ZWave.DecodeError.t()}
  def decode_type(0x00), do: {:ok, :no_override}
  def decode_type(0x01), do: {:ok, :temporary_override}
  def decode_type(0x02), do: {:ok, :permanent_override}

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

  @spec encode_state(state) :: byte
  def encode_state(kelvins) when kelvins >= -12.8 and kelvins <= -1, do: round(256 + kelvins * 10)
  def encode_state(kelvins) when kelvins >= 0 and kelvins <= 12, do: round(kelvins * 10)
  def encode_state(:frost_protection), do: 0x79
  def encode_state(:energy_saving), do: 0x7A

  @spec decode_state(byte) :: {:ok, state} | {:error, Grizzly.ZWave.DecodeError.t()}
  def decode_state(byte) when byte in 0x80..0xFF, do: {:ok, (byte - 256) / 10}
  def decode_state(byte) when byte in 0x00..0x78, do: {:ok, byte / 10}
  def decode_state(0x79), do: {:ok, :frost_protection}
  def decode_state(0x7A), do: {:ok, :energy_saving}

  def decode_state(byte),
    do: {:error, %DecodeError{value: byte, param: :state, command: :thermostat_setback}}
end
