#ifndef IPC_PROTOCOL_H
#define IPC_PROTOCOL_H

#include <cstdint>

namespace ipc {

constexpr uint64_t major_version = 1;
constexpr uint64_t minor_version = 24;

#define MSG_TYPE_REFLECTION(msg_type, msg_enum)        \
  template <>                                          \
  struct get_enum_from_type<msg_type> {                \
    static constexpr MsgType type = MsgType::msg_enum; \
  };

enum class MsgType : uint32_t {
  // This is for internal IPC system + universal
  RESERVED = 0,
  Error,
  Subscribe,
  Unsubscribe,
  SubscribeFilter,
  UnsubscribeFilter,
  ClearSubscriptionFilter,
  Heartbeat,
  String,
  IPCVersion,

  // Scoreboard
  SCOREBOARD_DAEMON = 1024,
  ScorePauseQuery,
  ScorePauseResponse,
  ScorePenaltyEvent, /* Deprecated, use ScorePenaltyResponse */
  ScorePenaltyQuery,
  ScorePenaltyResponse,
  ScorePeriodQuery,
  ScorePeriodResponse,
  ScoreScoreQuery,
  ScoreScoreResponse,
  ScoreTeamNameQuery,
  ScoreTeamNameResponse,
  ScoreTimePulse, /* time is encoded within headers */
  ScoreTimeOutQuery,
  ScoreTimeOutResponse,
  ScoreFoulCountQuery,
  ScoreFoulCountResponse,
  ScoreTimePulseQuery,
  ScoreGameStartQuery,
  ScoreGameStartResponse, /* time is encoded within headers */
  ScoreGameEndResponse,
  ScoreFreezeMaskQuery,
  ScoreFreezeMaskResponse,

  // Tracker
  TRACKER_DAEMON = 2048,
  TrackerPlayerQuerry,
  TrackerPlayerResponse,

  TrackerCameraTargetQuerry,
  TrackerCameraTargetResponse,

  TrackerObjectQuerry,
  TrackerObjectResponse,

  TrackerPlayerCentroidQuerry,
  TrackerPlayerCentroidResponse,

  // Number daemon
  NUMBER_DAEMON = 3072,

  NumberIdentificationQuerry,
  NumberIdentificationResponse,
  TeamIdentificationQuerry,
  TeamIdentificationResponse,
  NumberReviewResponse,
  TrackerSplitRequest,

  // Filtered Data from DB
  COACH_TABLET = 4096,

  PlayerHeatMapQuery,
  PlayerHeatMapResponse,
  PlayerGoalsHeatMapQuery,
  PlayerGoalsHeatMapResponse,
  GoalsHeatMapQuery,
  GoalsHeatMapResponse,
  PlayerShotsHeatMapQuery,
  PlayerShotsHeatMapResponse,
  ShotsHeatMapQuery,
  ShotsHeatMapResponse,
  PlayerPassesHeatMapQuery,
  PlayerPassesHeatMapResponse,
  PassesHeatMapQuery,
  PassesHeatMapResponse,
  PlayerMovementHeatMapQuery,
  PlayerMovementHeatMapResponse,
  MovementHeatMapQuery,
  MovementHeatMapResponse,
  PlayerStatisticsQuery,
  PlayerStatisticsResponse,
  GameEventQuery,
  GameEventData,
  HeatmapAllQuery,
  VisualizerTrackerDataQuery,
  VisualizerTrackerDataResponse,
  GameObjectStatisticsResponse,
  GameObjectStatisticsQuery,
  ArchivedGameListQuery,
  ArchivedGameListResponse,
  ArchivedGameRecordQuery,
  ArchivedGameRecordResponse,
  GameObjectPositionQuery,
  GameObjectPositionResponse,
  ArchivedGameTimeQuery,
  ArchivedGameTimeResponse,
  HomeTeamSideQuery,
  HomeTeamSideResponse,
  ObjectMovementHeatMapQuery,
  ObjectMovementHeatMapResponse,

