/*
  data.h

  Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com)

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/
#pragma once

#include "acn/pdu.h"
#include "sacn.h"

/**
 * @brief \cite sACN 1.4 Classes of Data Appropriate for Transmission
 *
 * > This standard, E1.31, is intended to define a method to carry DMX512-A
 * > \cite DMX style data and metadata over IP Networks. It is designed to carry
 * > repetitive control data from one or more sources to one or more receivers.
 * > This protocol is intended to be used to control dimmers, other lighting
 * > devices, and related non- hazardous effects equipment.
*/
namespace sACN::DATA {

/**
 * @brief 6.2.6 E1.31 Data Packet: Options
 *
 * > This bit-oriented field is used to encode optional flags that control how
 * > the packet is used.
 */
struct data_options
    : ACN::PDU::pdu_stream_object
{
  union {
    uint8_t byte;
    struct {
      uint8_t reserved              : 5;
      /// @brief \cite sACN Force_Synchronization: Bit 5
      ///
      /// > This bit indicates whether to lock or revert to an unsynchronized
      /// > state when synchronization is lost (See Section 11 on Universe
      /// > Synchronization and 11.1 for discussion on synchronization states).
      /// > When set to 0, components that had been operating in a synchronized
      /// > state shall not update with any new packets until synchronization
      /// > resumes. When set to 1, once synchronization has been lost,
      /// > components that had been operating in a synchronized state need
      /// > not wait for a new E1.31 Synchronization Packet in order to update
      /// > to the next E1.31 Data Packet.
      bool force_synchronization : 1;

      /// @brief \cite sACN Stream_Terminated: Bit 6
      ///
      /// > This bit is intended to allow E1.31 sources to terminate
      /// > transmission of a stream or of universe synchronization without
      /// > waiting for a timeout to occur, and to indicate to receivers that
      /// > such termination is not a fault condition.
      /// >
      /// > When set to 1 in an E1.31 Data Packet, this bit indicates that the
      /// > source of the data for the universe specified in this packet has
      /// > terminated transmission of that universe. Three packets containing
      /// > this bit set to 1 shall be sent by sources upon terminating
      /// > sourcing of a universe. Upon receipt of a packet containing this
      /// > bit set to a value of 1, a receiver shall enter network data loss
      /// > condition. Any property values in an E1.31 Data Packet containing
      /// > this bit shall be ignored.
      bool stream_terminated : 1;

      /// @brief \cite sACN Preview_Data: Bit 7 (most significant bit)
      ///
      /// > This bit, when set to 1, indicates that the data in this packet is
      /// > intended for use in visualization or media server preview
      /// > applications and shall not be used to generate live output.
      bool preview_data : 1;
    };
  };

  size_t   streamSize() const override { return 1; }
  void     iStream(ACN::PDU::Stream) override;
  void     oStream(ACN::PDU::Stream) const override;
};


/**
 * @brief \cite sACN Table 6-1: E1.31 Data Packet Framing Layer
 */
struct data_header
    : ACN::PDU::pdu_header
{
  /// @brief \cite sACN 6.2.2 E1.31 Data Packet: Source Name
  ///
  /// > A user-assigned name provided by the source of the packet for use in
  /// > displaying the identity of a source to a user. There is no mechanism,
  /// > other than user configuration, to ensure uniqueness of this name. The
  /// > source name shall be null-terminated. If the source component implements
  /// > ACN discovery as defined in EPI 19 \cite epi19, then this name shall be
  /// > the same as the UACN field specified in EPI 19 \cite epi19.
  /// > User-Assigned Component Names, as the title suggests, supply a single
  /// > name for an entire component, so this Source Name field will exist for
  /// > each unique CID, but may be the same across multiple universes sourced
  /// > by the same component.
  std::string source_name;

  /// @brief \cite sACN 6.2.3 E1.31 Data Packet: Priority
  ///
  /// > The Priority field is an unsigned, one octet field. The value is used by
  /// > receivers in selecting between multiple sources of data for a given
  /// > universe number. Sources that do not support variable priority shall
  /// > transmit a priority of 100. No priority outside the range of 0 to 200
  /// > shall be transmitted on the network. Priority increases with numerical
  /// > value, e.g., 200 is a higher priority than 100.
  uint8_t priority = 100;

  /// @brief \cite sACN 6.2.4 E1.31 Data Packet: Synchronization Address
  ///
  /// > The Synchronization Address identifies a universe number to be used for
  /// > universe synchronization.
  uint16_t sync_address;

  /// @brief \cite sACN 6.2.5 E1.31 Data Packet: Sequence Number
  ///
  /// > In a routed network environment it is possible for packets to be received
  /// > in a different order to the one in which they were sent. The sequence
  /// > number allows receivers or diagnostic equipment to detect out of sequence
  /// > or lost packets.
  /// >
  /// > Sources shall maintain a sequence for each universe they
  /// > transmit. The sequence number for a universe shall be incremented by one
  /// > for every packet sent on that universe. There is no implied relationship
  /// > between the sequence number of an E1.31 Synchronization Packet and the
  /// > sequence number of an E1.31 Data Packet on that same universe.
  mutable uint8_t sequence_number;

  /// @brief \cite sACN 6.2.6 E1.31 Data Packet: Options
  ///
  /// > This bit-oriented field is used to encode optional flags that control
  /// > how the packet is used.
  data_options options;

  /// @brief \cite sACN 6.2.7 E1.31 Data Packet: Universe
  ///
  /// > The Universe is a 16-bit field that defines the universe number of the
  /// > data carried in the packet. Universe values shall be limited to the
  /// > range 1 to 63999.
  uint16_t universe;

  size_t   streamSize() const override { return 71; }
  void     iStream(ACN::PDU::Stream) override;
  void     oStream(ACN::PDU::Stream) const override;

  friend bool operator== (const data_header& a, const data_header& b);
  friend bool operator< (const data_header& a, const data_header& b);
  friend bool operator> (const data_header& a, const data_header& b);
};


/**
 * @brief \cite sACN 6.2 E1.31 Data Packet Framing Layer
 */
class Pdu
  : public ACN::PDU::Pdu
{
public:
    Pdu();
    void iStream(ACN::PDU::Stream) override;
};

} // SACN::DATA


namespace std
{
  template<>
  /**
   * @brief The hash struct specilizaton for SACN::Provenance
   */
  struct hash<sACN::DATA::data_header>
  {
    /**
     * @brief operator ()
     * @param src
     * @return
     */
    size_t operator()(sACN::DATA::data_header const& src) const noexcept
    {
      size_t h1 = hash<string>{}(src.source_name);
      size_t h2 = hash<uint16_t>{}(src.universe);
      size_t h3 = hash<uint8_t>{}(src.priority);
      size_t h = h1 ^ h2 ^ h3; // or use boost::hash_combine
      return h;
    }
  };
} // std namespace
