// RDSData.cpp: implementation of the CRDSData class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "USBRadio.h"
#include "RDSData.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CRDSData::CRDSData()
{
	//Initialize the RDS
	InitRDS();
}	

CRDSData::~CRDSData()
{

}

void CRDSData::InitRDS()
{
    BYTE i = 0;

	//Set the RDS text to the default
	m_RDSText = DEFAULT_RDS_TEXT;

    // Reset RDS variables
	m_RdsDataAvailable = 0;
	m_RdsDataLost      = 0;
	m_RdsIndicator     = 0;
	m_RdsBlocksValid   = 0;
	m_RdsBlocksTotal   = 0;

    // Clear RDS Fifo
    m_RdsFifoEmpty = 1;
    m_RdsReadPtr = 0;
    m_RdsWritePtr = 0;

    // Clear Radio Text
	m_rtFlagValid     = 0;
    for (i=0; i<sizeof(m_rtDisplay); i++)
	{
        m_rtDisplay[i] = 0;
		m_rtTmp0[i]    = 0;
		m_rtTmp1[i]    = 0;
		m_rtCnt[i]     = 0;
    }

    // Clear Program Service
	for (i=0;i<sizeof(m_psDisplay);i++)
	{
		m_psDisplay[i] = 0;
		m_psTmp0[i]    = 0;
		m_psTmp1[i]    = 0;
		m_psCnt[i]     = 0;
	}

    // Reset Debug Group counters
    for (i=0; i<32; i++)
    {
        m_debug_group_counters[i] = 0;   
    }
}

void CRDSData::UpdateRDSText(WORD* registers, bool ignoreABFlag)
{
	BYTE group_type;      // bits 4:1 = type,  bit 0 = version
    BYTE addr;
	BYTE errorCount;
    BYTE errorFlags;
	BYTE abflag;
    
	m_RdsDataAvailable = 0;

	errorCount = (registers[0xa] & 0x0E00) >> 9;

	errorFlags = registers[0x6] & 0xFF;

	if (errorCount < 4)
	{
		m_RdsBlocksValid += (4 - errorCount);
	}

    // Drop the data if there are any errors
    if (errorCount)
	{
		return;
	}

    // Drop the data if more than two errors were corrected
    if (ERRORS_CORRECTED(errorFlags, BLOCK_B) > CORRECTED_ONE_TO_TWO)
	{
        //return;
	}

    UpdateRDSFifo(registers);

	m_RdsIndicator = 100; // Reset RdsIndicator
    
    group_type = registers[0xD] >> 11;  // upper five bits are the group type and version
	m_debug_group_counters[group_type] += 1;

    // Update pi code.  Version B formats always have the pi code in words A and C
    update_pi(registers[0xC]);

    if (group_type & 0x01)
	{
        update_pi(registers[0xE]);
	}
    
    // update pty code.  
    update_pty((registers[0xd]>>5) & 0x1f);  // gdc:  fix this so all 16 bits are passed and other bits are also updated here?

    switch (group_type) {
	    case RDS_TYPE_0B:
	        addr = (registers[0xd]&0x3)*2;
	        update_ps(addr+0, registers[0xf] >> 8  );
		    update_ps(addr+1, registers[0xf] & 0xff);
		    break;

	    case RDS_TYPE_2A:
	        addr = (registers[0xd] & 0xf) * 4;
			abflag = (registers[0xd] & 0x0010) >> 4;
			update_rt(abflag, 4, addr, (BYTE*)&(registers[0xe]), errorFlags, ignoreABFlag);
		    break;
    
	    case RDS_TYPE_2B:
	        addr = (registers[0xd] & 0xf) * 2;
			abflag = (registers[0xd] & 0x0010) >> 4;
	        // The last 32 bytes are unused in this format
			m_rtTmp0[32]    = 0x0d;
			m_rtTmp1[32]    = 0x0d;
			m_rtCnt[32]     = RT_VALIDATE_LIMIT;
			update_rt(abflag, 2, addr, (BYTE*)&(registers[0xe]), errorFlags, ignoreABFlag);
	        break;

	    default:
	        break;                
    }
}

void CRDSData::UpdateRDSFifo(WORD* group)
{
    m_RdsFifo[m_RdsWritePtr].a = group[0xC];
    m_RdsFifo[m_RdsWritePtr].b = group[0xD];
    m_RdsFifo[m_RdsWritePtr].c = group[0xE];
    m_RdsFifo[m_RdsWritePtr].d = group[0xF];
    if(m_RdsWritePtr == m_RdsReadPtr && !m_RdsFifoEmpty)
    {
		m_RdsDataLost++;
		m_RdsReadPtr++;
		m_RdsReadPtr %= RDS_FIFO_SIZE;
    }
    m_RdsFifoEmpty = 0;
	m_RdsWritePtr++;
	m_RdsWritePtr %= RDS_FIFO_SIZE;
}