  // Database stored daemon configuration service
  DB_DAEMON = 5120,
  DbDaemonConfigurationQuery,

  // Manual input
  MANUAL_INPUT = 6144,
  ManualObjectAtTrackerResponse,
  ManualPTZTargetTrackerResponse,

  // PG Lite
  PGLITE = 7168,
  PGLiteInterestPointResponse,
  PGLiteCameraRegionResponse,

  // Pipeline server messages
  PIPELINE_MGMT = 8192,

  StreamAuthenticateQuery,
  StreamListQuery,
  StreamQuery,
  StreamEventQuery,
  StreamAuthenticateResponse,
  StreamListResponse,
  StreamEventResponse,
};

// Default is error
template <typename T>
struct get_enum_from_type {
  static constexpr MsgType type = MsgType::Error;
};

/*
 * Internal + Universal messages
 */

/**
 * @brief IPC Version message
 * Major changes mean compatiblity break.
 * Minor changes mean new protocol messages or changes to existing messages, but minor enough not to cause compatiblity
 * break.
 */
struct IPCVersionMsg {
  /**
   * @brief Major version number.
   * Major changes mean compatiblity break.
   */
  const uint64_t major = major_version;
  /**
   * @brief Minor version number.
   * Minor changes mean protocol changes.
   */
  const uint64_t minor = minor_version;
};
MSG_TYPE_REFLECTION(IPCVersionMsg, IPCVersion)

/**
 * @brief Subscribe filter message
 * Sends MsgType to subscribe to.
 */
struct SubscribeFilterMsg {
  MsgType msg_type;
};
MSG_TYPE_REFLECTION(SubscribeFilterMsg, SubscribeFilter)

/**
 * @brief Unsubscribe filter message
 * Sends MsgType to unsubscribe to.
 */
struct UnsubscribeFilterMsg {
  MsgType msg_type;
};
MSG_TYPE_REFLECTION(UnsubscribeFilterMsg, UnsubscribeFilter)

/**
 * @brief String message
 * Sends on byte. To sends any ASCII string (or byte array) use std::vector<StringMsg> and fill it with data.
 */
struct StringMsg {
  uint8_t character;
};
MSG_TYPE_REFLECTION(StringMsg, String)

/**
 * @brief Error message
 * The error message can be used just like the StringMsg, but this specifies an error.
 * This can then be parsed and printed on recieving end.
 */
struct ErrorMsg {
  uint8_t character;
};
MSG_TYPE_REFLECTION(ErrorMsg, Error)

/*
 * Scoreboard daemon messages
 */

/* Game stoppage info */
// scorePauseReq has no data
/**
 * @brief Scoreboard pause event message.
 * Contains information about current game state.
 * @note ScorePauseRequest requires no additional information.
 */
struct scorePauseMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Paused indicator.
   * If paused, value is non-zero, otherwise it is zero.
   */
  uint8_t paused;
};
MSG_TYPE_REFLECTION(scorePauseMsg, ScorePauseResponse)

/* Penalty info */
/**
 * @brief The Scoreboard penalty information request message.
 * Contains relevant request parameters. When a request occurs,
 * response is sent containing all of the relevant information.
 * @sa scorePenaltyMsg for response message details.
 */
struct scorePenaltyReq {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;

  /**
   * @brief Slot ID.
   * @note If set to 255, information about all of the known active penalties will be sent for the team.
   */
  uint8_t slot_id;
};
MSG_TYPE_REFLECTION(scorePenaltyReq, ScorePenaltyQuery)

/**
 * @brief The scoreboard player penalty response message.
 * Contains a response to a request for a given team ID.
 * @sa scorePenaltyReq for request message
 */
struct scorePenaltyMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;

  /**
   * @brief Player ID.
   * Value ranges from 0 to 99
   * @note Value 255 represents that there is no active penalty in this slot.
   */
  uint8_t player_id;

  /**
   * @brief Duration.
   * Duration of the player's penalty in milliseconds.
   * @note If inactive/expired, this value is 0, otherwise it should be non-zero.
   * @note Represented in milliseconds with seconds precision.
   */
  uint32_t duration;
};
MSG_TYPE_REFLECTION(scorePenaltyMsg, ScorePenaltyResponse)

/* Period info */
// ScorePeriodReq has no data

/**
 * @brief The scoreboard period message.
 * @note May be broadcast as an event or sent on-demand.
 * @note ScorePeriod request does not require additional information.
 */
struct scorePeriodMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Current period.
   * Currently reported game period. Value ranges from 1 to 255.
   * @attention Not entirely reliable until correct scoreboard specification is sourced.
   * @note Special value: 15 signals intermission (pre-game and intermission between periods).
   * @note Special value: 14 signals over-time (extra time) period.
   * @attention Does not update after a game ends.
   * @note Periods numbers are not zero-based.
   */
  uint8_t period;
};
MSG_TYPE_REFLECTION(scorePeriodMsg, ScorePeriodResponse)

/* Game score info */
/**
 * @brief The scoreboard game score request message.
 * Used to request currently registered score for a specific team.
 * @sa scoreScoreMsg for response.
 */
struct scoreScoreReq {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;
};
MSG_TYPE_REFLECTION(scoreScoreReq, ScoreScoreQuery)

/**
 * @brief The scoreboard game score response message.
 * May be broadcast as an event or sent on-demand.
 * @sa scoreScoreReq for request message.
 */
struct scoreScoreMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;
  /**
   * @brief Score.
   * Total number of goals for the team at the current time.
   * @note Values range from 0 to 255.
   */
  uint8_t score;
};
MSG_TYPE_REFLECTION(scoreScoreMsg, ScoreScoreResponse)

/*
 * Regular time pulse data structure has been deprecated.
 * Use systime() and gametime() to get this information from the ScoreTimePulse message headers.
 * systime()           system clock timepoint
 * gametime()          steady clock game time timepoint, not steady
 * @sa Buffer class for additional details
 */

/* Team name info */
/**
 * @brief The scoreboard team name request message.
 * Request to fetch the name of a given team.
 * @sa scoreTeamNameMsg for response details.
 * @attention Not yet available.
 * @attention May not be available for all scoreboards and venues.
 */
struct scoreTeamNameReq {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;
};
MSG_TYPE_REFLECTION(scoreTeamNameReq, ScoreTeamNameQuery)

/**
 * @brief The score team name response message.
 * @attention May not be available for all scoreboards and venues.
 * @attention Not yet available.
 * @sa scoreTeamNameReq for request message.
 */
struct scoreTeamNameMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;
  /**
   * @brief Team name.
   * Contains name of a given team, up to 16 bytes, usually does not exceed 10 letters.
   */
  char team_name[16];
};
MSG_TYPE_REFLECTION(scoreTeamNameMsg, ScoreTeamNameResponse)

/* Timeout info */
/**
 * @brief The scoreboard team timeout info request message.
 * @sa scoreTimeOutMsg for response.
 */
struct scoreTimeOutReq {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;

  /**
   * @brief Timeout slot.
   * @note If set to 255, information on all possible timeout slots is sent.
   */
  uint8_t slot_id;
};
MSG_TYPE_REFLECTION(scoreTimeOutReq, ScoreTimeOutQuery)

/**
 * @brief The scoreboard team timeout info response message.
 * May be either broadcasted or sent on-demand.
 * @sa scoreTimeOutReq for request message.
 */
