/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | This source file is subject to BSD 3-Clause license,                 |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://opensource.org/licenses/BSD-3-Clause.                         |
  | If you did not receive a copy of the BSD license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | robert@xarg.org so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author: Robert Eisele <robert@xarg.org>                              |
  +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_facedetect.h"

#include "opencv2/core/core_c.h"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/photo/photo_c.h"
#include "opencv2/objdetect/objdetect_c.h"

#include "opencv2/core/version.hpp"

/* {{{ facedetect_functions[]
 *
 * Every user visible function must have an entry in facedetect_functions[].
 */
static zend_function_entry facedetect_functions[] = {
    PHP_FE(face_detect, NULL)
    PHP_FE(face_count, NULL)
    {NULL, NULL, NULL}
};
/* }}} */

/* {{{ facedetect_module_entry
 */
zend_module_entry facedetect_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_FACEDETECT_EXTNAME,
    facedetect_functions,
    NULL,
    NULL,
    NULL,
    NULL,
    PHP_MINFO(facedetect),
#if ZEND_MODULE_API_NO >= 20010901
    PHP_FACEDETECT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_FACEDETECT
ZEND_GET_MODULE(facedetect)
#endif

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINFO_FUNCTION(facedetect)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "facedetect support", "enabled");
	php_info_print_table_row(2, "facedetect version", PHP_FACEDETECT_VERSION);
	php_info_print_table_row(2, "OpenCV version", CV_VERSION);
	php_info_print_table_end();
}
/* }}} */

IplImage* cvLoadImage(char *file, int number);

static void php_facedetect(INTERNAL_FUNCTION_PARAMETERS, int return_type)
{
	char *file, *casc;
	long flen, clen;

#ifdef ZEND_ENGINE_3
	zval array;
#else
	zval *array;
#endif
	zval *pArray;

	CvHaarClassifierCascade* cascade;
	IplImage *img, *gray;
	CvMemStorage *storage;
	CvSeq *faces;
	CvRect *rect;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &file, &flen, &casc, &clen) == FAILURE) {
		RETURN_NULL();
	}

	if(access(file, R_OK) == -1) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Image file is missing or could not be read.\n");
		RETURN_FALSE;
	}
	
	img = cvLoadImage(file, 1);
	if(!img) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Image could not be loaded.\n");
		RETURN_FALSE;
	}

	if(access(casc, R_OK) == -1) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Haar-cascade file is missing or could not be read.\n");
		RETURN_FALSE;
	}
	cascade = (CvHaarClassifierCascade*)cvLoad(casc, 0, 0, 0);
	if(!cascade) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Haar-cascade file could not be loaded.\n");
		RETURN_FALSE;
	}

	gray = cvCreateImage(cvSize(img->width, img->height), 8, 1);
	cvCvtColor(img, gray, CV_BGR2GRAY);
	cvEqualizeHist(gray, gray);

	storage = cvCreateMemStorage(0);

	faces = cvHaarDetectObjects(gray, cascade, storage, 1.1, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(0, 0), cvSize(0, 0));

	if(return_type) {

		array_init(return_value);

		if(faces && faces->total > 0) {
			int i;
			for(i = 0; i < faces->total; i++) {
#ifdef ZEND_ENGINE_3
				ZVAL_NEW_ARR(&array);
				pArray = &array;
#else
				MAKE_STD_ZVAL(array);
				pArray = array;
#endif
				array_init(pArray);

				rect = (CvRect *)cvGetSeqElem(faces, i);

				add_assoc_long(pArray, "x", rect->x);
				add_assoc_long(pArray, "y", rect->y);
				add_assoc_long(pArray, "w", rect->width);
				add_assoc_long(pArray, "h", rect->height);

				add_next_index_zval(return_value, pArray);
			}
		}
	} else {
		RETVAL_LONG(faces ? faces->total : 0);
	}

	cvReleaseMemStorage(&storage);
	cvReleaseImage(&gray);
	cvReleaseImage(&img);
}

/* {{{ proto array face_detect(string picture_path, string cascade_file)
   Finds coordinates of faces (or in gernal "objects") on the given picture */
PHP_FUNCTION(face_detect)
{
	php_facedetect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */

/* {{{ proto int face_count(string picture_path, string cascade_file)
   Finds number of faces (or in gernal "objects") on the given picture*/
PHP_FUNCTION(face_count)
{
	php_facedetect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */

