/*
-----------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------
*/

/*
 Toolkit based on OpenCV library
 First version : dec 2010
 Author : Bastien BOURINEAU
*/

/*! @defgroup grpcapture Scol functions definition
 *  Scol functions definition
 *  @{
 */
/** @} */

#include "prerequisites.h"
#include "CameraInput.h"
#include "ScolConvert.h"
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <dshow.h>


// OBJCAPTURE Scol
int OBJCAPTURESCOL;

// CALLBACK
int NBCAPTURECB = 0;

using namespace cv;


/*! \mainpage Capture Scol Plugin
 *
 * \section intro_sec Introduction
 * This plugin allow Scol Virtual Machine to use Capture devices by OpenCV library
 * 
 */
/*
 *********************************************
 * CAMERA PART
 *********************************************
 */
int destroyCaptureObj(mmachine m,int handsys,int obj)
{
  CameraInput* CaptureOBJ = (CameraInput*) MMfetch(m, MTOP(obj), 0);
	SAFE_DELETE(CaptureOBJ);

	MMstore(m, MTOP(obj), 0, 0);
	
  MMechostr(MSKDEBUG, "ObjCapture destroyed.\n");
	return 0;
}


/*! @ingroup grpcapture
* \brief _DScapture : This function destroy a capture device
*
* <b>Prototype:</b> fun [ObjCapture] I
* \param ObjCapture : capture device to destroy
*
* \return I : return 0 if success or NIL otherwise
*/
int _DScapture(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_DScapture\n");
#endif
	
	int obj = MTOP(MMget(m, 0));
	if (obj == NIL)
  {
    MMset(m, 0, NIL);
    return 0;
  }
	
	OBJdelTM(m, OBJCAPTURESCOL, PTOM(obj));
  
	MMset(m, 0, ITOM(0));	

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}


/*! @ingroup grpcapture
* \brief _CRcapture : This function create a new capture device from a device index
*
* <b>Prototype:</b> fun [Chn I] ObjCapture
* \param Chn : Scol channel
* \param I : Device index (from 0) or NIL for first found
*
* \return ObjCapture : new capture device, NIL on error
*/
int _CRcapture(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"_CRcapture\n");
#endif

  int sidx = MMpull(m);
	int idx = 0;
	if ((sidx != NIL) && (MTOI(sidx) > 0))
		idx = MTOI(sidx);

  CameraInput* CaptureOBJ = new CameraInput(idx);

  // failed to create device
  if (!CaptureOBJ)
  {
		MMechostr(MSKDEBUG, "Failed to create device\n");
		MMset(m, 0, NIL);
    return 0;
	}

  // failed to open device
  if (!CaptureOBJ->Initialize())
  {
		MMechostr(MSKDEBUG, "Failed to open device\n");
		SAFE_DELETE(CaptureOBJ);
		MMset(m, 0, NIL);
    return 0;
	}
	
  int capture = MMmalloc(m, 1, TYPETAB);
	if (capture == NIL)
  {
		SAFE_DELETE(CaptureOBJ);
		MMset(m, 0, NIL);
    return MERRMEM;
	}
	
	MMstore(m, capture, 0, (int)CaptureOBJ);
	MMpush(m, PTOM(capture));

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG,"ok\n");
#endif
	return OBJcreate(m, OBJCAPTURESCOL, (int)CaptureOBJ, NIL, NIL);
}


