/*
-----------------------------------------------------------------------------
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 grpbitmap Scol functions definition
 *  Scol functions definition
 *  @{
 */
/** @} */

#include "prerequisites.h"
#include "ScolConvert.h"

using namespace cv;

/*! @ingroup grpbitmap
* \brief _BTBLURbitmap : This function apply a blur on a bitmap
* <b>Prototype:</b> fun [ObjBitmap I I] ObjBitmap
* \param ObjBitmap : bitmap to update
* \param I : Blur value on X
* \param I : Blur value on Y
*
* \return ObjBitmap : return same bitmap if success or NIL otherwise
*/
int _BTBLURbitmap(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTBLURbitmap\n");
#endif

	/* Get stack data*/
	int blurY = MMpull(m);
	int blurX = MMpull(m);
  int bitmap = MMget(m, 0);

	if ((bitmap == NIL) || ((blurY == NIL) && (blurX == NIL)))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	blurX = MTOI(blurX);
	blurY = MTOI(blurY);

	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

	cv::Mat imgdest(B->TailleH, B->TailleW, CV_8UC3);
	try
	{
		cv::Mat imagesrc(B->TailleH, B->TailleW, CV_8UC3, B->bits, B->BPL);
		cv::Size blursize(blurX, blurY);
		cv::blur(imagesrc, imgdest, blursize);
	}
	catch( cv::Exception& e )
	{
		MMechostr(MSKDEBUG, "_BTBLURbitmap error %s\n", e.what());
		return 0;
	}

  ConversionTools::MatToScolBitmapRGB(imgdest, B);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpbitmap
* \brief _BTLAPLACEbitmap : This function apply a LAPLACE filter on a bitmap
* <b>Prototype:</b> fun [ObjBitmap I I] ObjBitmap
* \param ObjBitmap : bitmap to update
* \param I : Desired depth
* \param I : Aperture size
*
* \return ObjBitmap : return same bitmap if success or NIL otherwise
*/
int _BTLAPLACEbitmap(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTLAPLACEbitmap\n");
#endif

	/* Get stack data*/
	int iksize = MMpull(m);
	int iddepth = MMpull(m);
  int bitmap = MMget(m, 0);

	if ((bitmap == NIL) || (iddepth == NIL) || (iksize == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	int ksize = MTOI(iksize);
	int ddepth = MTOI(iddepth);

	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

	cv::Mat imgdest(B->TailleH, B->TailleW, CV_8UC3);
	try
	{
		cv::Mat imagesrc(B->TailleH, B->TailleW, CV_8UC3, B->bits, B->BPL);

    cv::Laplacian(imagesrc, imgdest, ddepth, ksize);
	}
	catch( cv::Exception& e)
	{
		MMechostr(MSKDEBUG, "_BTLAPLACEbitmap error %s\n", e.what());
		return 0;
	}

	ConversionTools::MatToScolBitmapRGB(imgdest, B);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpbitmap
* \brief _BTFLIPbitmap : This function apply vertical flip on a bitmap
* <b>Prototype:</b> fun [ObjBitmap] ObjBitmap
* \param ObjBitmap : bitmap to flip
*
* \return ObjBitmap : return same bitmap if success or NIL otherwise
*/
int _BTFLIPbitmap(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTFLIPbitmap\n");
#endif

	/* Get stack data*/
  int bitmap = MMget(m, 0);

	if (bitmap == NIL)
  {
    MMset(m, 0, NIL);
    return 0;
  }

	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

	cv::Mat imgdest(B->TailleH, B->TailleW, CV_8UC3);
	try
	{
    cv::Mat imagesrc(B->TailleH, B->TailleW, CV_8UC3, B->bits, B->BPL);
		cv::flip(imagesrc, imgdest, 1);
	}
	catch( cv::Exception& e )
	{
		MMechostr(MSKDEBUG, "_BTBLURbitmap error %s\n", e.what());
		return 0;
	}

  ConversionTools::MatToScolBitmapRGB(imgdest, B);
  
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}


/*! @ingroup grpbitmap
* \brief _BTDRAWcircle : This function draw a circle into a bitmap
* <b>Prototype:</b> fun [ObjBitmap F F F I] ObjBitmap
* \param ObjBitmap : bitmap to update
* \param F : Position X
* \param F : Position Y
* \param F : Radius
* \param I : Thickness
*
* \return ObjBitmap : return same bitmap if success or NIL otherwise
*/
int _BTDRAWcircle(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTDRAWcircle\n");
#endif

	/* Get stack data*/
  int ithickness = MMpull(m);
  int fradius = MMpull(m);
	int fposY = MMpull(m);
	int fposX = MMpull(m);
  int bitmap = MMget(m, 0);

	if ((bitmap == NIL) || (fposX == NIL) || (fposY == NIL)|| (fradius == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	float posX = MTOF(fposX);
	float posY = MTOF(fposY);

	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

  cv::Mat imagesrc(B->TailleH, B->TailleW, CV_8UC3, B->bits, B->BPL);

	try
	{
		cv::Point position(posX, posY);
    cv::circle(imagesrc, position, MTOF(fradius), cv::Scalar(100,100,100),MTOI(ithickness));
	}
	catch( cv::Exception& e )
	{
		MMechostr(MSKDEBUG, "_BTDRAWcircle error %s\n", e.what());
		return 0;
	}

	ConversionTools::MatToScolBitmapRGB(imagesrc, B);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpbitmap
* \brief _BTMOTIONdetect : This function detect the motion between two bitmaps
* <b>Prototype:</b> fun [ObjBitmap ObjBitmap I I] I
* \param ObjBitmap : previous bitmap
* \param ObjBitmap : current bitmap
* \param I : thresol of the detection filter
* \param I : sensivity (0 to 100)
*
* \return I : return 1 if motion is detected, 0 if not or NIL otherwise
*/
int _BTMOTIONdetect(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTMOTIONdetect\n");
#endif

	/* Get stack data*/
  int sensivity = MMpull(m);
  int threshold = MMpull(m);
	int cbitmap = MMpull(m);
  int pbitmap = MMget(m, 0);

	if ((pbitmap == NIL) || (cbitmap == NIL) || (threshold == NIL) || (sensivity == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }

	PtrObjVoid pOB = (PtrObjVoid) MMstart(m, MTOP(pbitmap));
	PtrObjBitmap pB = (PtrObjBitmap) MMstart(m, MTOP(pOB->Buffer));

	PtrObjVoid cOB = (PtrObjVoid) MMstart(m, MTOP(cbitmap));
	PtrObjBitmap cB = (PtrObjBitmap) MMstart(m, MTOP(cOB->Buffer));

  if ((pB->TailleW != cB->TailleW) || (pB->TailleH != cB->TailleH))
  {
    MMset(m, 0, NIL);
    return 0;
  }
  
  cv::Mat diff(cB->TailleH, cB->TailleW, CV_8UC3);
  cv::Mat tmp(cB->TailleH, cB->TailleW, CV_8UC3);
	try
	{
    cv::Mat prevImage(pB->TailleH, pB->TailleW, CV_8UC3, pB->bits, pB->BPL);
    cv::Mat curImage(cB->TailleH, cB->TailleW, CV_8UC3, cB->bits, cB->BPL);
    cv::absdiff(prevImage, curImage, diff);

    //Convert the image to grayscale.
    cv::cvtColor(diff, tmp, CV_RGB2GRAY);

    //Convert the image to black and white.
    cv::threshold(tmp, tmp, MTOI(threshold), 255, CV_THRESH_BINARY);
 

    //Dilate and erode to get people blobs
    //cv::dilate(tmp, tmp, 0, 18);
    //cv::dilate(tmp, tmp, 0, 10);

    //Find the contours of the moving images in the frame.
    //std::vector< std::vector<cv::Point> > contour;
    //cv::findContours(tmp, contour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    //cv::cvtColor(tmp, curImage, CV_GRAY2RGB);
    //ConversionTools::MatToScolBitmapRGB(curImage, cB);
    int count = cv::countNonZero(tmp);
    float percent = ((float)count / (float)(cB->TailleH * cB->TailleW)) * 100.0f;
    
    MMset(m, 0, ITOM(percent >= ((100.0f - (float)MTOI(sensivity)) / 100.0f) ? 1 : 0));
  }
	catch( cv::Exception& e)
	{
		MMechostr(MSKDEBUG, "_BTMOTIONdetect error %s\n", e.what());
    MMset(m, 0, NIL);
	}

  tmp.release();
  diff.release();
  
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpbitmap
* \brief _BTCompBitmap : This function compress a bitmap and return the buffer as string
* <b>Prototype:</b> fun [ObjBitmap I] S
* \param ObjBitmap : bitmap to compress
* \param I : Compression factor 1 to 100
*
* \return S : return compressed data if success or NIL otherwise
*/
int _BTCompBitmap(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTCompBitmap\n");
#endif

	/* Get stack data*/
	int ifactor = MMpull(m);
  int bitmap = MMget(m, 0);

	if (bitmap == NIL)
  {
    MMset(m, 0, NIL);
    return 0;
  }
  
  int factor = 1;
  if ((ifactor != NIL) && (MTOI(ifactor) > 0))
	  factor = MTOI(ifactor);
  
	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

  vector<int> p;
  p.push_back(CV_IMWRITE_JPEG_QUALITY);
  p.push_back(factor);
  std::vector<unsigned char> buf;

	try
	{
		cv::Mat imagesrc(B->TailleH, B->TailleW, CV_8UC3, B->bits, B->BPL);
    imencode(".jpg", imagesrc, buf, p);
	}
	catch( cv::Exception& e )
	{
		MMechostr(MSKDEBUG, "_BTCompBitmap error %s\n", e.what());
    MMset(m, 0, NIL);
		return 0;
	}

  if (buf.empty())
  {
    MMset(m, 0, NIL);
    return 0;
  }
  
  char* scolbuf = 0;  
  int res = MMmalloc (m,((buf.size()+4)>>2)+1, TYPEBUF);
  *(int*) MMstart(m, res) = buf.size();
  unsigned char* BS = (unsigned char*)MMstart(m, res + 1);
  
  std::vector<unsigned char>::iterator it;
  int i = 0;
  for (it=buf.begin(); it < buf.end(); it++)
  {
    BS[i] = *it;
    i++;
  }
  
  MMset(m, 0, (res<<1) + 1);

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*! @ingroup grpbitmap
* \brief _BTUnCompBitmap : This function uncompress a bitmap data to an existed bitmap
* <b>Prototype:</b> fun [S ObjBitmap] S
* \param S : compressed bitmap data
* \param ObjBitmap : bitmap to fill
*
* \return S : return the filled bitmap if success or NIL otherwise
*/
int _BTUnCompBitmap(mmachine m)
{
#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "_BTUnCompBitmap\n");
#endif

	/* Get stack data*/
  int bitmap = MMpull(m);
	int idata = MMget(m, 0);

	if ((bitmap == NIL) || (idata == NIL))
  {
    MMset(m, 0, NIL);
    return 0;
  }
  
	PtrObjVoid OB = (PtrObjVoid) MMstart(m, MTOP(bitmap));
	PtrObjBitmap B = (PtrObjBitmap) MMstart(m, MTOP(OB->Buffer));

  int msize = MMsizestr(m, MTOP(idata));
  char* smess = (char*)MMstart(m, MTOP(idata)+1);

  //fill the buffer
  std::vector<char> data(smess, smess + msize);
  
	try
	{
    cv::Mat imagedst = imdecode(cv::Mat(data), 1);

    if (imagedst.empty())
    {
      MMset(m, 0, NIL);
      return 0;
    }
    
    cv::Size bsize = imagedst.size();
    if ((B->TailleW != bsize.width) || (B->TailleW != bsize.width))
    {
      MMechostr(MSKDEBUG, "_BTUnCompBitmap error the bitmap have not the correct data size\n");
      MMset(m, 0, NIL);
      return 0;
    }

    ConversionTools::MatToScolBitmapRGB(imagedst, B);
    MMset(m, 0, bitmap);
	}
	catch( cv::Exception& e )
	{
		MMechostr(MSKDEBUG, "_BTUnCompBitmap error %s\n", e.what());
    MMset(m, 0, NIL);
		return 0;
	}

#ifdef	_SCOL_DEBUG_
	MMechostr(MSKDEBUG, "ok\n");
#endif
	return 0;
}

/*
*********************************************
* SCOL PART
*********************************************
*/

/*Declaration*/
//! Nb of Scol functions or types
#define NbBtkPKG 7

/*!
*	Scol function names
*/
char *BtkName[NbBtkPKG] =
{
	"_BTBLURbitmap",
  "_BTLAPLACEbitmap",
	"_BTFLIPbitmap",
  "_BTDRAWcircle",
  "_BTCompBitmap",
  "_BTUnCompBitmap",
  "_BTMOTIONdetect"
};

/*!
*	Pointers to C functions that manipulate the VM for each scol function previously defined
*/
int (*BtkFunc[NbBtkPKG])(mmachine m)=
{
	_BTBLURbitmap,
  _BTLAPLACEbitmap,
	_BTFLIPbitmap,
  _BTDRAWcircle,
  _BTCompBitmap,
  _BTUnCompBitmap,
  _BTMOTIONdetect
};

/*!
*	Nb of arguments of each scol function
*/
int BtkNArg[NbBtkPKG]=
{
	3,						 								// _BTBLURbitmap
  3,                            // _BTLAPLACEbitmap
	1,														// _BTFLIPbitmap
  5,                            // _BTDRAWcircle
  2,                            // _BTCompBitmap
  2,                            // _BTUnCompBitmap
  4                             // _BTMOTIONdetect
};

/*!
*	Prototypes of the scol functions
*/
char* BtkType[NbBtkPKG]=
{
	"fun [ObjBitmap I I] ObjBitmap",					  // _BTBLURbitmap
  "fun [ObjBitmap I I] ObjBitmap",            // _BTLAPLACEbitmap
	"fun [ObjBitmap] ObjBitmap",								// _BTFLIPbitmap
  "fun [ObjBitmap F F F I] ObjBitmap",        // _BTDRAWcircle
  "fun [ObjBitmap I] S",                      // _BTCompBitmap
  "fun [S ObjBitmap] ObjBitmap",              // _BTUnCompBitmap
  "fun [ObjBitmap ObjBitmap I I] I"           // _BTMOTIONdetect
};


// Everything inside _cond and _endcond is ignored by doxygen
//! \cond

/*!
* \brief Load the packages in Scol virtual machine
* \param mmachine : the scol machine
*/
int LoadBitmapToolKit(mmachine m)
{
	int k;
  MMechostr(MSKDEBUG," > Loading BitmapToolkit\n");
	k = PKhardpak(m, "BitmapToolkitEngine", NbBtkPKG, BtkName, BtkFunc, BtkNArg, BtkType);
	MMechostr(MSKDEBUG," > Successfully Loaded\n\n" );
	return k;
}

//! \endcond
