# SPDX-FileCopyrightText: 2016 Frank Hunleth
# SPDX-FileCopyrightText: 2017 Amos King
# SPDX-FileCopyrightText: 2018 Jon Carstens
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Circuits.UART.Framing do
  @moduledoc """
  A behaviour for implementing framers for data received over a UART.
  """

  @doc """
  Initialize the state of the framer based on options passed to
  `Circuits.UART.open/3`.

  This function should return the initial state for the framer or
  an error.
  """
  @callback init(args :: term) :: {:ok, state} | {:error, reason} when state: term, reason: term

  @doc """
  Add framing to the passed in data.

  The returned `frame_data` will be sent out the UART.
  """
  @callback add_framing(data :: term, state :: term) ::
              {:ok, framed_data, new_state} | {:error, reason, new_state}
            when new_state: term,
                 framed_data: binary,
                 reason: term

  @doc """
  Remove the framing off received data. If a partial frame is left over at the
  end, then `:in_frame` should be returned. All of the frames received should
  be returned in the second tuple.

  The terms returned as the second part of the tuple can be anything. They can be
  the binary messages without the framing, structs based on your commands, or anything
  else. If you have errors in the protocol, for example a bad checksum, one convention
  is to return an error tuple `{:error, :echksum, message}`.
  For debugging you may want to include the message and framing with the error for
  simpler debugging.
  """
  @callback remove_framing(new_data :: binary, state :: term) ::
              {:in_frame, [term], new_state} | {:ok, [term], new_state}
            when new_state: term

  @doc """
  If `remove_framing/2` returned `:in_frame` and a user-specified timeout for
  reassembling frames has elapsed, then this function is called. Depending on
  the semantics of the framing, a partial frame may be returned or the
  incomplete frame may be dropped.
  """
  @callback frame_timeout(state :: term) :: {:ok, [term], new_state} when new_state: term

  @doc """
  This is called when the user invokes `Circuits.UART.flush/2`. Any partially
  received frames should be dropped.
  """
  @callback flush(direction :: :receive | :transmit | :both, state :: term) :: new_state
            when new_state: term
end
