/*****************************************************************
 * gmerlin - a general purpose multimedia framework and applications
 *
 * Copyright (c) 2001 - 2012 Members of the Gmerlin project
 * gmerlin-general@lists.sourceforge.net
 * http://gmerlin.sourceforge.net
 *
 * This program 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 Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 * *****************************************************************/

#include <config.h>
#include <gmerlin/utils.h>
#include <gmerlin/log.h>
#include <gmerlin/translation.h>
#include <gmerlin/cfgctx.h>
#include <gmerlin/cmdline.h>
#include <gmerlin/bgplug.h>
#include <gmerlin/subprocess.h>
#include <gmerlin/charset.h>
#include <gmerlin/bgsocket.h>
#include <gmerlin/http.h>
#include <gmerlin/httpserver.h>
#include <gmerlin/diskcache.h>

#include <gmerlin/upnp/device.h>
#include <gmerlin/upnp/ssdp.h>

#include "common.h"

#define CLIENT_STATUS_STARTING     0
#define CLIENT_STATUS_WAIT_START   1
#define CLIENT_STATUS_WAIT_SYNC    2
#define CLIENT_STATUS_RUNNING      3
#define CLIENT_STATUS_DONE         4
#define CLIENT_STATUS_STOP         5

#define CLIENT_TYPE_MEDIA           0 // Media file for upnp devices
#define CLIENT_TYPE_SOURCE_ONDEMAND 1 // Ondemand upstream source or recompressor (bgplug)
#define CLIENT_TYPE_SOURCE_STREAM   2 // Upstream source (bgplug)
#define CLIENT_TYPE_SINK            3 // Sink (bgplug or filtered)
#define CLIENT_TYPE_PROXY           4 // 
#define CLIENT_TYPE_DEVICEPROXY     5

#define PROTOCOL_HTTP               (1<<0)
#define PROTOCOL_RTSP               (1<<1)

#define CLIENT_TIMEOUT              1000

typedef struct server_s server_t;

/*
 *  URL structure:
 *  http://<root>/                      -> http interface
 *  http://<root>/static                -> Static files
 *  http://<root>/upnp/<dev>/desc.xml   -> Device description
 *  http://<root>/upnp/<dev>/<serv>.xml -> Service description
 *  ...
 */

typedef struct client_s
  {
  int type;
  pthread_mutex_t status_mutex;
  pthread_cond_t status_cond;

  int status;
  pthread_t thread;
  int fd;

  char * name;
  int64_t id;
  gavl_time_t idle_time;
  
  void *data;
  void (*free_data)(void*);
  void (*func)(struct client_s * client);

  struct client_s * source;

  int keep_alive;

  gavl_dictionary_t req;
  
  server_t * s;
  } client_t;

client_t * client_create_full(server_t * s, int type, int fd, const gavl_dictionary_t * req, void * data, 
                              void (*free_data)(void*), void (*func)(client_t*), client_t * source);

client_t * client_create(server_t * s, int type, int fd, const gavl_dictionary_t * req, void * data, 
                         void (*free_data)(void*), void (*func)(client_t*));

int client_get_status(client_t *);

void client_set_status(client_t * c, int status);
int client_wait_running(client_t * c);

void client_destroy(server_t * s, client_t * c);

/* Buffer: Passes data from a program source to listeners in a thread save way */

#define BUFFER_TYPE_PACKET      0
#define BUFFER_TYPE_METADATA    1
#define BUFFER_TYPE_SYNC_HEADER 2

typedef struct buffer_element_s
  {
  int type;
  gavl_packet_t p;
  gavl_dictionary_t m;

  int64_t * sh;
  
  int64_t seq; // Sequence number

  struct buffer_element_s * next;
  } buffer_element_t;

void buffer_element_set_sync_header(buffer_element_t * el,
                                    const int64_t * sh, int num);

void buffer_element_set_packet(buffer_element_t * el,
                               const gavl_packet_t * p);

