/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* ---- includes ----------------------------------------------------------- */

#include "FaceFinder_Internal.h"

/* ---- related objects  --------------------------------------------------- */

/* ---- typedefs ----------------------------------------------------------- */

/* ---- constants ---------------------------------------------------------- */

/* ------------------------------------------------------------------------- */

/* ========================================================================= */
/*                                                                           */
/* ---- functions ---------------------------------------------------------- */
/*                                                                           */
/* ========================================================================= */

/* ------------------------------------------------------------------------- */

void btk_FaceFinder_init( struct bbs_Context* cpA, struct btk_FaceFinder* ptrA )
{
	ptrA->hsdkE = NULL;
	ptrA->hidE = btk_HID_FF;

	bpi_FaceFinderRef_init( cpA, &ptrA->ffE );

	ptrA->facesE = 0;
	ptrA->faceIndexE = 0;
}

/* ------------------------------------------------------------------------- */

void btk_FaceFinder_exit( struct bbs_Context* cpA, struct btk_FaceFinder* ptrA )
{
	ptrA->hsdkE = NULL;
	ptrA->hidE = btk_HID_FF;

	bpi_FaceFinderRef_exit( cpA, &ptrA->ffE );

	ptrA->facesE = 0;
	ptrA->faceIndexE = 0;
}

/* ------------------------------------------------------------------------- */