struct scoreTimeOutMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;

  /**
   * @brief Timeout ID.
   * Number of timeout, not zero based.
   * @warning not zero-based.
   */
  uint8_t id;

  /**
   * @brief Timeout status enumeration.
   *
   * Status of the timeout can be either:
   *    0 - Inactive (was not taken or was cleared)
   *    1 - Active (in progress)
   *    2 - Expired (was previously active and not yet cleared)
   *
   * @note Basketball: timeouts are reset after a half of the game.
   * @note Ice Hockey: timeouts can only be taken once if it is not a succesful coach video challenge (replay).
   */
  uint8_t status;

  /**
   * @brief Duration of time-out.
   * If expired or inactive, duration is 0, otherwise non-zero milliseconds.
   * @note Not expanded to milliseconds, but sent as milliseconds.
   */
  uint32_t duration;
};
MSG_TYPE_REFLECTION(scoreTimeOutMsg, ScoreTimeOutResponse)

// Scoreboard foul counter request & response
/**
 * @brief The scoreboard foul counter request message.
 * @sa scoreFoulCountMsg for response message details.
 */
struct scoreFoulCountReq {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;
};
MSG_TYPE_REFLECTION(scoreFoulCountReq, ScoreFoulCountQuery)

/**
 * @brief The scoreboard foul counter response message.
 * May be either broadcasted or sent as a response to request @sa scoreFoulCountReq.
 */
struct scoreFoulCountMsg {
  /**
   * @brief Game ID
   */
  int64_t game_id;

  /**
   * @brief Team ID.
   * @note Home team is represented with 0, away team is represented with 1.
   */
  uint8_t team_id;

  /**
   * @brief Foul counter value.
   * @note May be reset during the break.
   */
  uint8_t count;
};
MSG_TYPE_REFLECTION(scoreFoulCountMsg, ScoreFoulCountResponse)

struct scoreGameStartMsg {
  /**
   * @brief Unique game ID
   */
  int64_t game_id;
};
MSG_TYPE_REFLECTION(scoreGameStartMsg, ScoreGameStartResponse)

struct scoreGameEndMsg {
  /**
   * @brief Unique game ID
   */
  int64_t game_id;
};
MSG_TYPE_REFLECTION(scoreGameEndMsg, ScoreGameEndResponse)

///
/// \brief Scoreboard-daemon data freeze mask
///
struct scoreFreezeMaskMsg {
  uint64_t mask;  ///< freeze mask to set
};
MSG_TYPE_REFLECTION(scoreFreezeMaskMsg, ScoreFreezeMaskResponse)

struct StreamAuthenticateResponseMsg {
  /**
   * @brief unique client id
   */
  uint32_t client_id;
};
MSG_TYPE_REFLECTION(StreamAuthenticateResponseMsg, StreamAuthenticateResponse)

struct StreamRequestMsg {
  /**
   * @brief stream name/id to open
   */
  char name[128];
  /**
   * @brief stream name to open
   */
  char pipeline[512];
  /**
   * @brief flag to enable stream restart
   */
  bool restart = false;
};
MSG_TYPE_REFLECTION(StreamRequestMsg, StreamQuery)

struct StreamListRequestMsg {
  /**
   * @brief game id of the relevant stream list
   */
  int64_t game_id;
  /**
   * @brief unique client id
   */
  uint32_t client_id;
};
MSG_TYPE_REFLECTION(StreamListRequestMsg, StreamListQuery)

struct StreamListResponseMsg {
  /**
   * @brief relevant unique game id
   */
  int64_t game_id;
  /**
   * @brief stream unique id
   */
  uint32_t stream_id;
  /**
   * @brief unique client id
   */
  uint32_t client_id;
  /**
   * @brief null-terminated name of the stream, e.g. "Top Merged"
   */
  char name[128];
  /**
   * @brief null-terminated pipeline configuration for the stream or just the camera URL
   */
  char pipeline[512];
};
MSG_TYPE_REFLECTION(StreamListResponseMsg, StreamListResponse)

struct StreamEventRequestMsg {
  /**
   * @brief relevant game id
   */
  int64_t game_id;
  /**
   * @brief relevant stream id
   */
  uint32_t stream_id;
  /**
   * @brief unique client id
   */
  uint32_t client_id;
  /**
   * @brief the clip beginning offset (Tbase - Tbegin); may be 0 in which case this fragment begins from systime()
   * timestamp in header
   */
  uint32_t begin_offset;
  /**
   * @brief the clip ending offset (Tbase + Tend); may be 0 in which case this fragment should be played back until the
   * maximum allotted end (e.g. end of stream)
   */
  uint32_t end_offset;
};
MSG_TYPE_REFLECTION(StreamEventRequestMsg, StreamEventQuery)

