//
// VLC VLM server scol plugin
// Bastien BOURINEAU
//


#include "scolplugin.h"

//#define		_SCOL_DEBUG_

// Scol flag cast
#define scolIntFlag int (__cdecl *)(struct Mmachine *)

// Scol virtual machine
cbmachine	ww;
mmachine	mm;

// Scol object types
int OBJ_TYPE_VLC_STREAM;


/** raise (internal)
  * called on libvlc exception
  * this put the error message in scol logs 
  *
  * return 0
**/
static void raise(libvlc_exception_t * ex)
{
    if (libvlc_exception_raised (ex))
    {
		 MMechostr(0,"VLM Plugin Error: %s\n", libvlc_exception_get_message(ex));
    }
}


/** destroyVlmStream (internal)
  * called on Scol ObjVlmStream object destroy
  * destroy the Scol object and the libvlc media player
  *
  * return 0
**/
int destroyVlmStream(mmachine m,int handsys,int vlmstream)
{
	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, MTOP(vlmstream), 0) ;
	
	if( !VlmOBJStream ) { MMset(m,0,NIL) ; return 0 ; }
	
	libvlc_exception_t ex;
	libvlc_exception_init (&ex);
	
	libvlc_vlm_stop_media(VlmOBJStream->VLCinst, VlmOBJStream->VlmName, &ex);
	raise (&ex);

	libvlc_vlm_del_media(VlmOBJStream->VLCinst, VlmOBJStream->VlmName, &ex);
	raise (&ex);
	
	libvlc_vlm_release (VlmOBJStream->VLCinst, &ex);
	raise (&ex);
	
	libvlc_release (VlmOBJStream->VLCinst);
	
	SAFE_DELETE( VlmOBJStream ) ;	

	MMstore( m, MTOP(vlmstream), 0, NULL ) ;
	return 0;
}


/** _dsVlmStream
  * destroy the Scol vlc media player
  *	fun [ObjVlmStream] I	
  *
  * return 0
**/
int _dsVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_dsVlmStream\n");
#endif

	int vlmstream = MMget(m,0) ;
	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }

	OBJdelTM( m, OBJ_TYPE_VLC_STREAM, vlmstream ) ;

	MMset(m,0,0);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _crVlmStream
  * create a Scol vlc stream
  *	fun [Chn S I S I] ObjVlmStream
  *
  * return Int OBJcreate
**/
int _crVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_crVlmStream\n");
#endif
	char vrate[32], port[5];
	
	int iport = MTOI(MMpull(m));
	int sip = MMpull(m);
	int ivrate = MTOI(MMpull(m));
	int sname = MMget(m,0);
	
	if (ivrate == NIL) ivrate = 128;
	
	ObjVlcStream* VlmOBJStream = new ObjVlcStream();
	if ( VlmOBJStream == NULL || iport == NIL || sname == NIL) { MMset(m,0,NIL) ; return 0 ; }
	
	int vlc = MMmalloc(m,1,TYPETAB) ;
	if ( vlc == NIL ) { SAFE_DELETE( VlmOBJStream ) ; MMset(m,0,NIL) ; return MERRMEM ; }

	VlmOBJStream->m = m;
	
	MMstore( m, vlc, 0, (int)VlmOBJStream ) ;
	MMpush(m, PTOM(vlc)) ;

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);
	
    /* init vlc modules, should be done only once */
    const char * const vlc_args[] = {
				"--reset-config",
				"-I", "dummy",                      /* no interface */
				"--vout", "dummy",					/* we don't want video (output) */          
				"--ignore-config",					/* don't use/overwrite the config */
				"--no-media-library",               /* don't want that */
				"--services-discovery", "",         /* nor that */
				"--no-video-title-show",            /* nor the filename displayed */
				"--plugin-path=plugins/vlc"
			};

	VlmOBJStream->VLCinst = libvlc_new (sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args, &ex);
    raise (&ex);
	
	sprintf(port, "%i", iport);
	sprintf(vrate, "%i", ivrate);

	VlmOBJStream->VlmName = MMstartstr(m,MTOP(sname));
	char *ip = "";
	if (sip == NIL)
		ip = "0.0.0.0";
	else
		ip = MMstartstr(m,MTOP(sip));
		
	// "#transcode{vcodec=FLV1,acodec=mp3}:std{access=http{mime=video/x-flv},dst=0.0.0.0:8080/stream.flv}" 
    char vlc_output[512];
	// ,fps=12,acodec=mp3,samplerate=11000
	strcpy (vlc_output, "#transcode{vcodec=FLV1,scale=1,acodec=mp3,ab=64,channels=1,vb=");
	strcat (vlc_output, vrate);
	strcat (vlc_output, "}:std{access=http{mime=video/x-flv},dst=");
	strcat (vlc_output, ip);
	strcat (vlc_output, ":");
	strcat (vlc_output, port);
	strcat (vlc_output, "/stream.flv}}");

	/*
	strcpy (vlc_output, "#transcode{vcodec=h264,vb=");
	strcat (vlc_output, vrate);
	strcat (vlc_output, ",scale=1,acodec=mp3,ab=64,channels=1}:duplicate{dst=rtp{dst=");
	strcat (vlc_output, ip);
	strcat (vlc_output, ",mux=ts,port=");
	strcat (vlc_output, port);
	strcat (vlc_output, "}}");
*/
	libvlc_vlm_add_broadcast(VlmOBJStream->VLCinst,
			VlmOBJStream->VlmName,
			NULL,
			vlc_output,
			0,
			vlc_args,
			0,
			1,
			&ex);
	raise (&ex);
	
	if(libvlc_exception_raised(&ex)) { MMechostr(0,"_crVlcStream : error on stream init\n"); SAFE_DELETE( VlmOBJStream ) ; MMset(m,0,NIL) ; return 0 ; }
	
	int k = OBJcreate(m, OBJ_TYPE_VLC_STREAM, (int)VlmOBJStream, NIL, NIL);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif
	return k ;
}


/** _setVlmStreamInput
  * create a Scol vlc stream
  *	fun [ObjVlmStream S I I] ObjVlmStream
  *
  * return Int OBJcreate
**/
int _setVlmStreamInput(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_crVlmStream\n");
#endif

	char width[32], height[32];
	
	int iheight = MTOI(MMpull(m));
	int iwidth = MTOI(MMpull(m));
	int sdevice = MMpull(m);
	int vlmstream = MTOP( MMget(m, 0) ) ;

	if (iwidth == NIL) iwidth = 160;
	if (iheight == NIL) iheight = 120;
	
	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);
	
    sprintf(width, "%i", iwidth);
    sprintf(height, "%i", iheight);

	char *device = MMstartstr(m,MTOP(sdevice));

    char vlc_input[512];
	strcpy (vlc_input, "dshow:// :dshow-vdev=\"");
	strcat (vlc_input, device);
	// :dshow-fps=12 
	strcat (vlc_input, "\" :dshow-adev=\"\" :dshow-size=");
	strcat (vlc_input, width);
	strcat (vlc_input, "x");
	strcat (vlc_input, height);

	libvlc_vlm_set_input(VlmOBJStream->VLCinst, VlmOBJStream->VlmName, vlc_input, &ex);
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif
	return 0 ;
}



/** _enableVlmStream
  * enable or disable the media player
  *	fun [ObjVlmStream I] ObjVlmStream
  *
  * return 0
**/
int _enableVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_enableVlmStream\n");
#endif
	
	int state = MTOI(MMpull(m));
	int vlmstream = MTOP( MMget(m, 0) ) ;
	
	if (state == NIL)
		state = 0;
	else
		state = 1;	

	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);
	
	libvlc_vlm_set_enabled(VlmOBJStream->VLCinst, VlmOBJStream->VlmName, state, &ex);
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _playVlmStream
  * play the media player
  *	fun [ObjVlmStream] ObjVlmStream
  *
  * return 0
**/
int _playVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_playVlmStream\n");
#endif
	
	int vlmstream = MTOP( MMget(m, 0) ) ;

	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	libvlc_vlm_play_media (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, &ex);
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _pauseVlmStream
  * pause / play the media player
  *	fun [ObjVlmStream] ObjVlmStream
  *
  * return 0
