/****************************************************************************
 * CCDECODE.C                                                               *
 *                                                                          *
 * Purpose: provides functions to decode closed captioning data from the    *
 *          Rage Theatre chip.                                              *
 *                                                                          *
 * Copyright (C) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>
#include <string.h>
#include "..\util\main.h"
#include "..\util\defines.h"
#include "..\util\regdef.h"

#ifdef BOOL
    #undef BOOL
#endif

//typedef unsigned char BOOL;

#define BIT_DURATION1520            56
#define BIT_DURATION1520_FRACTION   0
#define BLNK_START1520              486
#define N_BITS_PER_FIELD 16

static WORD pattern[17] = {0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8091,16183,32367,65535};

/****************************************************************************
 * WORD RT_DecodeCCData (BYTE *Data)                                        *
 *                                                                          *
 *   Purpose:   Used to decode two bytes of CC encoded in each CC-data line *
 *   Inputs:    BYTE * Data : pointer to the encoded CC-line                *
 *   Output:    the two bytes encoded in the CC-data line,                  *
 *              or ZERO if decoding error occured.                          *
 ****************************************************************************/
WORD RT_DecodeCCData (BYTE *Data)
{
//    BOOL    fCurr_Log_Level;
    unsigned char    fCurr_Log_Level;
    WORD    wCurr_pos;
    WORD    wCurrSeqStart;
    WORD    wCurrSeqEnd;
    WORD    wDetected_Bits_Numb = 0;
    WORD    wCurr_seq_width;
    WORD    wCurr_Detected_Bits_Num;
//    BOOL    fCriterion;
    unsigned char    fCriterion;
    WORD    wResult_CC_2_bytes = 0;
    WORD    CC_2_bytes;
//    BOOL    fStartBitPassed = FALSE;
    unsigned char    fStartBitPassed = FALSE;
    WORD    i;
    WORD    wLevelLow  = 0;
    WORD    wLevelHi = 0;
    WORD    wMidLevel;
    WORD    wPos;
    WORD    wDataStart = 0;
    WORD    wDataEnd = 0;

    static WORD wDebugLevelThreshold = 0x20;

    DWORD dwVbiLineTrim = 40;

    WORD wBitDuration   = BIT_DURATION1520;
    WORD wBlankStart    = (WORD)BLNK_START1520 - (WORD)(dwVbiLineTrim);
    WORD wPhase1_Param1 = 3 * wBitDuration + (3 * BIT_DURATION1520_FRACTION) /100;
    WORD wPhase1_Param2 = 2 * wBitDuration + (2 * BIT_DURATION1520_FRACTION) /100;
    WORD wPhase2_Param1 = wBlankStart + wBitDuration;
    WORD wPhase2_Param2 = wBlankStart + (4 * wBitDuration + (4 * BIT_DURATION1520_FRACTION) /100);
    WORD wPhase2_Param3 = wBitDuration / 2;
    WORD wPhase2_Param4 = 17 * wBitDuration + (17 * BIT_DURATION1520_FRACTION) /100;

    // Phase 1 ESTIMATION OF CC PARAMETERS
    // 1. Estimate the Low (0) Level:
    wPos = wBlankStart;
    wLevelLow = Data[wPos];
    for (i=wPos; i <= (wPos + wPhase1_Param1); i++)
    {
        if (wLevelLow > Data[i])
        {
            wLevelLow = Data[i];
        }
    }

    // 2. Estimate the High (1) Level:
    wPos = wBlankStart + wPhase1_Param2;
    wLevelHi = wLevelLow;
    for (i = wPos; i <= (wPos + wPhase1_Param2); i++)
    {
        if (wLevelHi < Data[i])
        {
            wLevelHi = Data[i];
        }
    }

    if (wLevelHi == wLevelLow)
    {
        return ((WORD)0);
    }

    if ((wLevelHi - wLevelLow) < wDebugLevelThreshold)
    {
        return ((WORD)0);
    }

    wMidLevel = wLevelLow + (wLevelHi - wLevelLow)/2;
    if (wMidLevel == wLevelLow)
    {
        return ((WORD)0);
    }

    // Phase 2 FIND STARTING BIT
    fCriterion = FALSE;
    wCurr_pos = wPhase2_Param1; // Around the middle of the Blank Pulse (assumption)
    while ((wCurr_pos <= (WORD)(wPhase2_Param2)) && !fCriterion)
    {

         if ((Data[wCurr_pos + 0]  < wMidLevel) &&
             (Data[wCurr_pos + 1]  < wMidLevel) &&
             (Data[wCurr_pos + 3]  > wMidLevel) &&
             (Data[wCurr_pos + 4]  > wMidLevel))
         {
              fCriterion = TRUE; // at wCurr_pos. Start Bit is coming...
              break;
         }
         else
         {
              fCriterion = FALSE;
              wCurr_pos ++;
         }
    }

    if (fCriterion)  // START BIT FOUND
    {
        fCurr_Log_Level = 1;
        wDataStart  = wCurr_pos + 1 + (wPhase2_Param3);  // middle of the "START"
        wDataEnd    = wCurr_pos + wPhase2_Param4 ;   // middle of the last pulse
        wCurr_pos       = wDataStart ; // The middle of the Start Pulse
        wCurrSeqStart   = wDataStart;
    }
    else            // ERROR: START NOT BIT FOUND
    {
        return (WORD)0;
    }

    // Phase 3 DETECTION  OF TWO CC-DATA USEFULL BYTES

    while (wCurr_pos <= wDataEnd)
    {
        fCriterion = FALSE;
        switch (fCurr_Log_Level)
        {
        case(1):
            if ((Data[wCurr_pos + 0]  > wMidLevel) &&
                (Data[wCurr_pos + 1]  > wMidLevel) &&
                (Data[wCurr_pos + 3]  < wMidLevel) &&
                (Data[wCurr_pos + 4]  < wMidLevel))
                fCriterion = TRUE;
                break;
        case(0):
            if ((fCurr_Log_Level==0) && (Data[wCurr_pos + 0]  < wMidLevel) &&
                (Data[wCurr_pos + 1]  < wMidLevel) &&
                (Data[wCurr_pos + 3]  > wMidLevel) &&
                (Data[wCurr_pos + 4]  > wMidLevel))
               fCriterion = TRUE;
               break;
        }

        // If the "data end" reached, assume that there is a front.
        if (wCurr_pos == wDataEnd)
        {
            fCriterion = TRUE;
        }

        if (fCriterion == TRUE)    // Front detected!
        {
            // Calculate this sequence's width
            wCurrSeqEnd = wCurr_pos;
            wCurr_seq_width = wCurrSeqEnd - wCurrSeqStart + 1;
            switch (fStartBitPassed)
            {
                case(1):
                    if(wCurr_pos == wDataEnd)
                    {
                        wCurr_Detected_Bits_Num = wCurr_seq_width  / wBitDuration;
                        wCurr_Detected_Bits_Num++; //add one extra bit
                    }
                    else
                    {
                        wCurr_Detected_Bits_Num = (wCurr_seq_width + (wBitDuration/2)) / wBitDuration;
                    }

                    wResult_CC_2_bytes = wResult_CC_2_bytes << wCurr_Detected_Bits_Num;
                    if (fCurr_Log_Level)
                    {
                        wResult_CC_2_bytes += pattern[wCurr_Detected_Bits_Num];
                    }
                    wDetected_Bits_Numb += wCurr_Detected_Bits_Num;
                    break;

                case(0):
                    if(wCurr_pos == wDataEnd)
                    {
                        return ((WORD)0); //ERROR
                    }

                    if(wCurr_seq_width >= wBitDuration) /*not to count the start pulse*/
                    {
                        wCurr_Detected_Bits_Num = wCurr_seq_width  / wBitDuration;
                        wResult_CC_2_bytes = wResult_CC_2_bytes << wCurr_Detected_Bits_Num;
                        if (fCurr_Log_Level)
                        {
                            wResult_CC_2_bytes += pattern[wCurr_Detected_Bits_Num];
                        }
                        wDetected_Bits_Numb += wCurr_Detected_Bits_Num;
                    }
                    fStartBitPassed = TRUE; // Anyway
                    break;
            } // end switch

            if ((wDetected_Bits_Numb == N_BITS_PER_FIELD))
            {
                break;
            }

            if (wCurr_pos == wDataEnd)       // End
            {
                break;
            }
            else                            // Prepare for a new sequence
            {
                fCurr_Log_Level = !fCurr_Log_Level; // New level after front
                // Start for the NEXT sequence will be
                wCurrSeqStart = wCurr_pos + 1;
                wCurr_pos += (wBitDuration/2);          // Jump inside the new pulse
                if (wCurr_pos > wDataEnd)
                {
                    wCurr_pos = wDataEnd;               // Do not miss Data End
                }
            }
        }
        else // Front not detected. Check next sequence
        {
            wCurr_pos ++;
        }
    } // end while

    // Validate the CC information from the field
    if ((wDetected_Bits_Numb == N_BITS_PER_FIELD))
    {
        CC_2_bytes = wResult_CC_2_bytes;
    }
    else /* unsuccessful detection */
    {
        CC_2_bytes = (short)0;
    }

    // Phase 4 RETURNING THE TWO CC-DATA USEFUL BYTES
    return (WORD)CC_2_bytes;

} // RT_DecodeCCData ()...