btk_FaceFinderCreateParam btk_FaceFinder_defaultParam()
{
	btk_FaceFinderCreateParam paramL;
	paramL.reserved = 0;
	paramL.pModuleParam = NULL;
	paramL.moduleParamSize = 0;
	paramL.maxDetectableFaces = 0;
	return paramL;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_create( btk_HSDK hsdkA,     /* sdk handle */
								  const btk_FaceFinderCreateParam* pCreateParamA,
								  btk_HFaceFinder* hpFaceFinderA )
{
	const char* fNameL = "btk_FaceFinder_create";

	btk_HFaceFinder hFaceFinderL = NULL;

	if( hpFaceFinderA == NULL )					return btk_STATUS_INVALID_HANDLE;
	if( *hpFaceFinderA != NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hsdkA == NULL )							return btk_STATUS_INVALID_HANDLE;
	if( hsdkA->hidE != btk_HID_SDK )			return btk_STATUS_INVALID_HANDLE;
	if( pCreateParamA == NULL )					return btk_STATUS_INVALID_HANDLE;
	if( bbs_Context_error( &hsdkA->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	hFaceFinderL = ( btk_HFaceFinder )bbs_MemSeg_alloc( &hsdkA->contextE, hsdkA->contextE.memTblE.espArrE[ 0 ], bbs_SIZEOF16( struct btk_FaceFinder ) );
	if( bbs_Context_error( &hsdkA->contextE ) ) return btk_STATUS_ERROR;

	btk_FaceFinder_init( &hsdkA->contextE, hFaceFinderL );
	if( bbs_Context_error( &hsdkA->contextE ) ) return btk_STATUS_ERROR;

	hFaceFinderL->hsdkE = hsdkA;

	if( btk_SDK_paramConsistencyTest( hsdkA, pCreateParamA->pModuleParam, pCreateParamA->moduleParamSize, fNameL ) == btk_STATUS_ERROR ) return btk_STATUS_ERROR;

	if( hsdkA->maxImageWidthE * hsdkA->maxImageHeightE == 0 )
	{
		bbs_Context_pushError( &hsdkA->contextE, 
			                   bbs_Error_create( bbs_ERR_ERROR, 0, NULL, "%s:\nSDK parameter maxImageWidth or maxImageWidth is 0!\n"
							                                             "Since SDK version 1.3.0 the maximum image size must be specified when creating the SDK handle.\n"
																		 "Set the values in *pCreateParamA when you call function btk_SDK_create.", fNameL ) );
		return btk_STATUS_ERROR;
	}

	bpi_FaceFinderRef_memRead( &hsdkA->contextE,
							   &hFaceFinderL->ffE,
							   hsdkA->maxImageWidthE,
							   hsdkA->maxImageHeightE,
							   pCreateParamA->pModuleParam,
							   &hsdkA->contextE.memTblE );

	if( bbs_Context_error( &hsdkA->contextE ) ) return btk_STATUS_ERROR;

	*hpFaceFinderA = hFaceFinderL;
	hsdkA->refCtrE++;

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_close( btk_HFaceFinder hFaceFinderA )
{
	btk_HSDK hsdkL = NULL;
	if( hFaceFinderA == NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hidE != btk_HID_FF )	return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hsdkE == NULL )		return btk_STATUS_INVALID_HANDLE;
	hsdkL = hFaceFinderA->hsdkE;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	hsdkL->refCtrE--;

	btk_FaceFinder_exit( &hsdkL->contextE, hFaceFinderA );
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	bbs_MemSeg_free( &hsdkL->contextE, hsdkL->contextE.memTblE.espArrE[ 0 ], hFaceFinderA );
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_setRange( btk_HFaceFinder hFaceFinderA,
								    u32 minDistA,
									u32 maxDistA )
{
	btk_HSDK hsdkL = NULL;
	if( hFaceFinderA == NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hidE != btk_HID_FF )	return btk_STATUS_INVALID_HANDLE;
	hsdkL = hFaceFinderA->hsdkE;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	bpi_FaceFinderRef_setRange( &hsdkL->contextE, &hFaceFinderA->ffE, minDistA, maxDistA );
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_putDCR( btk_HFaceFinder hFaceFinderA,
								  btk_HDCR hdcrA )
{
	const char* fNameL = "btk_FaceFinder_putDCR";

	btk_HSDK hsdkL = NULL;
	if( hFaceFinderA == NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hidE != btk_HID_FF )	return btk_STATUS_INVALID_HANDLE;
	if( hdcrA == NULL )			return btk_STATUS_INVALID_HANDLE;
	hsdkL = hFaceFinderA->hsdkE;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	if( hdcrA->dcrE.imageDataPtrE == NULL )
	{
		bbs_Context_pushError( &hsdkL->contextE,
			                   bbs_Error_create( bbs_ERR_ERROR, 0, NULL,
							       "%s:\nNo image was assigned to data carrier", fNameL ) );
	}

	hFaceFinderA->facesE = bpi_FaceFinderRef_putDcr( &hsdkL->contextE,
												     &hFaceFinderA->ffE,
													 &hdcrA->dcrE );

	hFaceFinderA->faceIndexE = 0;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

u32 btk_FaceFinder_faces( btk_HFaceFinder hFaceFinderA )
{
	if( hFaceFinderA == NULL )				return 0;
	if( hFaceFinderA->hidE != btk_HID_FF )	return 0;
	return hFaceFinderA->facesE - hFaceFinderA->faceIndexE;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_getDCR( btk_HFaceFinder hFaceFinderA,
								  btk_HDCR hdcrA )
{
	btk_HSDK hsdkL = NULL;
	if( hFaceFinderA == NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hidE != btk_HID_FF )	return btk_STATUS_INVALID_HANDLE;
	if( hdcrA == NULL )			return btk_STATUS_INVALID_HANDLE;
	hsdkL = hFaceFinderA->hsdkE;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	if( hFaceFinderA->faceIndexE < hFaceFinderA->facesE )
	{
		bpi_FaceFinderRef_getDcr( &hsdkL->contextE,
								  &hFaceFinderA->ffE,
								   hFaceFinderA->faceIndexE,
								  &hdcrA->dcrE );

		if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

		hdcrA->dcrE.approvedE = TRUE;
		hFaceFinderA->faceIndexE++;
	}
	else
	{
		bpi_FaceFinderRef_getDcr( &hsdkL->contextE,
								  &hFaceFinderA->ffE,
								  0,
								  &hdcrA->dcrE );
		hdcrA->dcrE.approvedE = FALSE;
	}

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

btk_Status btk_FaceFinder_process( btk_HFaceFinder hFaceFinderA,
								   btk_HDCR hdcrA )
{
	const char* fNameL = "btk_FaceFinder_process";
	int32 confL;

	btk_HSDK hsdkL = NULL;
	if( hFaceFinderA == NULL )				return btk_STATUS_INVALID_HANDLE;
	if( hFaceFinderA->hidE != btk_HID_FF )	return btk_STATUS_INVALID_HANDLE;
	if( hdcrA == NULL )						return btk_STATUS_INVALID_HANDLE;
	hsdkL = hFaceFinderA->hsdkE;
	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_PREEXISTING_ERROR;

	if( hdcrA->dcrE.imageDataPtrE == NULL )
	{
		bbs_Context_pushError( &hsdkL->contextE,
			                   bbs_Error_create( bbs_ERR_ERROR, 0, NULL,
							       "%s:\nNo image was assigned to data carrier", fNameL ) );
	}

	confL = bpi_FaceFinderRef_process( &hsdkL->contextE,
									   &hFaceFinderA->ffE,
									   &hdcrA->dcrE );

	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	hdcrA->dcrE.confidenceE = confL;
	hdcrA->dcrE.approvedE = confL > ( ( int32 )1 << 23 );

	hFaceFinderA->faceIndexE = 0;
	hFaceFinderA->facesE = 0;

	bts_IdCluster2D_copy( &hsdkL->contextE,
		                  &hdcrA->dcrE.sdkClusterE,
						  &hdcrA->dcrE.mainClusterE );

	if( bbs_Context_error( &hsdkL->contextE ) ) return btk_STATUS_ERROR;

	return btk_STATUS_OK;
}

/* ------------------------------------------------------------------------- */

/* ========================================================================= */