void buffer_element_set_metadata(buffer_element_t * el,
                                 const gavl_dictionary_t * m);

typedef struct buffer_s buffer_t;

buffer_t * buffer_create(int num_elements);

void buffer_destroy(buffer_t *);

buffer_element_t * buffer_get_write(buffer_t *);
void buffer_done_write(buffer_t *);

int buffer_get_read(buffer_t *, int64_t * seq, buffer_element_t ** el);
void buffer_done_read(buffer_t *);

void buffer_wait(buffer_t * b);

void buffer_get_fullness(buffer_t * b, int * num_elements,
                         int * max_elements);

// void buffer_done_read(buffer_t *, buffer_element_t *);

buffer_element_t * buffer_get_first(buffer_t *);

int64_t buffer_get_start_seq(buffer_t *);

void buffer_stop(buffer_t * b);

int buffer_get_free(buffer_t * b);

/* On the fly ID3V2 generation for the more stupid clients */

typedef struct
  {
  int64_t id;
  int len;
  int alloc;
  uint8_t * data;
  gavl_time_t last_used;
  int skip_size;
  const char * transcoder;
  } id3v2_t;

/* Filter: Sends data to the socket */

typedef struct
  {
  int is_supported;
  int (*supported)(bg_plugin_registry_t * plugin_reg);
  
  int (*probe)(int protocol,
               const gavf_program_header_t * ph,
               const gavl_dictionary_t * request);
  
  void * (*create)(server_t * s,
                   const gavf_program_header_t * ph,
                   const gavl_dictionary_t * req,
                   const gavl_dictionary_t * vars,
                   gavl_dictionary_t * res);

  int (*start)(void * priv,
               const gavf_program_header_t * ph,
               const gavl_dictionary_t * req,
               const gavl_dictionary_t * inline_metadata,
               int fd);

  void (*sync)(void * priv,
               const int64_t * pts);
  
  int (*put_buffer)(void * priv, buffer_element_t *);
  void (*destroy)(void * priv);
  } filter_t;

void filter_init();

const filter_t * filter_find(int prpotocol,
                             const gavf_program_header_t * ph,
                             const gavl_dictionary_t * request);

/* Keepalive sockets */

// int ka_push(server_t * s, int * fd);

/* Client */
client_t * source_client_create(server_t * s,
                                int * fd,
                                const gavl_dictionary_t * req,
                                bg_plug_t * plug, int type,
                                bg_subprocess_t * proc, int is_server);

client_t * sink_client_create(server_t * s,
                              int * fd, const gavl_dictionary_t * req,
                              const gavf_program_header_t * ph,
                              const gavl_dictionary_t * inline_metadata,
                              buffer_t * buf, const char * method,
                              int proto, client_t * source);

int sink_init_from_source(client_t * sink, client_t * source);

int sink_client_init(client_t * cl,
                     const gavf_program_header_t * ph,
                     const gavl_dictionary_t * inline_metadata,
                     buffer_t * buf);  

client_t * sink_create_from_source(server_t * s,
                                   int * fd, client_t * source,
                                   const gavl_dictionary_t * req,
                                   const char * method, int proto);

/* Remotedevice proxy */


int device_proxy_handle(bg_http_connection_t * conn, void * p);
int device_proxy_ping(client_t * cl);

/* Server */

struct server_s
  {
  /* Config stuff */
  char * dbpath;
  bg_plugin_registry_t * plugin_reg; // Must be set manually
  bg_cfg_registry_t * cfg_reg;
  
  pthread_mutex_t clients_mutex;
  client_t ** clients;
  int num_clients;
  int clients_alloc;

  const char * root_url; // Owned by http server

  bg_upnp_device_t * dev;
  bg_db_t * db;

  uuid_t uuid;
  char * uuid_str;
  char * upnp_name;
  
  const char * server_string;

  id3v2_t * id3_cache;
  int id3_cache_size;

  bg_socket_address_t * remote_addr;
  
  int rtsp_session_max;

  bg_http_server_t * s;

  bg_parameter_info_t * parameters;

  gavl_time_t current_time;
  char * vardir;
  
  bg_server_storage_t * storage;

  /* How many clients we remember for storing their data */
  int max_client_ids;

  bg_websocket_context_t * ws;
  bg_remote_device_registry_t * remote_reg;
  };

