/*
 *      Copyright (C) 2005-2013 Team XBMC
 *      http://xbmc.org
 *
 *  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, 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 XBMC; see the file COPYING.  If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS)
  #include "config.h"
#endif
#ifdef TARGET_WINDOWS
#include "system.h" // just for HAS_LIBRTMP
#endif

#ifdef HAS_LIBRTMP
#include "settings/AdvancedSettings.h"
#include "DVDInputStreamRTMP.h"
#include "threads/SingleLock.h"
#include "utils/log.h"
#include "utils/Variant.h"

#include <string>

using namespace XFILE;

static int RTMP_level=0;
extern "C" 
{
  static void CDVDInputStreamRTMP_Log(int level, const char *fmt, va_list args)
  {
    char buf[2048];

    if (level > RTMP_level)
      return;

    switch(level) 
    {
      default:
      case RTMP_LOGCRIT:    level = LOGFATAL;   break;
      case RTMP_LOGERROR:   level = LOGERROR;   break;
      case RTMP_LOGWARNING: level = LOGWARNING; break;
      case RTMP_LOGINFO:    level = LOGNOTICE;  break;
      case RTMP_LOGDEBUG:   level = LOGINFO;    break;
      case RTMP_LOGDEBUG2:  level = LOGDEBUG;   break;
    }

    vsnprintf(buf, sizeof(buf), fmt, args);
    CLog::Log(level, "%s", buf);
  }
}

CDVDInputStreamRTMP::CDVDInputStreamRTMP()
  : CDVDInputStream(DVDSTREAM_TYPE_RTMP)
  , m_canSeek(true)
  , m_canPause(true)
{
  if (m_libRTMP.Load())
  {
    CLog::Log(LOGINFO,"%s: Using external libRTMP",__FUNCTION__);
    RTMP_LogLevel level;

    m_libRTMP.LogSetCallback(CDVDInputStreamRTMP_Log);
    switch (g_advancedSettings.m_logLevel)
    {
      case LOG_LEVEL_DEBUG_FREEMEM:
      case LOG_LEVEL_DEBUG: level = RTMP_LOGDEBUG; break;
      case LOG_LEVEL_NORMAL: level = RTMP_LOGINFO; break;
      default: level = RTMP_LOGCRIT; break;
    }

    if (g_advancedSettings.CanLogComponent(LOGRTMP))
      level = RTMP_LOGDEBUG2;

    m_libRTMP.LogSetLevel(level);
    RTMP_level = level;
    
    m_rtmp = m_libRTMP.Alloc();
    m_libRTMP.Init(m_rtmp);
  }
  else
  {
    m_rtmp = NULL;
  }

  m_eof = true;
  m_bPaused = false;
  m_sStreamPlaying = NULL;
}

CDVDInputStreamRTMP::~CDVDInputStreamRTMP()
{
  free(m_sStreamPlaying);
  m_sStreamPlaying = NULL;

  Close();
  if (m_rtmp)
    m_libRTMP.Free(m_rtmp);
  m_rtmp = NULL;
  m_bPaused = false;
}

bool CDVDInputStreamRTMP::IsEOF()
{
  return m_eof;
}

#define  SetAVal(av, cstr)  av.av_val = (char *)cstr.c_str(); av.av_len = cstr.length()
#undef AVC
#define AVC(str)  {(char *)str,sizeof(str)-1}

/* librtmp option names are slightly different */
static const struct {
 const char *name;
 AVal key;
} options[] = {
 { "SWFPlayer", AVC("swfUrl") },
 { "PageURL", AVC("pageUrl") },
 { "PlayPath", AVC("playpath") },
 { "TcUrl", AVC("tcUrl") },
 { "IsLive", AVC("live") },
 { NULL }
};

