/*
  provenance.h

  Copyright (c) 2021 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 "data.h"
#include "uuid/uuid.h"

#include <cstdint>
#include <string>

namespace sACN {

/**
 * @brief Metadata for a sACN::Universe
 */
struct Provenance
{
  Provenance();
  explicit Provenance(ACN::PDU::Message<DATA::Pdu>);

  /// \cite sACN 5.6 CID (Component Identifier)
  ///
  /// The Root Layer contains a CID. The CID shall be compliant with RFC 4122
  /// \cite uuid.
  UUID::uuid cid;

  /// \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.
  ///
  /// 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 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;

  /// \cite sACN 6.2.3 E1.31 Data Packet: Priority
  ///
  /// Sources that do not support variable priority shall transmit a priority
  /// of 100. Priority increases with numerical value, e.g., 200 is a higher
  /// priority than 100.
  uint8_t priority;

  /// \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;

  /// \cite sACN 6.2.5 E1.31 Data Packet: Sequence Number
  ///
  /// 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.
  uint8_t sequence_number;

  /// \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::data_options options;

  /// \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;

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

} // SACN namespace


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