/*
-----------------------------------------------------------------------------
This source file is part of OpenSpace3D
For the latest info, see http://www.openspace3d.com

Copyright (c) 2010 I-maginer

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

You may alternatively use this source under the terms of a specific version of
the OpenSpace3D Unrestricted License provided you have obtained such a license from
I-maginer.
-----------------------------------------------------------------------------
*/

/*! \class ObjVlcMediaPlayer
*  \brief 
*  \
*  \version 1.0
*  \date may 2011
*/

#include "ObjVlcMediaPlayer.h"

extern int VLCMEDIA_EVENT;

VlcSingleton::VlcSingleton()
{
  char const *vlc_argv[] =  {
	"--no-xlib",
	"--plugin-path=plugins/vlc",
  "--no-video-title-show",
  "--swscale-mode=0"
  };
  
  int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);
  VLCinst = libvlc_new (vlc_argc, vlc_argv);
}

VlcSingleton::~VlcSingleton()
{
  // destroy cAudio manager
  if (VLCinst != 0)
  {
    libvlc_release(VLCinst);
    VLCinst = 0; 
  }
}

libvlc_instance_t* VlcSingleton::GetInstance()
{
  if (NULL == _singleton)
  {
    _singleton = new VlcSingleton;
  }

  return _singleton->VLCinst;
}

void VlcSingleton::Kill()
{
  SAFE_DELETE(_singleton);
}

// Initialize singleton
VlcSingleton* VlcSingleton::_singleton = NULL;


void ObjVlcMediaPlayer::CommonConstructor()
{
  VLCplayer = 0;
  VLCeventManager = 0;
  bVmem = false;
  bVlm = false;
  bLoop = true;
  VlmName = "";

  /* Create a media player playing environement */
  VLCplayer = libvlc_media_player_new(VlcSingleton::GetInstance());

  VLCeventManager = libvlc_media_player_event_manager(VLCplayer);

	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerEncounteredError, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerPositionChanged, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerBuffering, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerPaused, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerStopped, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerForward, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerBackward, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerEndReached, _mediaEventCallback, this);
	libvlc_event_attach(VLCeventManager, libvlc_MediaPlayerTimeChanged, _mediaEventCallback, this);
}

ObjVlcMediaPlayer::ObjVlcMediaPlayer()
{
  CommonConstructor();
}

ObjVlcMediaPlayer::ObjVlcMediaPlayer(PtrObjBitmap bitmap, int width, int height, int pitch)
{
  CommonConstructor();
  
  bVmem = true;

  libvlc_video_set_callbacks(VLCplayer, lock, unlock, display, bitmap);
  libvlc_video_set_format(VLCplayer, "RV24", width, height, pitch);
}

ObjVlcMediaPlayer::ObjVlcMediaPlayer(std::string ip, int port, std::string device, std::string name, int width, int height, int rate)
{
  char swidth[32], sheight[32], srate[32], sport[5];
  CommonConstructor();

  bVlm = true;
  VlmName = name;
  
  sprintf_s(sport, 32, "%i", port);
  sprintf_s(swidth, 32, "%i", width);
  sprintf_s(sheight, 32, "%i", height);
  sprintf_s(srate, 32, "%i", rate);

  char vlc_input[512];
	strcpy (vlc_input, "dshow:// :dshow-vdev=\"");
  strcat (vlc_input, device.c_str());
	strcat (vlc_input, " :dshow-adev=\"\" :dshow-size=");
	strcat (vlc_input, swidth);
	strcat (vlc_input, "x");
	strcat (vlc_input, sheight);

  char vlc_output[512];
	strcpy (vlc_output, "#transcode{vcodec=h264,vb=");
	strcat (vlc_output, srate);
	strcat (vlc_output, ",scale=1,acodec=mp3,ab=64,channels=1}:duplicate{dst=rtp{dst=");
  strcat (vlc_output, ip.c_str());
	strcat (vlc_output, ",mux=ts,port=");
	strcat (vlc_output, sport);
	strcat (vlc_output, "}}");

	libvlc_vlm_add_broadcast(VlcSingleton::GetInstance(), VlmName.c_str(), vlc_input, vlc_output, 0, 0, 0, 1);
	
	//libvlc_vlm_set_input(VlcSingleton::GetInstance(), VlmName, vlc_input, &ex);
	//

	libvlc_vlm_set_enabled(VlcSingleton::GetInstance(), VlmName.c_str(), 1);

  libvlc_vlm_play_media(VlcSingleton::GetInstance(), VlmName.c_str());
}

