/*---------------------------------------------------------------------------*
  Project:  NitroSDK - PRC - 
  File:     prc_algo_light.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: prc_algo_light.c,v $
  Revision 1.4  2004/07/19 06:52:06  seiki_masashi
  Revised identifier names

  Revision 1.3  2004/07/05 02:37:54  seiki_masashi
  Removed prevRatio

  Revision 1.2  2004/06/30 12:53:44  seiki_masashi
  Deleted ASSERT that could be harmful

  Revision 1.1  2004/06/30 08:49:24  seiki_masashi
  algo_simple -> algo_light
  algo_standard -> algo_fine
  algo_accurate -> algo_superfine
  Added algo_standard

  Revision 1.5  2004/06/29 07:25:02  seiki_masashi
  Bug fixes
  Implemented pruning

  Revision 1.4  2004/06/25 03:02:55  seiki_masashi
  Deleted bufferSize specification from all functions
  Added GetRecognitionBufferSize* 

  Revision 1.3  2004/06/24 08:08:33  seiki_masashi
  fix typo

  Revision 1.2  2004/06/24 07:03:39  seiki_masashi
  Divided and revised header files

  Revision 1.1  2004/06/23 11:59:30  seiki_masashi
  add 'PRC*' to SDK

  Revision 1.3  2004/06/22 11:19:00  seiki_masashi
  Functional version (light only)

  Revision 1.2  2004/06/17 08:14:52  seiki_masashi
  PRC_GetPrototypeDBBufferSizeEx

  Revision 1.1  2004/06/17 02:30:29  seiki_masashi
  Added prc_algo_* to facilitate recognition algorithm switching

  Revision 1.2  2004/06/17 00:52:36  seiki_masashi
  PRC_InitPrototypeDBEX, PRC_InitInputPatternEX

  Revision 1.1  2004/06/16 23:56:09  seiki_masashi
  rename ptrcg to prc.


  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#include <nitro.h>
#include <nitro/prc/algo_light.h>

/*===========================================================================*
  Prototype Declarations
 *===========================================================================*/

/*===========================================================================*
  Variable Definitions
 *===========================================================================*/

/*===========================================================================*
  Functions
 *===========================================================================*/

/*---------------------------------------------------------------------------*
  Name:         PRC_GetRecognitionBufferSizeEx_Fine

  Description:  Computes the size of the work area required for the recognition algorithm. 
 
  Arguments:    maxPointCount   Maximum number of input points (including pen up marker)
                maxStrokeCount  Maximum number of strokes
                protoDB       Sample database.
                param           Parameters affecting the recognition process.

  Returns:      Memory size required for recognition algorithm.
 *---------------------------------------------------------------------------*/
u32
PRC_GetRecognitionBufferSizeEx_Light(
        int                         maxPointCount,
        int                         maxStrokeCount,
        const PRCPrototypeDB_Light*          protoDB,
        const PRCRecognizeParam_Light*       param )
{
    (void)maxPointCount;
    (void)maxStrokeCount;
    (void)protoDB;
    (void)param;
    
    return 1; // Because OS_Alloc(0) causes an error
}

/*---------------------------------------------------------------------------*
   Name:         PRC_GetRecognizedEntriesEx_Light
     
   Description:  Compares the input pattern with the database entries of the specified kind,
                and returns the number of entries specified by numRanking. 

  Arguments:    resultEntries   Pointer to array containing pointers to recognition results.
                                If fewer than needed are recognized, the remainder are filled with NULL.
                resultScores   Pointer to array with the recognition result scores.
                numRanking      Number returned to result* 
                buffer         Pointer to the memory area used by the recognition algorithm.
                                (Memory size >= the return value of PRC_GetRecognitionBufferSize)
                input           Input pattern.  
                protoDB         Sample database.
                kindMask        Computes the logical product of each entry's kind value.
                                    Non-zero values are treated as valid. 
                param          Parameters affecting the recognition process.

  Returns:      The number of patterns in the sample database that were compared.

 *---------------------------------------------------------------------------*/