bool CDVDInputStreamRTMP::Open(const char* strFile, const std::string& content, bool contentLookup)
{
  if (m_sStreamPlaying)
  {
    free(m_sStreamPlaying);
    m_sStreamPlaying = NULL;
  }

  if (!m_rtmp || !CDVDInputStream::Open(strFile, "video/x-flv", contentLookup))
    return false;

  CSingleLock lock(m_RTMPSection);

  // libRTMP can and will alter strFile, so take a copy of it
  m_sStreamPlaying = (char*)calloc(strlen(strFile)+1,sizeof(char));
  strcpy(m_sStreamPlaying,strFile);

  if (!m_libRTMP.SetupURL(m_rtmp, m_sStreamPlaying))
    return false;

  /* Look for protocol options in the URL.
   * Options are added to the URL in space separated key=value pairs.
   * We are only interested in the "live" option to disable seeking,
   * the rest is handled by librtmp internally
   *
   * example URL suitable for use with RTMP_SetupURL():
   * "rtmp://flashserver:1935/ondemand/thefile swfUrl=http://flashserver/player.swf swfVfy=1 live=1"
   * details: https://rtmpdump.mplayerhq.hu/librtmp.3.html
   */
  std::string url = strFile;
  size_t iPosBlank = url.find(' ');
  if (iPosBlank != std::string::npos && (url.find("live=true") != std::string::npos || url.find("live=1") != std::string::npos))
  {
    m_canSeek = false;
    m_canPause = false;
  }
  CLog::Log(LOGDEBUG, "RTMP canseek: %s", m_canSeek ? "true" : "false");

  /* SetOpt and SetAVal copy pointers to the value. librtmp doesn't use the values until the Connect() call,
   * so value objects must stay allocated until then. To be extra safe, keep the values around until Close(),
   * in case librtmp needs them again.
   */
  m_optionvalues.clear();
  for (int i=0; options[i].name; i++)
  {
    std::string tmp = m_item.GetProperty(options[i].name).asString();
    if (!tmp.empty())
    {
      m_optionvalues.push_back(tmp);
      AVal av_tmp;
      SetAVal(av_tmp, m_optionvalues.back());
      m_libRTMP.SetOpt(m_rtmp, &options[i].key, &av_tmp);
    }
  }

  if (!m_libRTMP.Connect(m_rtmp, NULL) || !m_libRTMP.ConnectStream(m_rtmp, 0))
    return false;

  m_eof = false;

  return true;
}

// close file and reset everything
void CDVDInputStreamRTMP::Close()
{
  CSingleLock lock(m_RTMPSection);
  CDVDInputStream::Close();

  if (m_rtmp)
    m_libRTMP.Close(m_rtmp);

  m_optionvalues.clear();
  m_eof = true;
  m_bPaused = false;
}

int CDVDInputStreamRTMP::Read(uint8_t* buf, int buf_size)
{
  if (!m_rtmp)
    return -1;

  int i = m_libRTMP.Read(m_rtmp, (char *)buf, buf_size);
  if (i < 0)
    m_eof = true;

  return i;
}

int64_t CDVDInputStreamRTMP::Seek(int64_t offset, int whence)
{
  if (whence == SEEK_POSSIBLE)
    return 0;
  else
    return -1;
}

bool CDVDInputStreamRTMP::SeekTime(int iTimeInMsec)
{
  CLog::Log(LOGNOTICE, "RTMP Seek to %i requested", iTimeInMsec);
  CSingleLock lock(m_RTMPSection);

  if (m_rtmp && m_libRTMP.SendSeek(m_rtmp, iTimeInMsec))
    return true;

  return false;
}

int64_t CDVDInputStreamRTMP::GetLength()
{
  return -1;
}

bool CDVDInputStreamRTMP::Pause(double dTime)
{
  CSingleLock lock(m_RTMPSection);

  m_bPaused = !m_bPaused;

  CLog::Log(LOGNOTICE, "RTMP Pause %s requested", m_bPaused ? "TRUE" : "FALSE");

  if (m_rtmp)
    m_libRTMP.Pause(m_rtmp, m_bPaused);

  return true;
}
#endif