struct StreamEventResponseMsg {
  /**
   * @brief relevant game id
   */
  int64_t game_id;
  /**
   * @brief relevant stream id
   */
  uint32_t stream_id;
  /**
   * @brief unique client id
   */
  uint32_t client_id;
  /**
   * @brief relevant request status
   *
   * Status of the url configuration can be either:
   *    0 - OK (url configured and ready for streaming requested content)
   *    1 - Out of range (StreamEventRequestMsg header system_time does not fit in recorded live stream range)
   *    2 - Missing stream (Source file not available)
   *    3 - Stream not ready (Source files will be ready once the stream gets ready)
   *    4 - Stream not active (Some error occured while streaming)
   *    5 - RTSP-server not active (Configuration failed)
   *    6 - Requested offline stream was not found
   */
  uint16_t response;
  /**
   * @brief null-terminated name of the stream, e.g. "Top Merged"
   */
  char name[128];
  /**
   * @brief null-terminated rtsp-server mountpoint configuration to connect. Made of stream_id, client_id and timestamp
   * to make a mointpoint id
   */
  char url[512];
};
MSG_TYPE_REFLECTION(StreamEventResponseMsg, StreamEventResponse)

/*
 * Tracker daemon messages
 */
/**
 * @brief Tracker position message.
 */
struct TrackerPlayerPositionMsg {
  /**
   * @brief Tracker id.
   * Tracker id whose position is being sent. Starts at 1. Zero is reserved for error.
   */
  uint32_t id;
  /**
   * @brief X feet position in meters.
   */
  float x;
  /**
   * @brief Y feet position in meters.
   */
  float y;
};
MSG_TYPE_REFLECTION(TrackerPlayerPositionMsg, TrackerPlayerResponse)

/**
 * @brief Tracker position message.
 */
struct TrackerPlayerCentroidPositionMsg {
  /**
   * @brief Tracker id.
   * Tracker id whose position is being sent. Starts at 1. Zero is reserved for error.
   */
  uint32_t id;
  /**
   * @brief X centroid position in meters.
   */
  float x;
  /**
   * @brief Y centroid position in meters.
   */
  float y;
};
MSG_TYPE_REFLECTION(TrackerPlayerCentroidPositionMsg, TrackerPlayerCentroidResponse)

/**
 * @brief Position of camera target.
 */
struct TrackerCameraTargetMsg {
  /**
   * @brief X position in meters.
   */
  float target_x;
  /**
   * @brief Y position in meters.
   */
  float target_y;
};
MSG_TYPE_REFLECTION(TrackerCameraTargetMsg, TrackerCameraTargetResponse)

/**
 * @brief Position and state of the game object (puck, ball, etc.).
 */
struct TrackerObjectMsg {
  /**
   * @brief X position in meters.
   */
  float object_x;
  /**
   * @brief Y position in meters.
   */
  float object_y;
  /**
   * @brief State of the object.
   * 0 - flying / sliding without player contact
   * 1 - at player
   */
  uint8_t state;
};
MSG_TYPE_REFLECTION(TrackerObjectMsg, TrackerObjectResponse)

/**
 * @brief Assigns tracker id with team and player id.
 */
struct NumberIdentificationMsg {
  /**
   * @brief Tracker id which is being identified.
   */
  uint32_t tracker_id;
  /**
   * @brief Team id being assigned. Team can be 0, 1 or 2 (referee).
   */
  uint32_t team_id;
  /**
   * @brief Player id being assigned.
   */
  uint32_t player_id;
};
MSG_TYPE_REFLECTION(NumberIdentificationMsg, NumberIdentificationResponse)