void CRDSData::update_pi(WORD current_pi)
{
    static BYTE rds_pi_validate_count = 0;
    static WORD rds_pi_nonvalidated = 0;
    
    // if the pi value is the same for a certain number of times, update
    // a validated pi variable
    if (rds_pi_nonvalidated != current_pi)
	{
        rds_pi_nonvalidated =  current_pi;
        rds_pi_validate_count = 1;
    }
	else
	{
        rds_pi_validate_count++;
    }

    if (rds_pi_validate_count > RDS_PI_VALIDATE_LIMIT)
	{
        m_piDisplay = rds_pi_nonvalidated;
	}
}

void CRDSData::update_pty(BYTE current_pty)
{
    static BYTE rds_pty_validate_count = 0;
    static BYTE rds_pty_nonvalidated = 0;
    
    // if the pty value is the same for a certain number of times, update
    // a validated pty variable
    if (rds_pty_nonvalidated != current_pty)
	{
        rds_pty_nonvalidated =  current_pty;
        rds_pty_validate_count = 1;
    }
	else
	{
        rds_pty_validate_count++;
    }

    if (rds_pty_validate_count > RDS_PTY_VALIDATE_LIMIT)
	{
        m_ptyDisplay = rds_pty_nonvalidated;
	}
}

void CRDSData::update_ps(BYTE addr, BYTE byte)
{
    BYTE i = 0;
	BYTE textChange = 0; // indicates if the PS text is in transition
	BYTE psComplete = 1; // indicates that the PS text is ready to be displayed

	if(m_psTmp0[addr] == byte)
	{
        // The new byte matches the high probability byte
		if(m_psCnt[addr] < PS_VALIDATE_LIMIT)
		{
			m_psCnt[addr]++;
		}
		else
		{
            // we have recieved this byte enough to max out our counter
            // and push it into the low probability array as well
			m_psCnt[addr] = PS_VALIDATE_LIMIT;
			m_psTmp1[addr] = byte;
		}
	}
	else if(m_psTmp1[addr] == byte)
	{
        // The new byte is a match with the low probability byte. Swap
        // them, reset the counter and flag the text as in transition.
        // Note that the counter for this character goes higher than
        // the validation limit because it will get knocked down later
		if(m_psCnt[addr] >= PS_VALIDATE_LIMIT)
		{
			textChange = 1;
		}
		m_psCnt[addr] = PS_VALIDATE_LIMIT + 1;
		m_psTmp1[addr] = m_psTmp0[addr];
		m_psTmp0[addr] = byte;
	}
	else if(!m_psCnt[addr])
	{
        // The new byte is replacing an empty byte in the high
        // proability array
		m_psTmp0[addr] = byte;
		m_psCnt[addr] = 1;
	}
	else
	{
        // The new byte doesn't match anything, put it in the
        // low probablity array.
		m_psTmp1[addr] = byte;
	}

	if(textChange)
	{
        // When the text is changing, decrement the count for all 
        // characters to prevent displaying part of a message
        // that is in transition.
		for(i=0;i<sizeof(m_psCnt);i++)
		{
			if(m_psCnt[i] > 1)
			{
				m_psCnt[i]--;
			}
		}
	}

    // The PS text is incomplete if any character in the high
    // probability array has been seen fewer times than the
    // validation limit.
	for (i=0;i<sizeof(m_psCnt);i++)
	{
		if(m_psCnt[i] < PS_VALIDATE_LIMIT)
		{
			psComplete = 0;
			break;
		}
	}

    // If the PS text in the high probability array is complete
    // copy it to the display array
	if (psComplete)
	{
		for (i=0;i<sizeof(m_psDisplay); i++)
		{
			m_psDisplay[i] = m_psTmp0[i];
		}
	}
}