/*! @ingroup grpcapture
* \brief _BLTcapture : This function blit the device picture into a bitmap
* <b>Prototype:</b> fun [ObjCapture ObjBitmap] ObjBitmap
* \param ObjCapture : capture device to blit
* \param ObjBitmap : bitmap to update
*
* \return ObjBitmap : return same bitmap if success or NIL otherwise
*/
int _BLTcapture(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BLITcapture\n");
#endif
	
  int bitmap = MTOP(MMpull(m));
	int capture = MTOP(MMget(m, 0));
	if ((capture == NIL) || (bitmap == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	CameraInput* CaptureOBJ = (CameraInput*) MMfetch(m, capture, 0);
  if (CaptureOBJ == NULL)
  {
    MMset(m, 0, NIL);
    return 0;
  }
	
	cv::Mat image;
	try
	{
		image = CaptureOBJ->UpdateImage();
	}
	catch (std::exception e)
	{
		MMechostr(MSKRUNTIME, " error : %s \n", e.what());
		MMset(m, 0, NIL);
    return 0;
	}

	PtrObjVoid OB = (PtrObjVoid) MMstart(m, bitmap);
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

	cv::Mat imgdest(B->TailleH, B->TailleW, image.type());
	resize(image, imgdest, imgdest.size(), 0, 0, INTER_LINEAR);

	ConversionTools::MatToScolBitmapRGB(imgdest, B);

	MMset(m, 0, PTOM(bitmap));	

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpcapture
* \brief _GETcaptureDeviceList : This function list the installed video devices
* <b>Prototype:</b> fun [] [S r1]
*
* \return [S r1] : return the device list or NIL otherwise
*/
int _GETcaptureDeviceList(mmachine m)
{
	#ifdef SCOL_DEBUG
	MMechostr(0,"_GETcaptureDeviceList\n");
	#endif

	IBaseFilter *p_base_filter = NULL;
	IMoniker *p_moniker = NULL;
	ULONG i_fetched;
	HRESULT hr;

	/*Create the system device enumerator*/
	ICreateDevEnum *p_dev_enum = NULL;

	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void **)&p_dev_enum);

	if(FAILED(hr))
	{
		MMset(m, 0, NIL);
		return 0;
	}

	/* Create an enumerator for the video capture devices */
	IEnumMoniker *p_class_enum = NULL;
	hr = p_dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &p_class_enum, 0);

	p_dev_enum->Release();
	if(FAILED(hr))
	{
		MMset(m, 0, NIL);
		return 0;
	}

	/* If there are no enumerators for the requested type, then
	* CreateClassEnumerator will succeed, but p_class_enum will be NULL */
	if(p_class_enum == NULL)
	{
		MMset(m, 0, NIL);
		return 0;
	}

	/* Enumerate the devices */

	/* Note that if the Next() call succeeds but there are no monikers,
	* it will return S_FALSE (which is not a failure). Therefore, we check
	* that the return code is S_OK instead of using SUCCEEDED() macro. */

	int n = 0;
	int k;

	while(p_class_enum->Next(1, &p_moniker, &i_fetched) == S_OK)
	{
		/* Getting the property page to get the device name */
		IPropertyBag *p_bag;
		hr = p_moniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&p_bag);
		if(SUCCEEDED(hr))
		{
			VARIANT var;
			var.vt = VT_BSTR;
			hr = p_bag->Read(L"FriendlyName", &var, NULL);
			p_bag->Release();

			if(SUCCEEDED(hr))
			{
				int i_convert = WideCharToMultiByte(CP_ACP, 0, var.bstrVal, SysStringLen(var.bstrVal), NULL, 0, NULL, NULL);
				char *p_buf = (char *)malloc(i_convert+1);
				p_buf[0] = 0;

				WideCharToMultiByte(CP_ACP, 0, var.bstrVal, SysStringLen(var.bstrVal), p_buf, i_convert, NULL, NULL);
				SysFreeString(var.bstrVal);
				p_buf[i_convert] = '\0';

				if (k=Mpushstrbloc(m, p_buf)) return k;
				
				free(p_buf);
				n++;
			}
		}
		p_moniker->Release();
	}

	p_class_enum->Release();

	if(MMpush(m, NIL)) return MERRMEM;

	for(int j=0;j<n;j++)
	{
		if (MMpush(m, 2*2)) return MERRMEM;
		if (k=MBdeftab(m)) return k;
	}

	#ifdef SCOL_DEBUG
	MMechostr(0,"ok\n");
	#endif

	return 0;
}

