/*
 * 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 "b_APIEm/Functions.h"
#include "b_BasicEm/Memory.h"


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

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

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

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

/* ========================================================================= */
/*                                                                           */
/* ---- \ghd{ external functions } ----------------------------------------- */
/*                                                                           */
/* ========================================================================= */

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

void bpi_normalizeSimilarities( struct bbs_Context* cpA,
							    const int32* rawSimArrA,
							    const int32* rawIdArrA,
								uint32 rawSizeA,
								const int32* refSimArrA,
								const int32* refIdArrA,
								uint32 refSizeA,
								enum bpi_SimType simTypeA,
								int32* outSimArrA )
{
	/* 8.24 */
	int32 refSimL = 0;
	uint32 iL, jL, kL; 
	int32* outPtrL = outSimArrA;
	const int32* rawPtrL = rawSimArrA;

	switch( simTypeA )
	{
		case bpi_RAW_SIM: 
		{
			/* nothing to do */
		}
		break;

		case bpi_SUB_MEAN:
		{
			int32 shiftL = 0;
			int32 roundL = 0;
			refSimL = 0;
			for( iL = 0; iL < refSizeA; iL++ )
			{
				refSimL += ( refSimArrA[ iL ] + roundL ) >> shiftL;
				if( refSimL > 0x40000000 )
				{
					refSimL = ( refSimL + 1 ) >> 1;
					shiftL++;
					roundL = ( int32 )1 << ( shiftL - 1 );
				}
			}
			refSimL = ( refSimL / refSizeA ) << shiftL;
		}
		break;

		case bpi_SUB_MAX_2:
		{
			int32 maxL = 0;
			uint32 maxIndexL = 0;
			int32 idL = 0;

			/* find raw maximum */
			for( iL = 0; iL < rawSizeA; iL++ )
			{
				if( maxL < rawSimArrA[ iL ] )
				{
					maxL = refSimArrA[ iL ];
					maxIndexL = iL;
				}
			}

			/* consider id of maximum equal to probe id */
			idL = rawIdArrA[ maxIndexL ];

			/* find maximum similarity in ref array of different id */
			for( iL = 0; iL < refSizeA; iL++ )
			{
				if( refIdArrA[ iL ] != idL )
				{
					refSimL = ( refSimL > refSimArrA[ iL ] ) ? refSimL : refSimArrA[ iL ];
				}
			}
		}
		break;

		case bpi_SUB_16_MAX_2:
		{
			int32 maxL = 0;
			uint32 maxIndexL = 0;
			int32 idL = 0;

			int32 maxSimArrL[ 16 ];
			bbs_memset32( maxSimArrL, ( uint32 )-1, bbs_SIZEOF32( maxSimArrL ) );

			/* find raw maximum */
			for( iL = 0; iL < rawSizeA; iL++ )
			{
				if( maxL < rawSimArrA[ iL ] )
				{
					maxL = rawSimArrA[ iL ];
					maxIndexL = iL;
				}
			}

			/* consider id of maximum equal to probe id */
			idL = rawIdArrA[ maxIndexL ];

			/* find 16 maximum similarities of different id in ref array */
			for( iL = 0; iL < refSizeA; iL++ )
			{
				if( refIdArrA[ iL ] != idL )
				{
					int32 simL = refSimArrA[ iL ];
					for( jL = 0; jL < 16; jL++ )
					{
						if( simL > maxSimArrL[ jL ] ) break;
					}
					for( kL = 15; kL > jL; kL-- )
					{
						maxSimArrL[ kL ] = maxSimArrL[ kL - 1 ];
					}
					if( jL < 16 ) maxSimArrL[ jL ] = simL;
				}
			}

			refSimL = 0;
			for( jL = 0; jL < 16; jL++ )
			{
				if( maxSimArrL[ jL ] == -1 ) break;
				refSimL += maxSimArrL[ jL ];
			}

			if( jL > 0 )
			{
				refSimL /= jL;
			}
		}
		break;

		default:
		{
			bbs_ERROR1( "void bpi_Identifier_normalizeSimilarities(): simTypeA '%i' is handled", simTypeA );
			return;
		}
	}

	/* refSimL -= 1.0 */
	refSimL -= ( (uint32)1 << 24 );

	for( iL = rawSizeA; iL > 0; iL-- )
	{
		*outPtrL++ = ( *rawPtrL++ - refSimL + 1 ) >> 1;
	}

}

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