ObjVlcMediaPlayer::~ObjVlcMediaPlayer()
{
  // Stop media if playing (maybe more stable ?)
  if (isVlm())
  {
    libvlc_vlm_stop_media(VlcSingleton::GetInstance(), VlmName.c_str());
	  libvlc_vlm_del_media(VlcSingleton::GetInstance(), VlmName.c_str());
  }
  else
  {
	  libvlc_media_player_stop (VLCplayer);
  }

  if ( VLCeventManager != NULL )
  {
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerEncounteredError, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerPositionChanged, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerBuffering, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerPaused, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerStopped, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerForward, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerBackward, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerEndReached, _mediaEventCallback, this);
	  libvlc_event_detach(VLCeventManager, libvlc_MediaPlayerTimeChanged, _mediaEventCallback, this);

	  VLCeventManager = NULL;
  }
	
  if (isVlm())
  {
	  libvlc_vlm_release(VlcSingleton::GetInstance());
  }

  /* Free the media_player */
  libvlc_media_player_release(VLCplayer);
}

libvlc_media_player_t* ObjVlcMediaPlayer::getMediaPlayer()
{
  return VLCplayer;
}


libvlc_instance_t* ObjVlcMediaPlayer::getVlcInstance()
{
  return VlcSingleton::GetInstance();
}

bool ObjVlcMediaPlayer::isVmem()
{
  return bVmem;
}

bool ObjVlcMediaPlayer::isVlm()
{
  return bVlm;
}

std::string ObjVlcMediaPlayer::getVlmName()
{
  return VlmName;
}

void ObjVlcMediaPlayer::setWindow(HWND win)
{
  libvlc_media_player_set_hwnd(VLCplayer, (void*)win);
}

void ObjVlcMediaPlayer::setFullScreen(bool state)
{
  libvlc_set_fullscreen(VLCplayer, state ? 1 : 0);
}

bool ObjVlcMediaPlayer::getFullScreenState()
{
  bool ret = false;
  ret = (libvlc_get_fullscreen(VLCplayer) == 1) ? true : false;
  return ret;
}

void ObjVlcMediaPlayer::toggleFullScreen()
{
  libvlc_toggle_fullscreen(VLCplayer);
}

void ObjVlcMediaPlayer::setMediaPath(std::string path)
{
  /* Create a new item */
  libvlc_media_t *media = libvlc_media_new_path(VlcSingleton::GetInstance(), path.c_str());

	libvlc_media_player_set_media(VLCplayer, media);

  /* No need to keep the media now */
	libvlc_media_release(media);
}

void ObjVlcMediaPlayer::play()
{
	/* stop the player if end status */
	libvlc_state_t state = libvlc_media_player_get_state(VLCplayer);
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);

  /* play the media_player */
	if (media != NULL)
	{
    if ((int)state == libvlc_Ended)
      libvlc_media_player_stop(VLCplayer);

		libvlc_media_player_play(VLCplayer);
	}
  
	libvlc_media_release(media);
}

void ObjVlcMediaPlayer::pause()
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);

	int canpause = libvlc_media_player_can_pause(VLCplayer);
	
  /* pause the media_player */
	if ((media != 0) && canpause)
		libvlc_media_player_pause(VLCplayer);

	libvlc_media_release(media);
}

void ObjVlcMediaPlayer::stop()
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);

	
  /* stop the media_player */
	if (media != 0)
    libvlc_media_player_stop(VLCplayer);

	libvlc_media_release(media);
}

bool ObjVlcMediaPlayer::seek(float value)
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
  int isseek = libvlc_media_player_is_seekable(VLCplayer);

	if (!isseek || media == 0)
	{
    libvlc_media_release(media);
    return false;
  }

	float pos = libvlc_media_player_get_position(VLCplayer);
	
	float newpos = (pos + value);
	if (newpos > 1.0)
		newpos = 1.0;
	else if (newpos < 0.0)
		newpos = 0.0;

	libvlc_media_player_set_position(VLCplayer, newpos);

	libvlc_media_release(media);
  return true;
}