void server_attach_client(server_t*, client_t*cl);

void server_init(server_t*);

int server_start(server_t*);

int server_get_protocol(const gavl_dictionary_t * req);

id3v2_t * server_get_id3(server_t * s, bg_http_connection_t * conn,
                         int64_t id, const char * transcoder, const char * out_mimetype);
id3v2_t * server_get_id3_for_file(server_t * s, bg_http_connection_t * conn,
                                  bg_db_file_t * f);


const bg_parameter_info_t * server_get_parameters(server_t * s);
void server_set_parameter(void * server, const char * name,
                          const gavl_value_t * val);

int server_get_parameter(void * server, const char * name,
                         gavl_value_t * val);

int server_iteration(server_t * s);

void server_cleanup(server_t * s);

/* Media handler (fd will be set to -1 if we launched a
   thread for transferring the file) */

int server_handle_media(bg_http_connection_t * conn, void * data);

int server_handle_pls(bg_http_connection_t * conn, void * data);
int server_handle_m3u(bg_http_connection_t * conn, void * data);
int server_handle_xspf(bg_http_connection_t * conn, void * data);

int server_handle_transcode(bg_http_connection_t * conn, void * data);

int server_handle_source(bg_http_connection_t * conn, void * data);

int server_handle_stream(bg_http_connection_t * conn, void * data);

int server_handle_ondemand(bg_http_connection_t * conn, void * data);

int server_handle_recompress(bg_http_connection_t * conn, void * data);

int server_handle_proxy(bg_http_connection_t * conn, void * data);

char * server_extract_mimetype(const char * pos);

/* RTSP */

typedef enum
  {
    RTSP_STATUS_ERROR = 0,
    RTSP_STATUS_AGAIN = 1, // Call again
    RTSP_STATUS_SDP   = 2, // Sent SDP
    RTSP_STATUS_SETUP = 3, // Setup done
    RTSP_STATUS_PLAY  = 4, // Sent play command
  } rtsp_status_t;

rtsp_status_t rtsp_handshake_1(const server_t * s, int fd,
                     const gavl_dictionary_t * first_req,
                     const gavf_program_header_t * ph);

rtsp_status_t rtsp_handshake_2(const server_t * s, void * ctx, int fd,
                     const gavf_program_header_t * ph);

char * create_sdp(const server_t * s, const gavf_program_header_t * ph,
                  const gavl_dictionary_t * req);

/* Shoutcast metadata generator */

typedef struct
  {
  int metaint;
  int byte_counter;
  bg_charset_converter_t * cnv;
  char * buffer;
  int buffer_alloc;
  int buffer_len;
  } icy_t;

void icy_update_metadata(icy_t * m, const gavl_dictionary_t * met);

void icy_init(icy_t * m,
              const gavf_program_header_t * ph,
              const gavl_dictionary_t * req,
              const gavl_dictionary_t * inline_metadata,
              gavl_dictionary_t * res);

int icy_write(icy_t * m,
              const uint8_t * data, int len,
              gavf_io_t * io);

void icy_free(icy_t * m);


/*
 *  Websocket stuff. All constants need to replicated in
 *  web/js/utils.js
 */

/*
 *  arg0: name 
 */

#define SERVER_WS_MSG_IP    0

/*
 *  arg0: 1 = add, 0 = remove
 *  arg1: type
 *  arg2: version
 *  arg3: url
 */

#define SERVER_WS_MSG_SSDP  1

/*
 *  arg0: Subscription ID
 *  arg1: xml
 */

#define SERVER_WS_MSG_EVENT 2