void CRDSData::display_rt()
{
    BYTE rtComplete = 1;
	BYTE i;

    // The Radio Text is incomplete if any character in the high
    // probability array has been seen fewer times than the
    // validation limit.
	for (i=0; i<sizeof(m_rtTmp0);i++)
	{
		if(m_rtCnt[i] < RT_VALIDATE_LIMIT)
		{
			rtComplete = 0;
			break;
		}
		if(m_rtTmp0[i] == 0x0d)
		{
            // The array is shorter than the maximum allowed
			break;
		}
	}

    // If the Radio Text in the high probability array is complete
    // copy it to the display array
	if (rtComplete)
	{
		m_RDSText = "";

		for (i=0; i < sizeof(m_rtDisplay); i += 2)
		{
			if ((m_rtDisplay[i] != 0x0d) && (m_rtDisplay[i+1] != 0x0d))
			{
				m_rtDisplay[i] = m_rtTmp0[i+1];
				m_rtDisplay[i+1] = m_rtTmp0[i];
			}
			else
			{
				m_rtDisplay[i] = m_rtTmp0[i];
				m_rtDisplay[i+1] = m_rtTmp0[i+1];
			}

			if (m_rtDisplay[i] != 0x0d)
				m_RDSText += (CString)m_rtDisplay[i];
			
			if (m_rtDisplay[i+1] != 0x0d)
				m_RDSText += (CString)m_rtDisplay[i+1];

			if ((m_rtDisplay[i] == 0x0d) || (m_rtDisplay[i+1] == 0x0d))
				i = sizeof(m_rtDisplay);
		}

        // Wipe out everything after the end-of-message marker
		for (i++;i<sizeof(m_rtDisplay);i++)
		{
			m_rtDisplay[i] = 0;
			m_rtCnt[i]     = 0;
			m_rtTmp0[i]    = 0;
			m_rtTmp1[i]    = 0;
		}
	}
}

void CRDSData::update_rt(BYTE abFlag, BYTE count, BYTE addr, BYTE* byte, BYTE errorFlags, bool ignoreABFlag)
{
    BYTE i;
	BYTE textChange = 0; // indicates if the Radio Text is changing

	if ((abFlag != m_rtFlag && m_rtFlagValid) && !ignoreABFlag)
	{
		// If the A/B message flag changes, try to force a display
		// by increasing the validation count of each byte
		for (i=0;i<sizeof(m_rtCnt);i++)
		{
			m_rtCnt[addr+i]++;
		}
		display_rt();

		// Wipe out the cached text 
		for (i=0;i<sizeof(m_rtCnt);i++)
		{
			m_rtCnt[i] = 0;
			m_rtTmp0[i] = 0;
			m_rtTmp1[i] = 0;
		}
	}
	m_rtFlag = abFlag;    // Save the A/B flag
	m_rtFlagValid = 1;    // Our copy of the A/B flag is now valid

	for(i=0;i<count;i++)
	{
		if(!byte[i])
		{
			byte[i] = ' '; // translate nulls to spaces
		}
		
        // The new byte matches the high probability byte
		if(m_rtTmp0[addr+i] == byte[i])
		{
			if(m_rtCnt[addr+i] < RT_VALIDATE_LIMIT)
			{
				m_rtCnt[addr+i]++;
			}
			else
			{
                // we have recieved this byte enough to max out our counter
                // and push it into the low probability array as well
				m_rtCnt[addr+i] = RT_VALIDATE_LIMIT;
				m_rtTmp1[addr+i] = byte[i];
			}
		}
		else if(m_rtTmp1[addr+i] == byte[i])
		{

            // The new byte is a match with the low probability byte. Swap
            // them, reset the counter and flag the text as in transition.
            // Note that the counter for this character goes higher than
            // the validation limit because it will get knocked down later
			if(m_rtCnt[addr+i] >= PS_VALIDATE_LIMIT)
			{
				textChange = 1;
			}
			m_rtCnt[addr+i] = RT_VALIDATE_LIMIT + 1;
			m_rtTmp1[addr+i] = m_rtTmp0[addr+i];
			m_rtTmp0[addr+i] = byte[i];
		}
		else if(!m_rtCnt[addr+i])
		{
            // The new byte is replacing an empty byte in the high
            // proability array
			m_rtTmp0[addr+i] = byte[i];
			m_rtCnt[addr+i] = 1;
		}
		else
		{
            // The new byte doesn't match anything, put it in the
            // low probablity array.
			m_rtTmp1[addr+i] = byte[i];
		}
	}

	if(textChange)
	{
        // When the text is changing, decrement the count for all 
        // characters to prevent displaying part of a message
        // that is in transition.
		for(i=0;i<sizeof(m_rtCnt);i++)
		{
			if(m_rtCnt[i] > 1)
			{
				m_rtCnt[i]--;
			}
		}
	}

    // Display the Radio Text
	display_rt();
}

CString CRDSData::GetRDSText()
{
	//Return the latest RDS Text
	return m_RDSText;
}

void CRDSData::ResetRDSText()
{
	//Re-initialize the RDS
	InitRDS();
}