/*  RetroArch - A frontend for libretro.
 *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
 *  Copyright (C) 2011-2016 - Daniel De Matteis
 *
 *  RetroArch is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU General Public License as published by the Free Software Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with RetroArch.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __RARCH_NETPLAY_PRIVATE_H
#define __RARCH_NETPLAY_PRIVATE_H

#include "netplay.h"

#include <net/net_compat.h>
#include <retro_endianness.h>

#include "../../command.h"
#include "../../general.h"
#include "../../autosave.h"
#include "../../dynamic.h"
#include "../../movie.h"
#include "../../msg_hash.h"
#include "../../system.h"
#include "../../runloop.h"
#include "../../verbosity.h"

#ifdef ANDROID
#define HAVE_IPV6
#endif

#define UDP_FRAME_PACKETS 16
#define UDP_WORDS_PER_FRAME 4 /* Allows us to send 128 bits worth of state per frame. */
#define MAX_SPECTATORS 16
#define RARCH_DEFAULT_PORT 55435

#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)

struct delta_frame
{
   void *state;

   uint32_t real_input_state[UDP_WORDS_PER_FRAME - 1];
   uint32_t simulated_input_state[UDP_WORDS_PER_FRAME - 1];
   uint32_t self_state[UDP_WORDS_PER_FRAME - 1];

   bool is_simulated;
   bool used_real;
};

struct netplay_callbacks {
   void (*pre_frame) (netplay_t *netplay);
   void (*post_frame)(netplay_t *netplay);
   bool (*info_cb)   (netplay_t *netplay, unsigned frames);
};

struct netplay
{
   char nick[32];
   char other_nick[32];
   struct sockaddr_storage other_addr;

   struct retro_callbacks cbs;
   /* TCP connection for state sending, etc. Also used for commands */
   int fd;
   /* UDP connection for game state updates. */
   int udp_fd;
   /* Which port is governed by netplay (other user)? */
   unsigned port;
   bool has_connection;

   struct delta_frame *buffer;
   size_t buffer_size;

   /* Pointer where we are now. */
   size_t self_ptr; 
   /* Points to the last reliable state that self ever had. */
   size_t other_ptr;
   /* Pointer to where we are reading. 
    * Generally, other_ptr <= read_ptr <= self_ptr. */
   size_t read_ptr;
   /* A temporary pointer used on replay. */
   size_t tmp_ptr;

   size_t state_size;

   /* Are we replaying old frames? */
   bool is_replay;
   /* We don't want to poll several times on a frame. */
   bool can_poll;

   /* To compat UDP packet loss we also send 
    * old data along with the packets. */
   uint32_t packet_buffer[UDP_FRAME_PACKETS * UDP_WORDS_PER_FRAME];
   uint32_t frame_count;
   uint32_t read_frame_count;
   uint32_t other_frame_count;
   uint32_t tmp_frame_count;
   struct addrinfo *addr;
   struct sockaddr_storage their_addr;
   bool has_client_addr;

   unsigned timeout_cnt;

   /* Spectating. */
   struct {
      bool enabled;
      int fds[MAX_SPECTATORS];
      uint16_t *input;
      size_t input_ptr;
      size_t input_sz;
   } spectate;
   bool is_server;
   /* User flipping
    * Flipping state. If ptr >= flip_frame, we apply the flip.
    * If not, we apply the opposite, effectively creating a trigger point.
    * To avoid collition we need to make sure our client/host is synced up 
    * well after flip_frame before allowing another flip. */
   bool flip;
   uint32_t flip_frame;

   /* Netplay pausing
    */
   bool pause;
   uint32_t pause_frame;

   struct netplay_callbacks* net_cbs;
};

extern void *netplay_data;

struct netplay_callbacks* netplay_get_cbs_net(void);
struct netplay_callbacks* netplay_get_cbs_spectate(void);
void   netplay_log_connection(const struct sockaddr_storage *their_addr,
      unsigned slot, const char *nick);

bool netplay_get_nickname(netplay_t *netplay, int fd);
bool netplay_send_nickname(netplay_t *netplay, int fd);
bool netplay_send_info(netplay_t *netplay);
uint32_t *netplay_bsv_header_generate(size_t *size, uint32_t magic);
bool netplay_bsv_parse_header(const uint32_t *header, uint32_t magic);
uint32_t netplay_impl_magic(void);
bool netplay_send_info(netplay_t *netplay);
bool netplay_get_info(netplay_t *netplay);
bool netplay_is_server(netplay_t* netplay);
bool netplay_is_spectate(netplay_t* netplay);

#endif