/**
 * @brief Assigns tracker id with team id.
 * @sa NumberIdentificationMsg for full message.
 */
struct TeamIdentificationMsg {
  /**
   * @brief Tracker id which is being identified.
   */
  uint32_t tracker_id;
  /**
   * @brief Team id being assigned. Team can be 0, 1 or 2 (referee).
   */
  uint32_t team_id;
};
MSG_TYPE_REFLECTION(TeamIdentificationMsg, TeamIdentificationResponse)

/**
* @brief Split tracker with ID at timestamp specified in message header is requried
*/
struct TrackerSplitRequestMsg {
/**
* @brief Tracker id.
*/
uint32_t id;
/**
* @brief Team id being assigned. Team can be 0, 1 or 2 (referee).
*/
uint32_t team_id;
/**
* @brief Player id being assigned.
*/
uint32_t player_id;
};
MSG_TYPE_REFLECTION(TrackerSplitRequestMsg, TrackerSplitRequest)

/**
 * @brief Send tracker to review daemon to process or delete it.
 */
struct NumberReviewMsg {
  /**
   * @brief Tracker id which is being reviewed.
   */
  uint32_t tracker_id;
  /**
   * @brief Review id being assigned. Can be 0 (review tracker) or 1 (delete tracker from review daemon).
   */
  uint8_t review_id;
};
MSG_TYPE_REFLECTION(NumberReviewMsg, NumberReviewResponse)

/**
 * @brief Message to add game object (puck, ball, etc.) event to tracker.
 */
struct ManualObjectAtTrackerMsg {
  /**
   * @brief Tracker id for which the event happens.
   */
  uint32_t tracker_id;
  /**
   * @brief Game object event.
     Possible values:
      1 = Pass recieved
      2 = Pass reassigned (manual fix)
      3 = Goal
      4 = Goal missed
      5 = PLayer init (manual fix)
      6 = Ball lost
      7 = Pass given
   */
  uint32_t event_id;
};
MSG_TYPE_REFLECTION(ManualObjectAtTrackerMsg, ManualObjectAtTrackerResponse)

/**
 * @brief Message for specifying target tracker to follow for ptz camera
 */
struct ManualPTZTargetTrackerMsg {
  /**
   * @brief Tracker id which to follow with ptz
   */
  uint32_t tracker_id;
};
MSG_TYPE_REFLECTION(ManualPTZTargetTrackerMsg, ManualPTZTargetTrackerResponse)

struct PlayerStatisticsReq {
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerStatisticsReq, PlayerStatisticsQuery);

struct PlayerStatisticsMsg {
  uint32_t number;                  ///< Player number
  uint32_t team;                    ///< Player team ID \note 0 - home, 1 - away
  uint32_t passes;                  ///< number of passes sent by player
  uint32_t passes_recv;             ///< number of passes received by player
  uint32_t completed_passes;        ///< number of passes reached team members
  uint32_t shots;                   ///< number of shots made by player
  uint32_t goals;                   ///< number of goals scored by player
  uint32_t number_of_attacks;       ///< number of attacks, player was involved in
  uint32_t number_of_defences;      ///< number of defences player was involved in
  uint32_t time_on_field;           ///< time spent on field \note in milliseconds
  uint32_t time_with_object;        ///< time player had game object \note in milliseconds
  uint32_t time_in_offensive_zone;  ///< Some time
  uint32_t time_in_defensive_zone;  ///< Some other time
  float distance_ran;               ///< distance ran
  float average_speed;              ///< average speed for player
  float max_speed;                  ///< maximum speed of player
  int64_t game_id;                  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerStatisticsMsg, PlayerStatisticsResponse)

struct PlayerGoalsHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerGoalsHeatMapReq, PlayerGoalsHeatMapQuery)

struct PlayerGoalsHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerGoalsHeatMapMsg, PlayerGoalsHeatMapResponse)

struct GoalsHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(GoalsHeatMapReq, GoalsHeatMapQuery)