int32 bpi_normalizedSimilarity( struct bbs_Context* cpA,
							    int32 rawSimA,
							    int32 rawIdA,
								const int32* refSimArrA,
								const int32* refIdArrA,
								uint32 refSizeA,
								enum bpi_SimType simTypeA )
{
	/* 8.24 */
	int32 refSimL = 0;
	uint32 iL, jL, kL; 

	switch( simTypeA )
	{
		case bpi_RAW_SIM: 
		{
			/* nothing to do */
			return rawSimA; /* return without adjustment of value range */
		}

		case bpi_SUB_MEAN:
		{
			int32 shiftL = 0;
			int32 roundL = 0;
			refSimL = 0;
			for( iL = 0; iL < refSizeA; iL++ )
			{
				refSimL += ( refSimArrA[ iL ] + roundL ) >> shiftL;
				if( refSimL > 0x40000000 )
				{
					refSimL = ( refSimL + 1 ) >> 1;
					shiftL++;
					roundL = ( int32 )1 << ( shiftL - 1 );
				}
			}
			refSimL = ( refSimL / refSizeA ) << shiftL;
		}
		break;

		case bpi_SUB_MAX_2:
		{
			/* find maximum similarity in ref array of different rawIdA */
			for( iL = 0; iL < refSizeA; iL++ )
			{
				if( refIdArrA[ iL ] != rawIdA )
				{
					refSimL = ( refSimL > refSimArrA[ iL ] ) ? refSimL : refSimArrA[ iL ];
				}
			}
		}
		break;

		case bpi_SUB_16_MAX_2:
		{
			int32 maxSimArrL[ 16 ];
			int32 idL = rawIdA;
			bbs_memset32( maxSimArrL, ( uint32 )-1, bbs_SIZEOF32( maxSimArrL ) );

			/* find 16 maximum similarities of different id in ref array */
			for( iL = 0; iL < refSizeA; iL++ )
			{
				if( refIdArrA[ iL ] != idL )
				{
					int32 simL = refSimArrA[ iL ];
					for( jL = 0; jL < 16; jL++ )
					{
						if( simL > maxSimArrL[ jL ] ) break;
					}
					for( kL = 15; kL > jL; kL-- )
					{
						maxSimArrL[ kL ] = maxSimArrL[ kL - 1 ];
					}
					if( jL < 16 ) maxSimArrL[ jL ] = simL;
				}
			}

			refSimL = 0;
			for( jL = 0; jL < 16; jL++ )
			{
				if( maxSimArrL[ jL ] == -1 ) break;
				refSimL += maxSimArrL[ jL ];
			}

			if( jL > 0 )
			{
				refSimL /= jL;
			}
		}
		break;

		default:
		{
			bbs_ERROR1( "void bpi_Identifier_normalizeSimilarities(): simTypeA '%i' is handled", simTypeA );
		}
		break;
	}

	/* refSimL -= 1.0 */
	refSimL -= ( (uint32)1 << 24 );
	return ( rawSimA - refSimL + 1 ) >> 1;
}

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

uint32 bpi_memWriteCsa16( uint16* memPtrA, uint32 memSizeA, uint16 chkSumA )
{
	uint16* memPtrL = memPtrA - memSizeA + 1;
	uint32 iL;
	uint16 sumL = 0;
	uint16 csaL = 0;

	bbs_memWrite16( &csaL, memPtrA );
	for( iL = 0; iL < memSizeA; iL++ )
	{
		uint16 valL = 0;
		memPtrL += bbs_memRead16( &valL, memPtrL );
		sumL += valL;
	}
	csaL = chkSumA - sumL;

	return bbs_memWrite16( &csaL, memPtrA );
}

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

uint32 bpi_memReadCsa16( const uint16* memPtrA )
{
	return bbs_SIZEOF16( uint16 );
}

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