/*! @ingroup grpcapture
* \brief _SETcaptureMirror : This function change the miror mode for the capture
* <b>Prototype:</b> fun [ObjCapture I] I
* \param ObjCapture : capture device to apply miror mode
* \param I : value of the miror mode boolean
*
* \return I : return I
*/
int _SETcaptureMirror(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_SETcaptureMirror\n");
#endif
	
  int mMode = MMpull(m);
	int capture = MMget(m, 0);
	if ((capture == NIL) || (mMode == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	CameraInput* CaptureOBJ = (CameraInput*) MMfetch(m, MTOP(capture), 0);
  if (CaptureOBJ == NULL)
  {
    MMset(m, 0, NIL);
    return 0;
  }
	
	CaptureOBJ->SetMirrorMode(MTOI(mMode) == 1);

	MMset(m, 0, ITOM(1));	

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpcapture
* \brief _GETcaptureSize : This function the size of a capture device
* <b>Prototype:</b> fun [ObjCapture] [I I]
* \param ObjCapture : capture device
*
* \return [ I I ] : return width and height
*/
int _GETcaptureSize(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_GETcaptureSize\n");
#endif

	int capture = MMget(m, 0);
	if (capture == NIL)
  {
    MMset(m, 0, NIL);
    return 0;
  }

	CameraInput* CaptureOBJ = (CameraInput*) MMfetch(m, MTOP(capture), 0);
  if (CaptureOBJ == NULL)
  {
    MMset(m, 0, NIL);
    return 0;
  }

	int tupleSize = MMmalloc(m, 2, TYPETAB);
  if (tupleSize == NIL)
  {
    MMset(m, 0, NIL);
		return 0;
  }
  
  MMstore(m, tupleSize, 0, ITOM(CaptureOBJ->GetWidth()));
  MMstore(m, tupleSize, 1, ITOM(CaptureOBJ->GetHeight()));
  MMset(m, 0, PTOM(tupleSize));

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpcapture
* \brief _SETcaptureSize : This function apply a size to a capture device
* <b>Prototype:</b> fun [ObjCapture I I] I
* \param ObjCapture : capture device
* \param I : width
* \param I : height
*
* \return I : return I
*/
int _SETcaptureSize(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_SETcaptureSize\n");
#endif

	int mHeight = MMpull(m);
  int mWidth = MMpull(m);
	int capture = MMget(m, 0);

	if ((capture == NIL) || (mHeight == NIL) || (mWidth == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	CameraInput* CaptureOBJ = (CameraInput*) MMfetch(m, MTOP(capture), 0);

  if (CaptureOBJ == NULL)
  {
    MMset(m, 0, NIL);
    return 0;
  }
	
	CaptureOBJ->SetSize(MTOI(mWidth), MTOI(mHeight));

	MMset(m, 0, ITOM(1));	

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*
*********************************************
* SCOL PART
*********************************************
*/

/*Declaration*/
//! Nb of Scol functions or types
#define NbCapPKG 8

/*!
*	Scol function names
*/
char *CapName[NbCapPKG] =
{
	"ObjCapture",
	"_CRcapture",
	"_DScapture",
	"_BLTcapture",
	"_GETcaptureDeviceList",
	"_SETcaptureMirror",
	"_GETcaptureSize",
	"_SETcaptureSize"
};

/*!
*	Pointers to C functions that manipulate the VM for each scol function previously defined
*/
int (*CapFunc[NbCapPKG])(mmachine m)=
{
	NULL,
	_CRcapture,			
	_DScapture,
	_BLTcapture,
	_GETcaptureDeviceList,
	_SETcaptureMirror,
	_GETcaptureSize,
	_SETcaptureSize
};

/*!
*	Nb of arguments of each scol function
*/
int CapNArg[NbCapPKG]=
{
	TYPTYPE,
	2,														// _CRcapture
	1, 														// _DScapture
	2,														// _BLTcapture
	0,														// _GETcaptureDeviceList
	2,														// _SETcaptureMirror
	1,														// _GETcaptureSize
	3															// _SETcaptureSize
};

/*!
*	Prototypes of the scol functions
*/
char* CapType[NbCapPKG]=
{
	NULL,
	"fun [Chn I] ObjCapture",										// _CRcapture
	"fun [ObjCapture] I",												// _DScapture
	"fun [ObjCapture ObjBitmap] ObjBitmap",			// _BLTcapture
	"fun [] [S r1]",														// _GETcaptureDeviceList
	"fun [ObjCapture I] I",											// _SETcaptureMirror
	"fun [ObjCapture][I I]",										// _GETcaptureSize
	"fun [ObjCapture I I] I"										// _SETcaptureSize
};



// Everything inside _cond and _endcond is ignored by doxygen
//! \cond

/*!
* \brief Load the packages in Scol virtual machine
* \param mmachine : the scol machine
*/
int LoadCaptureToolkit(mmachine m)
{
	int k;
  MMechostr(MSKDEBUG," > Loading CaptureToolkit\n");
	OBJCAPTURESCOL = OBJregister(NBCAPTURECB, 0, destroyCaptureObj, "OBJCAPTURESCOL");
	k = PKhardpak(m, "CaptureToolkitEngine", NbCapPKG, CapName, CapFunc, CapNArg, CapType);
	MMechostr(MSKDEBUG," > Successfully Loaded\n\n");
	return k;
}

//! \endcond