/******************************************************************************
Project: 	MicroMacro
Author: 	SolarStrike Software
URL:		www.solarstrike.net
License:	Modified BSD (see license.txt)
******************************************************************************/

#include "cv_lua.h"
#include "error.h"
#include "opencv2/core/core.hpp"
#include "opencv2/video/background_segm.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "window_lua.h"
#include "types.h"
#include "strl.h"
#include "event.h"
#include "macro.h"
#include "luatypes.h"
#include "types.h"
#include <iostream>

using namespace cv;
using namespace std;


extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
//filter on heap
Mat filter;
bool filter_flag = false;
int number_args = 0;
// save info on heap
queue<int> buffer;
/*
Helper function to which made screenshot and convert it into an Mat object
*/
Mat hwnd2mat(HWND hwnd){

	HDC hwindowDC,hwindowCompatibleDC;

	int height,width,srcheight,srcwidth;
	HBITMAP hbwindow;
	Mat src;
	BITMAPINFOHEADER  bi;

	hwindowDC=GetDC(hwnd);
	hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
	SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);  

	RECT windowsize;    // get the height and width of the screen
	GetClientRect(hwnd, &windowsize);

	srcheight = windowsize.bottom;
	srcwidth = windowsize.right;
	height = windowsize.bottom;  //change this to whatever size you want to resize to
	width = windowsize.right;

	src.create(height,width,CV_8UC4);

	// create a bitmap
	hbwindow = CreateCompatibleBitmap( hwindowDC, width, height);
	bi.biSize = sizeof(BITMAPINFOHEADER);    //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
	bi.biWidth = width;    
	bi.biHeight = -height;  //this is the line that makes it draw upside down or not
	bi.biPlanes = 1;    
	bi.biBitCount = 32;    
	bi.biCompression = BI_RGB;    
	bi.biSizeImage = 0;  
	bi.biXPelsPerMeter = 0;    
	bi.biYPelsPerMeter = 0;    
	bi.biClrUsed = 0;    
	bi.biClrImportant = 0;

	// use the previously created device context with the bitmap
	SelectObject(hwindowCompatibleDC, hbwindow);
	// copy from the window device context to the bitmap device context
	StretchBlt( hwindowCompatibleDC, 0,0, width, height, hwindowDC, 0, 0,srcwidth,srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
	GetDIBits(hwindowCompatibleDC,hbwindow,0,height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS);  //copy from hwindowCompatibleDC to hbwindow

	// avoid memory leak
	DeleteObject (hbwindow); DeleteDC(hwindowCompatibleDC); ReleaseDC(hwnd, hwindowDC);
	if(src.empty()){
		printf("possible wrong HWD no picture \n");
	}
	return src;
}