struct GoalsHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(GoalsHeatMapMsg, GoalsHeatMapResponse)

struct PlayerShotsHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerShotsHeatMapReq, PlayerShotsHeatMapQuery)

struct PlayerShotsHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerShotsHeatMapMsg, PlayerShotsHeatMapResponse)

struct ShotsHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t team;    ///< player number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(ShotsHeatMapReq, ShotsHeatMapQuery)

struct ShotsHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t team;    ///< player number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(ShotsHeatMapMsg, ShotsHeatMapResponse)

struct PlayerPassesHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerPassesHeatMapReq, PlayerPassesHeatMapQuery)

struct PlayerPassesHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerPassesHeatMapMsg, PlayerPassesHeatMapResponse)

struct PassesHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PassesHeatMapReq, PassesHeatMapQuery)

struct PassesHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PassesHeatMapMsg, PassesHeatMapResponse)

struct PlayerMovementHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerMovementHeatMapReq, PlayerMovementHeatMapQuery)

struct PlayerMovementHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t number;  ///< player number
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(PlayerMovementHeatMapMsg, PlayerMovementHeatMapResponse)

struct MovementHeatMapReq {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(MovementHeatMapReq, MovementHeatMapQuery)

struct MovementHeatMapMsg {
  uint32_t id;      ///< request id
  uint32_t team;    ///< team number
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(MovementHeatMapMsg, MovementHeatMapResponse)

struct ObjectMovementHeatMapReq {
  uint32_t id;      ///< request id
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(ObjectMovementHeatMapReq, ObjectMovementHeatMapQuery)

struct ObjectMovementHeatMapMsg {
  uint32_t id;      ///< request id
  uint8_t byte;     ///< data byte
  uint8_t status;   ///< response status: 0 - OK; 1 - error;
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(ObjectMovementHeatMapMsg, ObjectMovementHeatMapResponse)


/**
 * @brief Request message for the visualizer tracker data
 */
struct VisualizerTrackerDataReq {
  /**
   * @brief unique game id to request tracker data for
   */
  int64_t game_id;
};
MSG_TYPE_REFLECTION(VisualizerTrackerDataReq, VisualizerTrackerDataQuery);

/**
 * @brief Structure holding position and speed info for single tracker
 * Very useful for all kinds of real-time visualization
 */
struct VisualizerTrackerDataMsg {
  /**
   * @brief x position - feet position in meters
   */
  float x;
  /**
   * @brief y position - feet position in meters
   */
  float y;
  /**
   * @brief average x speed of current tracker in meters
   */
  float speed_x;
  /**
   * @brief average y speed of current tracker in meters
   */
  float speed_y;
  /**
   * @brief centroid x position - centroid position in pixels
   */
  float centroid_x;
  /**
   * @brief centroid y position - centroid position in pixels
   */
  float centroid_y;
  /**
   * @brief unique tracker ID that is currently alive
   */
  uint32_t t_id;
  /**
   * @brief team, which tracker belongs to
   * @note if tracker has no team assigned, this value is 256
   */
  uint32_t team;
  /**
   * @brief player number, which this tracker belongs to
   * @note if tracker has no number assigned, this value is 256
   */
  uint32_t number;
  /**
   * @brief unique game id this tracker belongs to
   */
  int64_t game_id;
};
MSG_TYPE_REFLECTION(VisualizerTrackerDataMsg, VisualizerTrackerDataResponse)

///
/// \brief The game object statistics request
///
struct GameObjectStatisticsReq {
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(GameObjectStatisticsReq, GameObjectStatisticsQuery);

///
/// \brief The game object statistics
///
struct GameObjectStatisticsMsg {
  float speed;      ///< Speed for game object (puck, ball)
  float max_speed;  ///< Maximum speed of game object
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(GameObjectStatisticsMsg, GameObjectStatisticsResponse)

///
/// \brief Game event archive request message
///
struct GameEventDataReq {
  int64_t game_id;  ///< unique game id
};
MSG_TYPE_REFLECTION(GameEventDataReq, GameEventQuery);

///
/// \brief User-created game event representation
///
struct GameEventDataMsg {
  char desc[2048];  ///< event description
  char name[256];   ///< event title
  int64_t game_id;  ///< unique game id
  int64_t event_id; ///< event id
  uint32_t team;    ///< team id
  uint32_t player;  ///< player id
  uint32_t period;  ///< period number
};
MSG_TYPE_REFLECTION(GameEventDataMsg, GameEventData);

///
/// \brief Archived game list entry message
/// \note Game start time is encoded in the header
///
struct ArchivedGameListMsg {
  int64_t game_id;      ///< game id
  uint32_t sport_id;    ///< sport type id
  char name_home[255];  ///< home team name
  char name_away[255];  ///< away team name
  uint64_t game_start;  ///< game start time
  uint64_t game_end;    ///< game end time
};
MSG_TYPE_REFLECTION(ArchivedGameListMsg, ArchivedGameListResponse);

///
/// \brief Archived game information begin look-up message
/// Requests a game and all its information from the server.
///
struct ArchivedGameInfoReq {
  int64_t game_id;  ///< game id
};
MSG_TYPE_REFLECTION(ArchivedGameInfoReq, ArchivedGameRecordQuery);

///
/// \brief Archived game look-up request status message
/// Main purpose of this message is to inform the client of the request readiness as loading game from the archive may
/// take a while on slow connection.
///
struct ArchivedGameInfoMsg {
  int64_t game_id;  ///< game id
  uint32_t status;  ///< status of response: 0 - OK, all done
};
MSG_TYPE_REFLECTION(ArchivedGameInfoMsg, ArchivedGameRecordResponse);

///
/// \brief Archived game object positions request message
/// Triggers a torrent of archived tracker position messages
/// \sa GameObjectPositionMsg
///
struct GameObjectPositionReq {
  int64_t game_id;  ///< game id
};
MSG_TYPE_REFLECTION(GameObjectPositionReq, GameObjectPositionQuery);

///
/// \brief Archived tracker positions response message
///
struct GameObjectPositionMsg {
  float x;          ///< x position in meters
  float y;          ///< y position in meters
  int64_t game_id;  ///< game id
};
MSG_TYPE_REFLECTION(GameObjectPositionMsg, GameObjectPositionResponse);

///
/// \brief Historical game time data request
///
struct ArchivedGameTimeReq {
  int64_t game_id;  ///< game id
};
MSG_TYPE_REFLECTION(ArchivedGameTimeReq, ArchivedGameTimeQuery)

///
/// \brief Historical game time data response
///
struct ArchivedGameTimeMsg {
  int64_t game_id;  ///< game id
};
MSG_TYPE_REFLECTION(ArchivedGameTimeMsg, ArchivedGameTimeResponse)

///
/// \brief Home team side response
///
struct HomeTeamSideMsg {
  uint8_t side;  ///< 0 - left side for home team, 1 - right side for home team
};
MSG_TYPE_REFLECTION(HomeTeamSideMsg, HomeTeamSideResponse)


/*
 * PGLite messages (aka 3cam)
 */
/**
 * @brief Interest point PGLite is filming
 */
struct PGLiteInterestPointMsg {
  /**
   * @brief Normalized X position (0-1) for interest point
   */
  float x;
  /**
   * @brief Normalized Y position (0-1) for interest point
   */
  float y;
};
MSG_TYPE_REFLECTION(PGLiteInterestPointMsg, PGLiteInterestPointResponse)

/**
 * @brief Interest region PGLite is filming
 */
struct PGLiteCameraRegionMsg {
  /**
   * @brief 0 - full, 1 - left, 2 - center, 3 - right
   */
  uint8_t region;
};
MSG_TYPE_REFLECTION(PGLiteCameraRegionMsg, PGLiteCameraRegionResponse)
}  // namespace ipc
#endif  // IPC_PROTOCOL_H