int
PRC_GetRecognizedEntriesEx_Light(
        PRCPrototypeEntry**                 resultEntries,
        fx32*                               resultScores,
        int                                 numRanking,
        void*                               buffer,
        const PRCInputPattern_Light*       input,
        const PRCPrototypeDB_Light* protoDB,
        u32                                 kindMask,
        const PRCRecognizeParam_Light*     param )
{
    int i;
    const PRCiPatternData_Common* inputData;
    int numCompared;

    (void)buffer;
    (void)param;

    SDK_ASSERT(resultEntries);
    SDK_ASSERT(resultScores);
    SDK_ASSERT(input);
    SDK_ASSERT(protoDB);
    SDK_ASSERT(numRanking>0);

    for (i=0; i<numRanking; i++) 
    {
        resultEntries[i] = NULL;
        resultScores[i] = FX32_ONE*32768;
    }

    inputData = &input->data;
    numCompared = 0;

    {
        const PRCiPrototypeEntry_Common* proto;
        int iPattern;
        fx32 border;

        border = FX32_ONE; // Cutoff level: If this value is set to FX32_ONE/2,  then those with a score below 0.5 will not be evaluated at all. 
        border *= 32768; // Internal score factor 

        proto = protoDB->patterns;
        
        for (iPattern=0; iPattern<protoDB->patternCount; iPattern++, proto++)
        {
            const PRCiPatternData_Common* protoData;
            int iStroke;
            fx32 wholeScore;
            fx32 patternBorder;
            
            if ( !proto->entry->enabled || !(proto->entry->kind & kindMask) )
                continue;
            
            protoData = &proto->data;
            
            if (inputData->strokeCount != protoData->strokeCount)
                continue;
            
            wholeScore = 0;
            
            // Set the cutoff point for reversed correction 
            patternBorder = border;
            if ( proto->entry->correction != 0 )
            {
                // Approximate patternBorder *= 1 / (FX32_ONE - correction) by setting a larger cutoff 
                // with a straight line
                if ( proto->entry->correction < -FX32_ONE )
                {
                    patternBorder >>= 1; // Less than 1/2 is a match 
                }
                else if ( proto->entry->correction < 0 )
                {
                    // If -1 < correction < 0, correction/2+1 is a match
                    patternBorder = FX_Mul(patternBorder, FX32_ONE + proto->entry->correction/2);
                }
                else if ( proto->entry->correction < FX32_ONE/2 )
                {
                    // If 0 < correction < 1/2, correction*2+1 is a match
                    patternBorder = FX_Mul(patternBorder, FX32_ONE + proto->entry->correction*2);
                }
                else { patternBorder = FX32_ONE*32768; } // No elimination
            }
            
            for (iStroke=0; iStroke<inputData->strokeCount; iStroke++)
            {
                int iProto, iInput;
                int protoStrokeIndex, inputStrokeIndex;
                int protoSize, inputSize;
                const u16* protoAngle;
                const u16* inputAngle;
                const fx16* protoRatio;
                const fx16* inputRatio;
                fx16 protoNextRatio, inputNextRatio;
                fx32 score;
                fx32 localBorder;
                fx16 strokeRatio;
                int loopEnd;
                
                score = 0;
                
                protoStrokeIndex = protoData->strokes[iStroke];
                inputStrokeIndex = inputData->strokes[iStroke];
                protoSize = protoData->strokeSizes[iStroke];
                inputSize = inputData->strokeSizes[iStroke];
                protoAngle = &protoData->lineSegmentAngleArray[protoStrokeIndex];
                inputAngle = &inputData->lineSegmentAngleArray[inputStrokeIndex];
                protoRatio = &protoData->lineSegmentRatioToStrokeArray[protoStrokeIndex];
                inputRatio = &inputData->lineSegmentRatioToStrokeArray[inputStrokeIndex];
                
                strokeRatio = protoData->strokeRatios[iStroke]; // Use this with inputData-> to add weighting based on the ratio of the input strokes
                
                // Set the elimination level
                if ( strokeRatio == FX32_ONE )
                {
                    localBorder = patternBorder;
                }
                else if ( strokeRatio >= FX32_ONE/2 )
                {
                    localBorder = patternBorder * 2; // No problem with taking excess
                }
                else if ( strokeRatio >= FX32_ONE/3 )
                {
                    localBorder = patternBorder * 3; // No problem with taking excess
                }
                else
                {
                    localBorder = FX32_ONE * 32768; // No elimination
                }
                
                SDK_ASSERT(protoSize>=2);
                SDK_ASSERT(inputSize>=2);
                
                iProto = iInput = 1;
                protoNextRatio = protoRatio[iProto];
                inputNextRatio = inputRatio[iInput];
                loopEnd = protoSize+inputSize-3;
                for (i=0; i<loopEnd; i++)
                {
                    int diff;
                    SDK_ASSERT( iProto < protoSize );
                    SDK_ASSERT( iInput < inputSize );
                    diff = (s16)(protoAngle[iProto]-inputAngle[iInput]);
                    if ( diff < 0 ) { diff = -diff; }
                    if ( protoNextRatio <= inputNextRatio )
                    {
                        score += protoNextRatio * diff;
                        iProto++;
                        inputNextRatio -= protoNextRatio;
                        protoNextRatio = protoRatio[iProto];
                    }
                    else
                    {
                        score += inputNextRatio * diff;
                        iInput++;
                        protoNextRatio -= inputNextRatio;
                        inputNextRatio = inputRatio[iInput];
                    }
                    // Check the cutoff
                    if ( score > localBorder )
                    {
                        // At this point, no hope of even a poor match
                        wholeScore = FX32_ONE * 32768;
                        goto quit_compare;
                    }
                }
                
                wholeScore += FX_Mul(score, strokeRatio);
            }
            
            if ( proto->entry->correction != 0 )
            {
                wholeScore = FX_Mul(wholeScore, FX32_ONE - proto->entry->correction);
            }

//                wholeScore = FX_Mul(wholeScore, FX32_ONE - proto->entry->correction)
//                            + proto->entry->correction;

            //  Note that low scores are better at this stage 
quit_compare:
            numCompared++;
            if ( resultScores[numRanking-1] > wholeScore )
            {
                resultScores[numRanking-1] = wholeScore;
                resultEntries[numRanking-1] = (PRCPrototypeEntry*)proto->entry;
                for (i=numRanking-2; i>=0; i--)
                {
                    if ( resultScores[i] > resultScores[i+1] )
                    {
                        fx32 tmpScore;
                        PRCPrototypeEntry* tmpEntry;
                        tmpScore = resultScores[i];
                        resultScores[i] = resultScores[i+1];
                        resultScores[i+1] = tmpScore;
                        tmpEntry = resultEntries[i];
                        resultEntries[i] = resultEntries[i+1];
                        resultEntries[i+1] = tmpEntry;
                    }
                }
                // Set the cutoff score 
                border = resultScores[numRanking-1];
            }
        }
    }
    // Normalize the score
    {
        int iRank;
        fx32 score;
        for ( iRank=0; iRank<numRanking; iRank++ )
        {
            if ( resultEntries[iRank] == NULL ) break;
            
            score = resultScores[iRank];
            score = FX32_ONE - (score/32768);
            if ( score < 0 ) score = 0;
            if ( score >= FX32_ONE ) score = FX32_ONE;
            
            resultScores[iRank] = score;
        }
    }
    
    return numCompared;
}

/*===========================================================================*
  Static Functions
 *===========================================================================*/


/*---------------------------------------------------------------------------*
  End of file
 *---------------------------------------------------------------------------*/