**/
int _pauseVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_pauseVlmStream\n");
#endif

	int vlmstream = MTOP( MMget(m, 0) ) ;

	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }
	
	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	libvlc_vlm_pause_media (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, &ex);
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _stopVlmStream
  * stop the media player
  *	fun [ObjVlmStream] ObjVlmStream
  *
  * return 0
**/
int _stopVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_stopVlmStream\n");
#endif
	
	int vlmstream = MTOP( MMget(m, 0) ) ;

	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	libvlc_vlm_stop_media (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, &ex);
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _seekVlmStream
  * seek the vlc media player
  *	fun [ObjVlmStream F] F
  * the F param is a -1.0 to 1.0 percentage
  * return the new position
  *
  * return 0
**/
int _seekVlmStream(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_seekVlmStream\n");
#endif
	
	float seek = MTOF (MMpull (m));
	int vlmstream = MTOP( MMget(m, 0) ) ;
	
	if ( vlmstream == NIL || seek == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);
	
	int isseek = libvlc_vlm_get_media_instance_seekable(VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex);
	raise (&ex);

	if ( isseek )
	{
		float pos = libvlc_vlm_get_media_instance_position (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex);
		raise (&ex);
		
		float newpos = (pos + seek);
		if (newpos > 1.0)
			newpos = 1.0;
		else if (newpos < 0.0)
			newpos = 0.0;

		libvlc_vlm_seek_media (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, newpos, &ex);
		raise (&ex);

		MMset(m,0,(FTOM (libvlc_vlm_get_media_instance_position (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex))));
		raise (&ex);
	}
	else
		MMset(m,0, NIL);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _getVlmStreamPosition
  * get the media position
  *	fun [ObjVlmStream F] F
  * the F param is a -1.0 to 1.0 percentage
  * return the new position
  *
  * return 0
**/
int _getVlmStreamPosition(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_getVlmStreamPosition\n");
#endif
	
	float seek = MTOF (MMpull (m));
	int vlmstream = MTOP( MMget(m, 0) ) ;
	
	if ( vlmstream == NIL || seek == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	MMset(m,0,(FTOM (libvlc_vlm_get_media_instance_position (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex))));
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _getVlmStreamTime
  * get the current vlc media player time
  *	fun [ObjVlmStream] I
  * the I return param is the current time pos in ms
  *
  * return 0
**/
int _getVlmStreamTime(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_getVlmStreamTime\n");
#endif
	
	int vlmstream = MTOP( MMget(m, 0) ) ;
	
	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	MMset(m,0,(ITOM ((int)libvlc_vlm_get_media_instance_time (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex))));
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


/** _getVlmStreamDuration
  * get the vlc media player duration in ms
  *	fun [ObjVlmStream] I
  * the I return param is the duration in ms
  *
  * return 0
**/
int _getVlmStreamDuration(mmachine m)
{
#ifdef _SCOL_DEBUG_
	MMechostr(0,"_getVlmStreamDuration\n");
#endif

	int vlmstream = MTOP( MMget(m, 0) ) ;
	
	if ( vlmstream == NIL ) { MMset(m,0,NIL) ; return 0 ; }
	
	ObjVlcStream* VlmOBJStream =(ObjVlcStream*)MMfetch(m, vlmstream, 0) ;
	
	if( !VlmOBJStream || !VlmOBJStream->VLCinst ) { MMset(m,0,NIL) ; return 0 ; }

	libvlc_exception_t ex;
	libvlc_exception_init (&ex);

	MMset(m,0,(ITOM ((int)libvlc_vlm_get_media_instance_length (VlmOBJStream->VLCinst, VlmOBJStream->VlmName, NULL, &ex))));
	raise (&ex);

#ifdef	_SCOL_DEBUG_
	MMechostr(0,"ok\n");
#endif

	return 0;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////																											   ////
///										DECLARATION DES FONCTIONS POUR SCOL											///
////																											   ////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define NbTplPKG	12


//////////////////////////////////////////////////////////////////////////////////////////////
///		Definition des noms de fonction SCOL
//////////////////////////////////////////////////////////////////////////////////////////////
char	*TplName[NbTplPKG] =
{
	"ObjVlmStream",
	
	"_crVlmStream",
	"_dsVlmStream",
	"_setVlmStreamInput",
	"_enableVlmStream",
	"_playVlmStream",
	"_pauseVlmStream",
	"_stopVlmStream",
	"_seekVlmStream",
	"_getVlmStreamDuration",
	"_getVlmStreamPosition",
	"_getVlmStreamTime"
};




//////////////////////////////////////////////////////////////////////////////////////////////
///		Definition des pointeurs sur fonctions
//////////////////////////////////////////////////////////////////////////////////////////////
int (*TplFunc[NbTplPKG])(mmachine m)=
{
	NULL,
	
	_crVlmStream,
	_dsVlmStream,
	_setVlmStreamInput,
	_enableVlmStream,
	_playVlmStream,
	_pauseVlmStream,
	_stopVlmStream,
	_seekVlmStream,
	_getVlmStreamDuration,
	_getVlmStreamPosition,
	_getVlmStreamTime
};



//////////////////////////////////////////////////////////////////////////////////////////////
///		Definition du nombre de paramètres
//////////////////////////////////////////////////////////////////////////////////////////////
int TplNArg[NbTplPKG]=
{
	TYPTYPE,

	5,														// _crVlmStream
	1,														// _dsVlmStream
	4,														// _setVlmStreamInput
	2,														// _enableVlmStream
	1,                                                      // _playVlmStream
	1,                                                      // _pauseVlmStream
	1,                                                      // _stopVlmStream
	2,														// _seekVlmStream
	1,														// _getVlmStreamDuration
	1,														// _getVlmStreamPosition
	1														// _getVlmStreamTime
	
};



//////////////////////////////////////////////////////////////////////////////////////////////
///		Definition des grammaires fontionnelles
//////////////////////////////////////////////////////////////////////////////////////////////
char* TplType[NbTplPKG]=
{
	NULL,

	"fun [Chn S I S I] ObjVlmStream",												// _crVlmStream
	"fun [ObjVlmStream] I",				    										// _dsVlmStream
	"fun [ObjVlmStream S I I] ObjVlmStream",										// _setVlmStreamInput
	"fun [ObjVlmStream I] ObjVlmStream",											// _enableVlmStream
	"fun [ObjVlmStream] ObjVlmStream",				    							// _playVlmStream
	"fun [ObjVlmStream] ObjVlmStream",				    							// _pauseVlmStream
	"fun [ObjVlmStream] ObjVlmStream",				    							// _stopVlmStream
	"fun [ObjVlmStream F] F",														// _seekVlmStream
	"fun [ObjVlmStream] I",															// _getVlmStreamDuration
	"fun [ObjVlmStream] F",															// _getVlmStreamPosition
	"fun [ObjVlmStream] I"															// _getVlmStreamTime

};





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////																											   ////
///											FONCTIONS D'APPEL DE LA DLL												///
////																											   ////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



int LoadVlmPlugin(mmachine m)
{
	int			k;

	//MMechostr(0,"\n" );
	//MMechostr(0,"*************************    VlcPlugin Support   *************************\n" );

	k = PKhardpak(m, "VlcPlugin.pkg", NbTplPKG, TplName, TplFunc, TplNArg, TplType);
	
	OBJ_TYPE_VLC_STREAM = OBJregister( 0, 1, destroyVlmStream, "OBJ_TYPE_VLC_STREAM" ) ;
	

	//MMechostr(0,"**********************  Successfully Loaded  **********************\n" );
	//MMechostr(0,"\n" );

	return k;
}





///////////////////////////////////////////////////////////////
///		Starting point of the DLL							///
///////////////////////////////////////////////////////////////
extern "C" __declspec (dllexport) int SCOLloadVLM(mmachine m, cbmachine w)
{
	ww = w;
	mm = m;

	//MMechostr(MSKDEBUG,"SCOLloadVLM trying loading VlmPlugin DLL ...\n");
	LoadVlmPlugin(m);

	return 0;
}





///////////////////////////////////////////////////////////////
///		Ending point of the DLL								///
///////////////////////////////////////////////////////////////
extern "C" __declspec (dllexport) int SCOLfreeVLM()
{
	MMechostr(MSKDEBUG,"Release VlmPlugin DLL\n");
	
	MMechostr(1, "------ ..VlmPlugin DLL CLOSED\n\n" );

	return 0;
}