/*****************************************************************
 * 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 "server.h"
#include "rtp.h"
#include <gmerlin/http.h>
#include <string.h>
#include <unistd.h>

int rtp_supported()
  {
  return 1;
  }
  
int rtp_probe(int protocol,
              const gavf_program_header_t * ph,
              const gavl_dictionary_t * request)
  {
  if(protocol == PROTOCOL_RTSP)
    return 1;
  else
    return 0;
  }
  
void * rtp_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 i;
  rtp_context_t * ret = calloc(1, sizeof(*ret));

  ret->session = ++s->rtsp_session_max;
  
  ret->num_streams = ph->num_streams;
  ret->streams = calloc(ret->num_streams, sizeof(*ret->streams));

  for(i = 0; i < ret->num_streams; i++)
    {
    ret->streams[i].sh = ph->streams + i;
    
    }
  
  return ret;
  }

int rtp_start(void * priv,
              const gavf_program_header_t * ph,
              const gavl_dictionary_t * req,
              const gavl_dictionary_t * inline_metadata,
              int fd)
  {
  rtp_context_t * ctx = priv;
  ctx->fd = fd;
  return 1;
  }

int rtp_put_buffer(void * priv, buffer_element_t * buf)
  {
  return 0;
  }

void rtp_destroy(void * priv)
  {
  int i;
  rtp_context_t * ctx = priv;
  for(i = 0; i < ctx->num_streams; i++)
    rtp_packet_free(&ctx->streams[i].p);
  free(ctx->streams);
  }

rtsp_status_t
rtp_stream_setup(const server_t * srv,
                 rtp_context_t * ctx,
                 rtp_stream_t * s,
                 const gavl_dictionary_t * req,
                 gavl_dictionary_t * res)
  {
  int i = 0;
  unsigned int client_ports[2] = { 0, 0 };
  unsigned int server_ports[2] = { 0, 0 };
  const char * transport;
  char ** transport_params;
  rtsp_status_t result = RTSP_STATUS_ERROR;
  bg_socket_address_t * addr = NULL;
  
  transport = gavl_dictionary_get_string_i(req, "Transport");
  if(!transport)
    {
    bg_http_response_init(res, "RTSP/1.0", 400, "Bad Request");
    goto end;
    }

  transport_params = bg_strbreak(transport, ';');

  /* RTP over UDP */
  if(!strcasecmp(transport_params[0], "RTP/AVP") ||
     !strcasecmp(transport_params[0], "RTP/AVP/UDP"))
    {
    addr = bg_socket_address_create();
    
    i = 1;
    while(transport_params[i])
      {
      if(!strncmp(transport_params[i], "client_port=", 12))
        {
        if((sscanf(transport_params[i] + 12,
                  "%u-%u", &client_ports[0], &client_ports[1]) < 2) ||
           (client_ports[1] - client_ports[0] > 1))
          {
          bg_http_response_init(res, "RTSP/1.0", 461, "Unsupported Transport");
          result = RTSP_STATUS_AGAIN;
          goto end;
          }
        fprintf(stderr, "Got UDP ports: %d %d\n",
                client_ports[0], client_ports[1]);
        break;
        }
      i++;
      }
    
    if(client_ports[0] == 0)
      {
      bg_http_response_init(res, "RTSP/1.0", 461, "Unsupported Transport");
      result = RTSP_STATUS_AGAIN;
      goto end;
      }

    /* Figure out local ports and create UDP sockets */
    
    bg_socket_address_copy(addr, bg_http_server_get_address(srv->s));

    s->udp_fds[0] = -1;
    s->udp_fds[1] = -1;

    while(1)
      {
      bg_socket_address_set_port(addr, 0);

      s->udp_fds[0] = bg_udp_socket_create(addr);
      
      if(s->udp_fds[0] < 0) // failed
        break;
  
      server_ports[0] = bg_socket_address_get_port(addr);
    
      if(server_ports[0] % 2) // Got the higher port
        {
        server_ports[1] = server_ports[0];
        s->udp_fds[1]   = s->udp_fds[0];
        s->udp_fds[0] = -1;
        server_ports[0] = server_ports[1]-1;

        bg_socket_address_set_port(addr, server_ports[0]);
        s->udp_fds[0] = bg_udp_socket_create(addr);
         
        if(s->udp_fds[0] < 0) // failed
          {
          close(s->udp_fds[1]);
          s->udp_fds[1] = -1;
          continue;
          }
        break;
        }
      else
        {
        server_ports[1] = server_ports[0]+1;
        bg_socket_address_set_port(addr, server_ports[1]);
        s->udp_fds[1] = bg_udp_socket_create(addr);

        if(s->udp_fds[1] < 0) // failed
          {
          close(s->udp_fds[0]);
          s->udp_fds[0] = -1;
          continue;
          }
        break;
        }
      }
    if((s->udp_fds[0] >= 0) && (s->udp_fds[1] >= 0))
      {
      /* Connect the UDP sockets */
      bg_socket_address_copy(addr, srv->remote_addr);
      bg_socket_address_set_port(addr, client_ports[0]);
      bg_udp_socket_connect(s->udp_fds[0], addr);
      bg_socket_address_set_port(addr, client_ports[1]);
      bg_udp_socket_connect(s->udp_fds[1], addr);

      bg_http_response_init(res, "RTSP/1.0", 200, "OK");
      gavl_dictionary_set_int(res, "Session", ctx->session);
      gavl_dictionary_set_string_nocopy(res, "Transport", bg_sprintf("%s;server_port=%d-%d", 
                              transport, server_ports[0], server_ports[1]));
      }
    }
  
  /* RTP over TCP */
  else if(!strcasecmp(transport_params[0], "RTP/AVP/TCP"))
    {
    bg_http_response_init(res, "RTSP/1.0", 200, "OK");
    gavl_dictionary_set_string(res, "Transport", transport);
    gavl_dictionary_set_int(res, "Session", ctx->session);
    result = RTSP_STATUS_SETUP;
    }
  else
    {
    bg_http_response_init(res, "RTSP/1.0", 461, "Unsupported Transport");
    result = RTSP_STATUS_AGAIN;
    }

  end:

  if(addr)
    bg_socket_address_destroy(addr);
  
  bg_strbreak_free(transport_params);
  return result;
  }

int rtp_sync(void * priv, int64_t * pts)
  {
  // rtp_context_t * ctx = priv;
  
  return 0;
  }