int CV_lua::regmod(lua_State *L)
{
	static const luaL_Reg _funcs[] = {
		{"lines", CV_lua::lines},
		{"cycles", CV_lua::cycles},
		{"object", CV_lua::object},
		{"motion", CV_lua::motion},
		{"setfilter_colour", CV_lua::setfilter_colour},
		{"setfilter_rect", CV_lua::setfilter_rect},
		{"lines_next", CV_lua::lines_next},
		{"cycles_next", CV_lua::cycles_next},
		{"objects_next", CV_lua::objects_next},
		{"motions_next", CV_lua::motions_next},
		{"clearfilter", CV_lua::clearfilter},
		{"clearbuffer", CV_lua::clearbuffer},
		{"loadimage", CV_lua::loadimage},
		{"saveimage", CV_lua::saveimage},
		{NULL, NULL}
	};

	luaL_newlib(L, _funcs);
	lua_setglobal(L, CV_MODULE_NAME);

	return MicroMacro::ERR_OK;
}
/*	cv.setfilter_rect(hwd, int x, int y, int width, int heigh[,bool debugwindow]))
This function copy only a part of the screen you choose
@pre x,y are top left.

@pre if a filter is definded already it will be used otherweise hwd for the application 
from which a screenshot will be made.

@pre width and heigh must be greater than 0

@pre hwd must be valid or 0 

@modify The results will be saved in the filter buffer


Returns nil
*/
int CV_lua::setfilter_rect(lua_State *L){
	int argsn = lua_gettop(L);
	if( argsn != 5 && argsn != 6 )
		wrongArgs(L);
	checkType(L, LT_NUMBER, 1);
	checkType(L, LT_NUMBER, 2);
	checkType(L, LT_NUMBER, 3);
	checkType(L, LT_NUMBER, 4);
	checkType(L, LT_NUMBER, 5);


	HWND hwnd = (HWND)lua_tointeger(L, 1);

	int x = (int)lua_tointeger(L, 2);
	int y = (int)lua_tointeger(L, 3);
	int width = (int)lua_tointeger(L, 4);
	int heigh = (int)lua_tointeger(L, 5);

	if(width == 0 || heigh == 0){
		wrongArgs(L);
	}


	if(filter_flag == false){
		//filter your screen/window
		if( hwnd == 0 ){
			hwnd = GetDesktopWindow();
		}

		Mat src = hwnd2mat(hwnd);
		//clear the old filter
		filter = Scalar(0,0,0);
		cvtColor(src,src,CV_8UC3);
		//cvtColor(filter,filter,CV_8UC3);
		//Mat dst,cdst;
		Rect myROI(x, y, width, heigh);
		filter = src(myROI);
		filter_flag = true;
	}
	else
	{
		Rect myROI(x, y, width, heigh);
		filter = filter(myROI);
		//filter the result again

	}
	if(argsn == 6){
		checkType(L, LT_BOOLEAN, 6);
		bool showwindows = lua_toboolean(L,6);
		if(showwindows){
			namedWindow("Micro Macro 2:cv_filter", WINDOW_NORMAL );
			imshow("Micro Macro 2:cv_filter", filter);
			waitKey(0);

		}
	}
	return 0;
}
/*	cv.setfilter_colour(hwd, int min_red, int min_green, int min_blue, int max_red, int max_green, int max_blue[,bool debugwindow]))
This filter give out only the part of the the picture which is in between the values you gave it

@pre if a filter is definded already it will be used otherweise hwd for the application 
from which a screenshot will be made.

@pre all max parameters must be greater than 0

@pre hwd must be valid or 0 

@modify The results will be saved in the filter buffer


Returns nil
*/
int CV_lua::setfilter_colour(lua_State *L){
	int argsn = lua_gettop(L);
	if( argsn != 7 && argsn != 8 )
		wrongArgs(L);
	checkType(L, LT_NUMBER, 1);
	checkType(L, LT_NUMBER, 2);
	checkType(L, LT_NUMBER, 3);
	checkType(L, LT_NUMBER, 4);
	checkType(L, LT_NUMBER, 5);
	checkType(L, LT_NUMBER, 6);
	checkType(L, LT_NUMBER, 7);
	if(argsn == 8)
		checkType(L, LT_BOOLEAN, 8);


	HWND hwnd = (HWND)lua_tointeger(L, 1);

	int min_red = (int)lua_tointeger(L, 2);
	int min_green = (int)lua_tointeger(L, 3);
	int min_blue = (int)lua_tointeger(L, 4);

	int max_red = (int)lua_tointeger(L, 5);
	int max_green = (int)lua_tointeger(L, 6);
	int max_blue = (int)lua_tointeger(L, 7);

	if(max_red == 0 || max_blue == 0 || max_green == 0){

		wrongArgs(L);
	}

	if(filter_flag == false){
		//filter your screen/window
		if( hwnd == 0 ){
			hwnd = GetDesktopWindow();
		}

		Mat src = hwnd2mat(hwnd);
		//clear the old filter
		filter = Scalar(0,0,0);

		//	cvtColor(src,src,CV_8UC3);
		//cvtColor(filter,filter,CV_8UC3);
		//Mat dst,cdst;
		inRange(src, Scalar(min_blue, min_green, min_red), Scalar(max_blue, max_green, max_red), filter);
		filter_flag = true;
		//filter.copyTo(filter,target);
	}
	else
	{
		//filter the result again
		inRange(filter, Scalar(min_blue, min_green, min_red), Scalar(max_blue, max_green, max_red), filter);
	}
	if(argsn == 8){
		checkType(L, LT_BOOLEAN, 8);
		bool showwindows = lua_toboolean(L,8);
		if(showwindows){
			namedWindow("Micro Macro 2:cv_filter_colour", WINDOW_NORMAL );
			imshow("Micro Macro 2:cv_filter_colour", filter);
			waitKey(0);
			
		}
	}
	return 0;
}
/*	cv.clearfilter()
This empty the filter buffer in which all pictures are stored between calls

Returns nil
*/
int CV_lua::clearfilter(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);
	filter_flag = false;
	filter = Scalar(0,0,0);
	filter.resize(0);
	return 0;
}
/*	cv.lines(hwd[, int min_lenght][, max_gap][,bool debugwindow]))
This call will search lines in the picture/screen

@pre if a filter is definded already it will be used otherweise hwd for the application 
from which a screenshot will be made.

@pre hwd must be valid or 0 

@modify The results will be saved in the result buffer, you can get it with cv.lines_next()

@post The filter buffer will be flushed

Returns int number of results
*/
int CV_lua::lines(lua_State *L){
	int argsn = lua_gettop(L);
	if(argsn > 4 || argsn < 1)
		wrongArgs(L);
	checkType(L, LT_NUMBER, 1);

	if(argsn > 1)
		checkType(L, LT_NUMBER, 2);
	if(argsn > 2)
		checkType(L, LT_NUMBER, 3);
	if(argsn > 3)
		checkType(L, LT_BOOLEAN, 4);


	//checkType(L, LT_STRING, 2);
	//checkType(L, LT_STRING, 2);
	//checkType(L, LT_STRING, 2);

	//RECT winRect;
	//HDC hdc;
	//HDC tmpDc;
	//HBITMAP hBmp;
	//global setup for return values
	number_args = 4;
	//size_t filenameLen;
	HWND hwnd = (HWND)lua_tointeger(L, 1);
	//const char *filename = lua_tolstring(L, 2, &filenameLen);
	Mat src; 

	if( hwnd == 0 )
		hwnd = GetDesktopWindow();

	if (filter_flag == false){
		src = hwnd2mat(hwnd);
	}
	else
	{
		src = filter;

	}
	Mat dst,cdst;

	//	//dst.copySize(src);
	//	dst.create(src.size(),COLOR_BGR2GRAY);
	//	dst = Scalar(0,0,0);
	//
	////	dst.create(src.copySize(), COLOR_BGR2GRAY)
	//	
	//	//cdst.copySize(src);
	//	cdst.create(src.size(),COLOR_BGR2GRAY);
	//	cdst = Scalar(0,0,0);
	//
	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, COLOR_GRAY2BGR);
	//length_error

	int min_length;
	int max_gap;
	bool debug_image;
	int show_insec = 30;
	if (argsn >= 2){
		min_length = (int) lua_tonumber(L, 2);
	}
	else
	{
		//default value
		min_length = 50;
	}

	if (argsn >= 3){
		max_gap = (int)lua_tonumber(L, 3);
	}
	else
	{
		//default value
		max_gap  = 10;
	}
	//debug window?
	if (argsn >= 4){
		debug_image = lua_toboolean(L, 4);
	}
	else
	{
		//default value
		debug_image = false;
	}

	vector<Vec4i> lines;
	//HoughLinesP(cdst, lines, 1, CV_PI/180, 50, 50, 10 );
	HoughLinesP(dst, lines, 1, CV_PI/180, 50, min_length, max_gap );
	int counter = 0;
	for( size_t i = 0; i < lines.size() ; i++ )
	{
		Vec4i l = lines[i];

		buffer.push(l[0]);
		buffer.push(l[1]);
		buffer.push(l[2]);
		buffer.push(l[3]);
		//lua_pushnumber(L, l[0]);//x1
		//lua_pushnumber(L, l[1]);//y1

		//lua_pushnumber(L, l[2]);//x2
		//lua_pushnumber(L, l[3]);//y2
		//counter = counter + 1;
		if(debug_image == true){
			line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, 8);
		}
	}
	//std::cout << "Here comes the counter: "<< counter << std::endl;
	//std::cout << "Here comes the lines" << lines.size() << std::endl;

	//imshow("source", src);
	//imshow("detected lines", cdst);

	//waitKey();
	if(debug_image){
		namedWindow("Micro Macro 2:cv_lines", WINDOW_NORMAL );
		imshow("Micro Macro 2:cv_lines", cdst);
		waitKey(0);
	}
	// avoid memory leak

	if(filter_flag == false){
		// we can't relese it when then pointer show to the filer on heap.
		src.release();
	}
	else
	{
		//clear buffer;
		filter.resize(0);
	}

	dst.release();
	cdst.release();

	if(filter_flag == true){
		filter_flag = false;
		filter = Scalar(0,0,0);
		filter.resize(0);
	}
	//push number of objects
	lua_pushunsigned(L,(lua_Unsigned)lines.size());

	return 1;

}
/*	cv.cycles(hwd[, int min_radius][, max_radius][,bool debugwindow]))
This call will search cycles in the picture/screen

@pre if a filter is definded already it will be used otherweise hwd for the application 
from which a screenshot will be made.

@pre hwd must be valid or 0 

@modify The results will be saved in the result buffer, you can get it with cv.cycles_next()

@post The filter buffer will be flushed

Returns int number of results
*/
int CV_lua::cycles(lua_State *L){
	int argsn = lua_gettop(L);
	if(argsn > 5 || argsn < 1)
		wrongArgs(L);
	checkType(L, LT_NUMBER, 1);

	if(argsn > 1)
		checkType(L, LT_NUMBER, 2);
	if(argsn > 2)
		checkType(L, LT_NUMBER, 3);
	if(argsn > 3)
		checkType(L, LT_BOOLEAN, 4);
	//checkType(L, LT_STRING, 2);

	/*RECT winRect;
	HDC hdc;
	HDC tmpDc;
	HBITMAP hBmp;



	size_t filenameLen*/;
	//global setup for return values
	number_args = 3;
	HWND hwnd = (HWND)lua_tointeger(L, 1);
	//const char *filename = lua_tolstring(L, 2, &filenameLen);
	Mat src; 

	if( hwnd == 0 )
		hwnd = GetDesktopWindow();

	if (filter_flag == false){
		src =  hwnd2mat(hwnd);
	}
	else
	{
		src = filter;

	}
	Mat dst,cdst;

	//src = hwnd2mat(hwnd);

	//dst.copySize(src);

	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, COLOR_GRAY2BGR);

	int min_radius;
	int max_radius;
	bool debug_image;
	int show_insec = 30;
	if (argsn >= 2){
		min_radius = (int)lua_tonumber(L, 2);
	}
	else
	{
		//default value
		min_radius = 0;
	}

	if (argsn >= 3){
		max_radius = (int)lua_tonumber(L, 3);
	}
	else
	{
		//default value
		max_radius  = 0;
	}
	//debug window?
	if (argsn >= 4){
		debug_image = lua_toboolean(L, 4);
	}
	else
	{
		//default value
		debug_image = false;
	}


	vector<Vec3f> circles;

	/// Apply the Hough Transform to find the circles
	HoughCircles( dst, circles, HOUGH_GRADIENT, 1, dst.rows/8, 200, 100, min_radius, max_radius );
	//int counter = 0;
	for( size_t i = 0; i < circles.size(); i++ )
	{


		buffer.push((int)circles[i][0]);
		buffer.push((int)circles[i][1]);
		buffer.push((int)circles[i][2]);
		if(debug_image == true){
			Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
			int radius = cvRound(circles[i][2]);
			// circle center
			circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
			// circle outline
			circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
		}

	}



	//waitKey();
	if(debug_image){
		namedWindow( "Micro Macro 2:cv_cycles", WINDOW_NORMAL );
		imshow("Micro Macro 2:cv_cycles", src);
		waitKey(0);
	}
	// avoid memory leak

	if(filter_flag == false){
		// we can't relese it when then pointer show to the filer on heap.
		src.release();
	}
	else
	{
		//clear buffer;
		filter.resize(0);
	}

	dst.release();
	cdst.release();

	if(filter_flag == true){
		filter_flag = false;
		filter = Scalar(0,0,0);
		filter.resize(0);
	}
	//push number of objects
	lua_pushunsigned(L,(lua_Unsigned)circles.size());

	return 1;


}
/*	cv.object(hwd, string xml_path[,int min_width, int min_height][,int max_width, int max_height][,bool debugwindow]))
This call will search objects in the picture/screen

@pre if a filter is definded already it will be used otherweise hwd for the application 
from which a screenshot will be made.

@pre max_width and max_height must be not 0

@pre hwd must be valid or 0 

@modify The results will be saved in the result buffer, you can get it with cv.object_next()

@post The filter buffer will be flushed

Returns int number of results
*/
int CV_lua::object(lua_State *L){
	int top = lua_gettop(L);
	if( top < 2 ||top > 7)
		wrongArgs(L);



	checkType(L, LT_NUMBER, 1);
	checkType(L, LT_STRING, 2);

	if(top >= 4){
		checkType(L, LT_NUMBER, 3);
		checkType(L, LT_NUMBER, 4);
	}

	if( top >= 6){
		checkType(L, LT_NUMBER, 5);
		checkType(L, LT_NUMBER, 6);
	}

	if( top >=7){
		checkType(L, LT_BOOLEAN, 7);
	}


	//RECT winRect;
	//HDC hdc;
	//HDC tmpDc;
	//HBITMAP hBmp;

	//size_t filenameLen;
	HWND hwnd = (HWND)lua_tointeger(L, 1);
	//const char *filename = lua_tolstring(L, 2, &filenameLen);

	if( hwnd == 0 )
		hwnd = GetDesktopWindow();


	//HWND hwnd = (HWND)lua_tointeger(L, 1);
	//const char *filename = lua_tolstring(L, 2, &filenameLen);
	Mat src; 

	//global setup for return values
	number_args = 4;

	if( hwnd == 0 )
		hwnd = GetDesktopWindow();

	if (filter_flag == false){
		src = hwnd2mat(hwnd);
	}
	else
	{
		src = filter;

	}
	Mat dst;

	String xml_string = lua_tostring(L,2);
	CascadeClassifier seekobject;

	if( !seekobject.load(xml_string ) ){ printf("--(!)Error loading\n"); return 0; };

	std::vector<Rect> faces;

	bool debug_image;
	int show_insec = 30;
	if (top >= 7){
		debug_image = lua_toboolean(L, 7);
	}
	else
	{
		//default value
		debug_image = false;
	}
	//any time for the window?

	Mat frame_gray;
	frame_gray.create(src.size(),COLOR_BGR2GRAY);
	frame_gray = Scalar(0,0,0);

	cvtColor( src, frame_gray, COLOR_BGR2GRAY );
	equalizeHist( frame_gray, frame_gray );


	//-- Detect what you seek
	if(top == 2){
		seekobject.detectMultiScale( frame_gray, faces, 1.1, 2, 0 );
	}
	else
	{
		if(top == 4){
			int min_width = (int)lua_tointeger(L, 3);
			int min_height = (int)lua_tointeger(L, 4);

			seekobject.detectMultiScale( frame_gray, faces, 1.1, 2, 0, Size(min_width,min_height ) );
		}
		else
		{
			int min_width = (int)lua_tointeger(L, 3);
			int min_height = (int)lua_tointeger(L, 4);
			int max_width = (int)lua_tointeger(L, 5);
			int max_height = (int)lua_tointeger(L, 6);

			seekobject.detectMultiScale( frame_gray, faces, 1.1, 2, 0, Size(min_width,min_height ), Size(max_width,max_height ) );
		}
	}

	//int counter = 0;
	for( size_t i = 0; i < faces.size(); i++ ){

		// push in queue 
		buffer.push(faces[i].x);
		buffer.push(faces[i].y);
		buffer.push(faces[i].width);
		buffer.push(faces[i].height);

		if(debug_image == true){
			Point center( (int)(faces[i].x + faces[i].width*0.5), (int)(faces[i].y + faces[i].height*0.5) );
			ellipse( src, center, Size( (int)(faces[i].width*0.5), (int)(faces[i].height*0.5)), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
		}
	}




	//waitKey();
	if(debug_image){
		namedWindow("Micro Macro 2:cv_object", WINDOW_NORMAL );
		imshow("Micro Macro 2:cv_object", src);
		waitKey(0);
	}
	// avoid memory leak

	if(filter_flag == false){
		// we can't relese it when then pointer show to the filer on heap.
		src.release();
	}
	else
	{
		//clear buffer;
		filter.resize(0);
	}

	frame_gray.release();
	dst.release();


	if(filter_flag == true){
		filter_flag = false;
		filter = Scalar(0,0,0);
		filter.resize(0);
	}
	//push number of objects
	lua_pushunsigned(L,(lua_Unsigned)faces.size());

	return 1;

}
/*	cv.lines_next()
This will be return the results of cv.lines()

@pre cv.lines(..) must be called previously 

Returns nil or tables {tab.x1,tab.y1,tab.x2,tab.y2},{..} until 10 times
each table has stored the coordinates for a line to get all results 
you must call that function muliply times until you got less than 10 arguments or nil.

if you have all the results before the result buffer is empty please flush it with:
cv.clearbuffer()
*/
int CV_lua::lines_next(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);
	int size = 0;

	if(buffer.empty() == true){
		lua_pushnil(L);
		return 1;
	}

	for(size_t i = 0; i < buffer.size() && i < 10; i++){
		lua_createtable(L, 0, 4);

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"x1");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"y1");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"x2");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"y2");
		buffer.pop();//delete value 

		size = size + 1;

	}
	return size;
}
/*	cv.lines_cycles()
This will be return the results of cv.lines()

@pre cv.cycles(..) must be called previously 

Returns nil or tables {tab.x,tab.y,tab.radius},{..} until 10 times
each table has stored the coordinates for a cycle to get all results 
you must call that function muliply times until you got less than 10 arguments or nil.

if you have all the results before the result buffer is empty please flush it with:
cv.clearbuffer()
*/
int CV_lua::cycles_next(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);
	int size = 0;

	if(buffer.size() == 0){
		lua_pushnil(L);
		return 1;
	}

	for(size_t i = 0; i < buffer.size() && i < 10; i++){
		lua_createtable(L, 0, 3);

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"x");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"y");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"radius");
		buffer.pop();//delete value 



		size = size + 1;

	}
	return size;
}
/*	cv.objects_next()
This will be return the results of cv.lines()

@pre cv.objects(..) must be called previously 

Returns nil or tables {tab.x,tab.y,tab.width,tab.height},{..} until 10 times
each table has stored the coordinates for a object to get all results 
you must call that function muliply times until you got less than 10 arguments or nil.

if you have all the results before the result buffer is empty please flush it with:
cv.clearbuffer()
*/
int CV_lua::objects_next(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);
	int size = 0;

	if(buffer.size() == 0){
		lua_pushnil(L);
		return 1;
	}

	for(size_t i = 0; i < buffer.size() && i <= 10; i++){
		lua_createtable(L, 0, 4);

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"x");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"y");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"width");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"height");
		buffer.pop();//delete value 

		size = size + 1;

	}
	return size;
}
/*	cv.clearbuffer()
Clear the result buffer

Return nil
*/
int CV_lua::clearbuffer(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);

	while(!buffer.empty()){
		buffer.pop();
	}

	return 0;
}
/*	cv.loadimage(string file)
Load an image in the filter buffer

Return nil
*/
int CV_lua::loadimage(lua_State *L){
	/// Read the image
	if(lua_gettop(L)!=1)
		wrongArgs(L);
	checkType(L, LT_STRING, 1);

	filter = imread( lua_tostring(L, 1), 1 );

	if( !filter.data )
		wrongArgs(L);

	filter_flag = true;

	return 0;
}
/*	cv.saveimage(string file)
Save  an image from the filter buffer

Return nil
*/
int CV_lua::saveimage(lua_State *L){
	if(lua_gettop(L)!=1)
		wrongArgs(L);
	checkType(L, LT_STRING, 1);

	//check if something is in the filterbuffer
	if(filter_flag == false){
		printf("The filter is empty it make no sense to save an image \n");
	}
	//write image
	imwrite( lua_tostring(L, 1), filter );
	//clear filter buffer
	filter_flag = false;
	filter = Scalar::all(0);
	filter.resize(0);

	return 0;
}
int CV_lua::motion(lua_State *L){
	int argsn = lua_gettop(L);
	if(argsn > 7 || argsn < 1)
		wrongArgs(L);

	checkType(L, LT_NUMBER, 1);

	if(argsn > 1)
		checkType(L, LT_NUMBER, 2);
	if(argsn > 2)
		checkType(L, LT_NUMBER, 3);
	if(argsn > 3)
		checkType(L, LT_NUMBER, 4);
	if(argsn > 4)
		checkType(L, LT_NUMBER, 5);
	if(argsn > 5)
		checkType(L, LT_BOOLEAN, 6);
	if(argsn > 6)
		checkType(L, LT_BOOLEAN, 7);


	//global setup for return values

	HWND hwnd = (HWND)lua_tointeger(L, 1);

	Mat src; 

	if( hwnd == 0 )
		hwnd = GetDesktopWindow();

	src =  hwnd2mat(hwnd);


	Mat back;
	Mat fore;
	//MOG2 Background subtractor
	Ptr<BackgroundSubtractor> pMOG2;
	vector<vector<Point> > contours;
	int min_found = 3;
	DWORD pause = 100;
	int threshold = 16;
	int history = 15;


	bool shadows = true;
	bool debug_image = false;


	if(argsn > 1) 
		min_found = lua_tounsigned(L, 2);
	if(argsn > 2) 
		pause = (DWORD)lua_tounsigned(L, 3);
	if(argsn > 3)
		threshold = lua_tounsigned(L, 4);
	if(argsn > 4)
		history = lua_tounsigned(L, 5);
	if(argsn > 5) 
		shadows = lua_toboolean(L, 6);
	if(argsn > 6) 
		debug_image = lua_toboolean(L, 7);

	//create Background Subtractor objects
	pMOG2 = createBackgroundSubtractorMOG2(history, threshold, shadows); //MOG2 approach


	int foundcounter = 0;

	while( foundcounter < min_found  ){
		src = hwnd2mat(hwnd);

		pMOG2->apply(src, fore);
		//pMOG2->getBackgroundImage(back);

		erode(fore,fore,cv::Mat());
		dilate(fore,fore,cv::Mat());

		findContours (fore, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

		if(contours.size() > 0){
			foundcounter = foundcounter + 1;
			if(debug_image == true){
				drawContours (src, contours, -1, cv::Scalar (0, 0, 255), 2);
			}

		}
		// don't work too hard
		Sleep(pause);


	}
	//int p1,p2,p3,p4;
	int centerx,centery,width,height = 0;
	int min_x,max_x, min_y,max_y = 0;

	for( size_t i = 0; i < contours.size(); i++ ){
		
		centerx = 0;
		centery = 0;
		min_x = 0;
		min_y = 0;
		max_x = 0;
		max_y = 0;
		vector<Point> vec = contours[i];
		if( vec.size() > 0){

			min_x = vec[0].x;
			min_y = vec[0].y;

		}
		for( size_t j = 0; j < vec.size(); j++ ){
			Point point = vec[j];

			centerx = centerx + point.x;
			centery = centery + point.y;

			if(point.x > max_x){
				max_x = point.x;
			}
			if(point.x < min_x){
				min_x = point.x;
			}
			if(point.y > max_y){
				max_y = point.y;
			}
			if(point.y < min_y){
				min_y = point.y;
			}

		}
		width = max_x - min_x;
		height = max_y - min_y;

		centerx = centerx/(int)vec.size();
		centery = centery/(int)vec.size();

		buffer.push(centerx);
		buffer.push(centery);
		buffer.push(width);
		buffer.push(height);
	}

	//waitKey();
	if(debug_image){
		namedWindow("Micro Macro 2:cv_motion", WINDOW_NORMAL );
		imshow("Micro Macro 2:cv_motion", src);
		waitKey(0);
	}
	// avoid memory leak

	//push number of objects
	lua_pushunsigned(L,(lua_Unsigned)contours.size());

	return 1;
}
int CV_lua::motions_next(lua_State *L){
	if(lua_gettop(L)!=0)
		wrongArgs(L);
	int size = 0;

	if(buffer.size() == 0){
		lua_pushnil(L);
		return 1;
	}

	for(size_t i = 0; i < buffer.size() && i <= 10; i++){
		lua_createtable(L, 0, 4);

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"x");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"y");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"width");
		buffer.pop();//delete value 

		lua_pushnumber(L, buffer.front());
		lua_setfield(L, -2,"height");
		buffer.pop();//delete value 

		size = size + 1;

	}
	return size;
}