/*****************************************************************
 * 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 <string.h>


#include "server.h"
#include "rtp.h"

#include <gmerlin/http.h>
#define LOG_DOMAIN "rtsp"

static rtsp_status_t
server_handle_rtsp(const server_t * s, int fd,
                   const gavl_dictionary_t * req,
                   const gavf_program_header_t * ph,
                   rtp_context_t * rtp)
  {
  rtsp_status_t  result = RTSP_STATUS_ERROR;
  int cseq;
  int i;
  gavl_dictionary_t res;
  const char * method = bg_http_request_get_method(req);

  gavl_dictionary_init(&res);
  
  if(!gavl_dictionary_get_int_i(req, "CSeq", &cseq))
    {
    bg_http_response_init(&res, "RTSP/1.0", 400, "Bad Request");
    bg_http_response_write(fd, &res);
    goto end;
    }
  
  if(!strcmp(method, "OPTIONS"))
    {
    bg_http_response_init(&res, "RTSP/1.0", 200, "OK");
    gavl_dictionary_set_int(&res, "CSeq", cseq);
    gavl_dictionary_set_string(&res, "Public", "DESCRIBE, SETUP, PLAY, TEARDOWN");
    bg_http_response_write(fd, &res);
    result = RTSP_STATUS_AGAIN;
    }
  else if(!strcmp(method, "DESCRIBE"))
    {
    /* Generate sdp */
    int sdp_len;
    char * sdp = create_sdp(s, ph, req);
    
    if(!sdp)
      {
      bg_http_response_init(&res, "RTSP/1.0", 404, "Not Found");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      }
    else
      {
      sdp_len = strlen(sdp);
      bg_http_response_init(&res, "RTSP/1.0", 200, "OK");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      gavl_dictionary_set_string(&res, "Content-Type", "application/sdp");
      gavl_dictionary_set_int(&res, "Content-Length", sdp_len);

      gavl_dictionary_set_string_nocopy(&res, "Content-base",
                              bg_sprintf("%s/",
                                         bg_http_request_get_path(req))); 
      
      if(bg_http_response_write(fd, &res) &&
         (bg_http_write_data(fd, (const uint8_t *)sdp, sdp_len, 0) == sdp_len))
        result = RTSP_STATUS_SDP;
      free(sdp);
      }
    
    }
  else if(!strcmp(method, "SETUP"))
    {
    /* Negotiate transport */
    rtp_stream_t * st = NULL;
    int stream_id;
    const char * var;
    const char * pos;
    
    if(!rtp)
      {
      bg_http_response_init(&res, "RTSP/1.0", 400, "Bad Request");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      goto end;
      }

    /* Get the stream */
    if(!(var = bg_http_request_get_path(req)) ||
       !(pos = strrchr(var, '=')))
      {
      bg_http_response_init(&res, "RTSP/1.0", 459,
                            "Aggregate Operation not Allowed");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      goto end;
      }

    pos++;
    stream_id = atoi(pos);

    for(i = 0; i < rtp->num_streams; i++)
      {
      if(rtp->streams[i].sh->id == stream_id)
        {
        st = &rtp->streams[i];
        break;
        }
      }

    if(!st)
      {
      bg_http_response_init(&res, "RTSP/1.0", 400, "Bad Request");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      goto end;
      }
    result = rtp_stream_setup(s, rtp, st, req, &res);
    gavl_dictionary_set_int(&res, "CSeq", cseq);
    bg_http_response_write(fd, &res);
    }
  else if(!strcmp(method, "PLAY"))
    {
    /* Start sending packets */
    const char * var;
    
    if(!(var = bg_http_request_get_path(req)) ||
       (strrchr(var, '=')))
      {
      bg_http_response_init(&res, "RTSP/1.0", 460,
                            "Only Aggregate Operation Allowed");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      goto end;
      }
    else if(!rtp)
      {
      bg_http_response_init(&res, "RTSP/1.0", 400, "Bad Request");
      gavl_dictionary_set_int(&res, "CSeq", cseq);
      bg_http_response_write(fd, &res);
      goto end;
      }
    else
      {
      bg_http_response_init(&rtp->play_response, "RTSP/1.0", 200, "OK");
      gavl_dictionary_set_int(&rtp->play_response, "CSeq", cseq);
      gavl_dictionary_set_int(&rtp->play_response, "Session",
                            rtp->session);
      // RTP-Info is missing at this point (missing timestamps). We'll
      // Complete this in rtp_sync()
      result = RTSP_STATUS_PLAY;
      }
    }

  end:
  
  gavl_dictionary_free(&res);
  return result;
  }

/* Called before the filter is created */
rtsp_status_t rtsp_handshake_1(const server_t * s, int fd,
                                const gavl_dictionary_t * first_req,
                                const gavf_program_header_t * ph)
  {
  int done = 0;
  rtsp_status_t result;
  gavl_dictionary_t req;
  gavl_dictionary_init(&req);
  gavl_dictionary_copy(&req, first_req);

  while(!done)
    {
    result = server_handle_rtsp(s, fd, &req, ph, NULL);
    
    switch(result)
      {
      case RTSP_STATUS_ERROR:
        done = 1;
        break;
      case RTSP_STATUS_AGAIN:
        break;
      case RTSP_STATUS_SDP:
        done = 1;
        break;
      case RTSP_STATUS_SETUP:
        break;
      case RTSP_STATUS_PLAY:
        result = RTSP_STATUS_ERROR; // Can't be yet
        done = 1;
        break;
      }

    if(done)
      break;
    
    gavl_dictionary_free(&req);
    gavl_dictionary_init(&req);

    if(!bg_http_request_read(fd, &req, CLIENT_TIMEOUT))
      {
      result = RTSP_STATUS_ERROR;
      break;
      }
    }
  gavl_dictionary_free(&req);
  return !!result;
  }

/* Called after the filter is created */
rtsp_status_t rtsp_handshake_2(const server_t * s, void * ctx, int fd,
                               const gavf_program_header_t * ph)
  {
  int done = 0;
  rtsp_status_t result = RTSP_STATUS_ERROR;
  gavl_dictionary_t req;
  rtp_context_t * rtp = ctx;

  gavl_dictionary_init(&req);
  
  while(!done)
    {
    if(!bg_http_request_read(fd, &req, CLIENT_TIMEOUT))
      break;
    
    result = server_handle_rtsp(s, fd, &req, ph, rtp);

    switch(result)
      {
      case RTSP_STATUS_ERROR:
        break;
      case RTSP_STATUS_AGAIN:
        break;
      case RTSP_STATUS_SDP:
        break;
      case RTSP_STATUS_SETUP:
        break;
      case RTSP_STATUS_PLAY:
        done = 1;
        break;
      }
    
    gavl_dictionary_free(&req);
    gavl_dictionary_init(&req);
    }
  
  gavl_dictionary_free(&req);
  return !!result;
  }