bool ObjVlcMediaPlayer::setPosition(float value)
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
  int isseek = libvlc_media_player_is_seekable(VLCplayer);

	if (!isseek || media == 0)
	{
    libvlc_media_release(media);
    return false;
  }

	if (value > 1.0)
		value = 1.0;
	else if (value < 0.0)
		value = 0.0;

	libvlc_media_player_set_position(VLCplayer, value);

	libvlc_media_release(media);
  return true;
}

float ObjVlcMediaPlayer::getPosition()
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
	float pos = 0.0f;
  
  if (media != 0)
    pos = libvlc_media_player_get_position(VLCplayer);

	libvlc_media_release(media);
  return pos;
}

bool ObjVlcMediaPlayer::setTime(int value)
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
  int isseek = libvlc_media_player_is_seekable(VLCplayer);

	if (!isseek || media == 0)
	{
    libvlc_media_release(media);
    return false;
  }
  
	int maxtime = libvlc_media_player_get_length(VLCplayer);

	if (value > maxtime)
		value = maxtime;
	else if (value < 0)
		value = 0;

  libvlc_media_player_set_time(VLCplayer, value);

	libvlc_media_release(media);
  return true;
}

int ObjVlcMediaPlayer::getTime()
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
	int pos = 0;
  
  if (media != 0)
    pos = libvlc_media_player_get_time(VLCplayer);

	libvlc_media_release(media);
  return pos;
}

int ObjVlcMediaPlayer::getDuration()
{
	libvlc_media_t* media = libvlc_media_player_get_media(VLCplayer);
	int lenght = 0;
  
  if (media != 0)
    lenght = libvlc_media_player_get_length(VLCplayer);

	libvlc_media_release(media);
  return lenght;
}

int ObjVlcMediaPlayer::getState()
{
  int state = 0;
  if (VLCplayer)
    state = libvlc_media_player_get_state(VLCplayer);

  return state;
}

void ObjVlcMediaPlayer::setVolume(int vol)
{
  if (VLCplayer)
    libvlc_audio_set_volume(VLCplayer, vol);
}

int ObjVlcMediaPlayer::getVolume()
{
  int vol = 0;
  if (VLCplayer)
    vol = libvlc_audio_get_volume(VLCplayer);

  return vol;
}

void ObjVlcMediaPlayer::setMute(bool state)
{
  if (VLCplayer)
    libvlc_audio_set_mute(VLCplayer, state ? 1 : 0);
}

bool ObjVlcMediaPlayer::getMute()
{
  bool ret = false;
  if (VLCplayer)
    ret = (libvlc_audio_get_mute(VLCplayer) == 1) ? true : false;

  return ret;
}

void ObjVlcMediaPlayer::setLoop(bool state)
{
  bLoop = state;
}

bool ObjVlcMediaPlayer::getLoop()
{
  return bLoop;
}

void *ObjVlcMediaPlayer::lock(void *data, void **p_pixels)
{
  PtrObjBitmap B = (PtrObjBitmap)data;
  if(!(B->Flags & BUFFER_FLAG_MUTEX))
  {
    B->Flags = B->Flags|BUFFER_FLAG_MUTEX;
    if (B->bits != 0)
    {
      *p_pixels = B->bits;
    }
    else
    {
      MMechostr(MSKDEBUG, "VLC plugin : bitmap buffer already destroyed");
      *p_pixels = 0;
    }
  }
  return NULL; /* picture identifier, not needed here */
}

void ObjVlcMediaPlayer::unlock(void *data, void *id, void *const *p_pixels)
{
  PtrObjBitmap B = (PtrObjBitmap)data;
  B->Flags = B->Flags&(~BUFFER_FLAG_MUTEX);
    /* VLC just rendered the video, but we can also render stuff */
}

void ObjVlcMediaPlayer::display(void *data, void *id)
{
    /* VLC wants to display the video */
    (void) data;
}

static void _mediaEventCallback(const libvlc_event_t *ev, void *p)
{
  PostMessage ((HWND)SCgetExtra("hscol"), VLCMEDIA_EVENT, (WPARAM)p, (LPARAM)ev->type);
}