/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: VerifyDTCFFData.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-5 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-5 document in an automated manner, when compiled
**            and used with an SAE J2534-compatible pass-thru device.
**
**            File j1699.c contains information on building and running this test.
**
**  Description:
**
**  Modifications:  03/13/2023  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <string.h>   // C Library character array declarations
#include <time.h>     // C Library time and date declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// Funtion prototypes
STATUS RequestFFIDData        ( BOOL *pbFFSupOnPendFault );
STATUS VerifyFFDTCinDTCList   ( void );
STATUS VerifyFFDTCinFFDTCList ( void );

// Variable prototypes
extern unsigned char DTCTypeCharacter[];


/*******************************************************************************
**
**  Function:  VerifyDTCFFData
**
**  Purpose:   Verify DTC Freeze Frame support and data
**
*******************************************************************************/
STATUS VerifyDTCFFData ( void )
{
	REQ_MSG   stReqMsg;
	BYTE      EcuIdx;
	STATUS    eRetCode = PASS;

	unsigned short  DTCIdx;
	unsigned short  DTCSize;
	DTCASVR        *pDTC;
	
	BYTE            EcuIdx2;
	unsigned short  FFDTCIdx;
	unsigned short  FrameIdx;
	DTCASTR        *pFF;

	BYTE  FFDTCList[MAX_RESP_BUF_SIZE];  // array of the different Freeze Frame DTCs
	BYTE  FFDTCListSize;                 // the size (in bytes) of FFDTCList

	unsigned long  InitialFailureCount = 0;


	// Initialize FFDTC list
	memset ( &FFDTCList[0],
	         0,
	         MAX_RESP_BUF_SIZE );

	FFDTCListSize = 0;

	InitialFailureCount = GetFailureCount ( );

	// Request SID $19 LEV $04 support data
	if ( gbPhysicalAddressing == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFDTCs (SID $19 LEV $04) Functionally Addressed\n" );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFDTCs (SID $19 LEV $04) Physically Addressed\n" );
	}


	// Check for freeze frame DTCs
	// Request SID $19 LEV $04
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		gstResponse[EcuIdx].pFFDTCList->Size = 0;

		if ( (DTCSize = gstResponse[EcuIdx].pDTCList->Size) != 0 )
		{
			for ( DTCIdx = 0;
			      DTCIdx < DTCSize;
			      DTCIdx += sizeof ( DTCASVR ) )
			{
				pDTC = (DTCASVR *) &gstResponse[EcuIdx].pDTCList->Dtc[DTCIdx];

				// if functional addressing, only request DTC once
				if ( gbPhysicalAddressing == FALSE )
				{
					for ( FFDTCIdx = 0;
					      FFDTCIdx < FFDTCListSize;
					      FFDTCIdx += sizeof ( DTCASTR ) )
					{
						pFF = (DTCASTR*)&FFDTCList[FFDTCIdx];

						// is the DTC has already in the list
						if ( pFF->Dtc.HighByte == pDTC->Record.Dtc.HighByte &&
						     pFF->Dtc.MidByte  == pDTC->Record.Dtc.MidByte  &&
						     pFF->Dtc.LowByte  == pDTC->Record.Dtc.LowByte )
						{
							break;
						}
					}

					// if the DTC hasn't been requested yet, add it to the list
					if ( FFDTCIdx >= FFDTCListSize )
					{
						memcpy ( &FFDTCList[FFDTCListSize],
						         &pDTC->Record.Dtc,
						         sizeof ( DTCASTR ) );
						FFDTCListSize += sizeof ( DTCASTR );
					}
					// else the DTC has already been requested, skip it
					else
					{
						continue;
					}
				}

				for ( FrameIdx = 0;
				      FrameIdx < 2;
				      FrameIdx++ )
				{
					// Request SID $19 LEV $04 data
					stReqMsg.SID     = 0x19;
					stReqMsg.NumIds  = 5;
					stReqMsg.u.ID[0] = 0x04;  // LEV: ReportDTCSnapshotRecordByDTCNumber
					stReqMsg.u.ID[1] = pDTC->Record.Dtc.HighByte;      // DTCMaskRecord.DTCHighByte
					stReqMsg.u.ID[2] = pDTC->Record.Dtc.MidByte;       // DTCMaskRecord.DTCMiddleByte
					stReqMsg.u.ID[3] = pDTC->Record.Dtc.LowByte;       // DTCMaskRecord.DTCLowByte
					stReqMsg.u.ID[4] = (FrameIdx == 0 ? 0x00 : 0xF0);  // DTCMaskRecord.DTCSnapshotRecordNumber
					                                                   // (first (00) or most recent (F0) freeze frame )
					if ( (gbPhysicalAddressing == TRUE &&
					      RequestSID_PhysicallyAddressed_Single ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE, gstResponse[EcuIdx].ReqId ) != PASS) ||
					     (gbPhysicalAddressing == FALSE &&
					      RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "SID $19 LEV $04 request error\n" );
						eRetCode = FAIL;
						continue;
					}


					for ( EcuIdx2 = (gbPhysicalAddressing == TRUE ? EcuIdx : 0);
					      EcuIdx2 < (gbPhysicalAddressing == TRUE ? (EcuIdx+1) : gNumOfECUs);
					      EcuIdx2++ )
					{
						// Skip if no FFID data or no DTCs stored
						if ( gstResponse[EcuIdx2].FFSize != 0 &&
						     (gstResponse[EcuIdx2].FF[0] != 0 ||
						      gstResponse[EcuIdx2].FF[1] != 0) )
						{
							pFF = (DTCASTR*)&gstResponse[EcuIdx2].FF[0];

							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $04  FFDTC %c%02X%02X %02X %s frame stored\n",
							      GetEcuId ( EcuIdx2 ),
							      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      pFF->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
							      pFF->Dtc.LowByte,                                   // 6th and 7th (0-F) characters
							      (FrameIdx == 0 ? "first" : "most recent") );

							// if this is the first frame request, save the DTC
							if ( FrameIdx == 0 )
							{
								memcpy ( &gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size],
								         &gstResponse[EcuIdx2].FF[0],
								         sizeof ( DTC ) );
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.HighByte;
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.MidByte;
								gstResponse[EcuIdx2].pFFDTCList->Dtc[gstResponse[EcuIdx2].pFFDTCList->Size++] = pFF->Dtc.LowByte;
							}

//							// Verify that the SID $19 LEV $04 FF data is valid
//							if ( VerifyFFData ( ) != PASS )
//							{
//								eRetCode = FAIL;
//							}
						}  // end if ( Freeze Frame Data )
						else
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $04  FFDTC %c%02X%02X %02X no data reported\n",
							      GetEcuId ( EcuIdx2 ),
							      DTCTypeCharacter[(pDTC->Record.Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
							      pDTC->Record.Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
							      pDTC->Record.Dtc.MidByte,                                   // 4th and 5th (0-F) characters
							      pDTC->Record.Dtc.LowByte  );                                // 6th and 7th (0-F) characters
						}
					}  // end for ( EcuIdx2 )
				}  // end for ( FrameIdx )
			}  // end for ( DTCIdx )
		}  // end if ( DTCSize != 0 )  (ECU had pending DTC(s))
	}  // end for ( EcuIdx )


	if ( InitialFailureCount != GetFailureCount ( ) )
	{
		// There could have been early/late responses that weren't treated as FAIL
		// or the test failed earlier
		return FAIL;
	}
	else
	{
		return eRetCode;
	}

}


/*******************************************************************************
**
**  Function: RequestFFIDData
**
**  Purpose: Verify that SID $19 LEV $03 returns stored DTC.
**           Assuming SID $19 LEV $03 was supported by the controller,
**           data should be returned.
**
*******************************************************************************/
STATUS RequestFFIDData ( BOOL          *pbFFSupOnPendFault )
{
	REQ_MSG   stReqMsg;
	BYTE      EcuIdx;
	STATUS    eRetCode = PASS;

	BYTE      FFDTCCount = 0;         // Count of ECUs which respond to Freeze Frame Request with data
	BYTE      FFDTCSupportCount = 0;  // Count of ECUs which respond to Freeze Frame Request
	DTCASSR  *pFF;


	if ( gbPhysicalAddressing == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFIDs (SID $19 LEV $03) Functionally Addressed\n" );
	}
	else
	{
		Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Request FFIDs (SID $19 LEV $03) Physically Addressed\n" );
	}


	// Request SID $19 LEV $03 data
	stReqMsg.SID     = 0x19;
	stReqMsg.NumIds  = 1;
	stReqMsg.u.ID[0] = 0x03;  // Subfunction (LEV): ReportSnapshotIdentification
	gbIgnoreUnsupported = TRUE;
	if ( (gbPhysicalAddressing == FALSE &&
	      RequestSID ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) ||
	     (gbPhysicalAddressing == TRUE  &&
	      RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_ALLOW_NO_RESPONSE ) != PASS) )
	{
		gbIgnoreUnsupported = FALSE;
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "SID $19 LEV $03 request error\n" );
		return FAIL;
	}
	gbIgnoreUnsupported = FALSE;

	// Check for freeze frame DTCs
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		// Clear Freeze Frame Array entry for this ECU
//		pFreezeFrameDTC[EcuIdx] = 0;

		// If there was a response by this ECU, Increment the count of ECUs which support Freeze Frame DTC
		if ( gstResponse[EcuIdx].bResponseReceived == TRUE )
		{
			FFDTCSupportCount++;
		}

		// Skip if no FFID data or no DTCs stored
		if ( gstResponse[EcuIdx].FFSize != 0 &&
		     (gstResponse[EcuIdx].FF[0] != 0 ||
		      gstResponse[EcuIdx].FF[1] != 0) )
		{
//			// Save the DTC
//			memcpy ( pFreezeFrameDTC,
//			         &gstResponse[EcuIdx].FF,
//			         gstResponse[EcuIdx].FFSize );

			pFF = (DTCASSR*)&gstResponse[EcuIdx].FF[0];
			FFDTCCount++;

			// Check if a DTC is not supposed to be present
			if ( gbDTCStored     == FALSE &&
			     gbDTCPending    == FALSE &&
			     gbDTCHistorical == FALSE )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03  FFDTC %c%02X%02X %02X stored\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0 ) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                            // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters

				eRetCode = FAIL;
			}

			else if ( gbDTCPending == TRUE  &&
			          gbDTCStored  == FALSE )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03  FFDTC %c%02X%02X %02X stored on pending fault\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters
				*pbFFSupOnPendFault = TRUE;
			}
			else
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $19 LEV $03 FFDTC %c%02X%02X %02X stored\n",
				      GetEcuId ( EcuIdx ),
				      DTCTypeCharacter[(pFF->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFF->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFF->Dtc.MidByte,                                    // 4th and 5th (0-F) characters
				      pFF->Dtc.LowByte );                                  // 6th and 7th (0-F) characters
				*pbFFSupOnPendFault = TRUE;
			}
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  SID $19 LEV $03 indicates no FFDTC stored\n",
			      GetEcuId ( EcuIdx ) );
		}
	}

	// Test 5.11.1 - If all ECUs report Freeze Frame DTCs stored, it is a Failure
	if ( geTestPhase == eTestNoDTC && gTestSubsection == 11 &&
	     FFDTCCount != 0 &&
	     FFDTCCount == FFDTCSupportCount )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "All ECUs reported Freeze Frame DTCs stored\n" );
		eRetCode = FAIL;
	}

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  VerifyFFDTCinDTCList
**
**  Purpose:   Verify the DTC in the freeze frame is in the list of stored DTCs.
**
*******************************************************************************/
STATUS VerifyFFDTCinDTCList ( void )
{
	STATUS         eRetCode = PASS;

	BYTE           FFEcuIdx;
	unsigned long  FFDTCIdx;
	unsigned long  FFDTC;
	unsigned long  FFDTCCount = 0;
	DTCASSR  *pFFDTC;

	BYTE           DTCEcuIdx;
	unsigned long  DTCIdx;
	unsigned long  StrdDTC;
	unsigned long  StrdDTCCount = 0;
	DTCASVR  *pDTC;

	unsigned long  MtchdDTCCount = 0;
	BOOL           bMtchdDTC = FALSE;


	// Count all DTCs for all ECUs
	for ( DTCEcuIdx = 0;
	      DTCEcuIdx < gNumOfECUs;
	      DTCEcuIdx++ )
	{
		// Count all Stored DTCs
		if ( gstResponse[DTCEcuIdx].pDTCList->Size != 0 )
		{
			for ( DTCIdx = 0;
			      DTCIdx < gstResponse[DTCEcuIdx].pDTCList->Size;
			      DTCIdx += sizeof ( DTCASVR ) )
			{
				pDTC = (DTCASVR *)(&gstResponse[DTCEcuIdx].pDTCList->Dtc[DTCIdx]);
				StrdDTC = (pDTC->Record.Dtc.HighByte << 16) + (pDTC->Record.Dtc.MidByte << 8) + pDTC->Record.Dtc.LowByte;
				if ( StrdDTC != 0x0000 )
				{
					StrdDTCCount++;
				}
			}
		}

		// Count all Freeze Frames
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[DTCEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			pFFDTC = (DTCASSR *)(&gstResponse[DTCEcuIdx].FF[0]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x0000  )
			{
				FFDTCCount++;
			}
		}
	}


	// Verify each Freeze DTCs is in the list of Confirmed or Pending DTCs
	for ( FFEcuIdx = 0;
	      FFEcuIdx < gNumOfECUs;
	      FFEcuIdx++ )
	{
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[FFEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			bMtchdDTC = FALSE;

			pFFDTC = (DTCASSR *)(&gstResponse[FFEcuIdx].FF[FFDTCIdx]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x0000 )
			{
				for ( DTCEcuIdx = 0;
				      DTCEcuIdx < gNumOfECUs;
				      DTCEcuIdx++ )
				{
					for ( DTCIdx = 0;
					      DTCIdx < gstResponse[DTCEcuIdx].pDTCList->Size;
					      DTCIdx += sizeof ( DTCASVR ) )
					{
						pDTC = (DTCASVR*)(&gstResponse[DTCEcuIdx].pDTCList->Dtc[DTCIdx]);
						StrdDTC = (pDTC->Record.Dtc.HighByte << 16) + (pDTC->Record.Dtc.MidByte << 8) + pDTC->Record.Dtc.LowByte;
						if ( StrdDTC != 0x0000 )
						{
							if ( FFDTC == StrdDTC )
							{
								bMtchdDTC = TRUE;
								MtchdDTCCount++;
								break;  // leave DTC index for loop
							}
						}
					}  // end for DTC Index < DTC Size
	
					if ( bMtchdDTC == TRUE )
					{
						break;  // leave DTC ECU for loop
					}
				}  // end for DTC ECU
	
				if ( !bMtchdDTC )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Freeze Frame DTC %c%02X%02X %02X not found in list of stored or pending DTCs on any ECU\n",
					      GetEcuId (FFEcuIdx),
					      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
					      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
					      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
					      pFFDTC->Dtc.LowByte );                                 // 6th and 7th (0-F) characters
					eRetCode = FAIL;
				}
			}  // end FFDTC != 0x0000
		}  // end for FFDTC index < FFSize
	}  // end for FF ECU


	if ( StrdDTCCount !=0  &&
	     MtchdDTCCount == 0 )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "Stored DTC with no Freeze Frame DTC on any ECU\n" );
		eRetCode = FAIL;
	}


	return eRetCode;
}


/*******************************************************************************
**
**  Function:  VerifyFFDTCinFFDTCList
**
**  Purpose:   Verify the DTC in the freeze frame is in the list of previous freeze frame DTCs.
**
*******************************************************************************/
STATUS VerifyFFDTCinFFDTCList ( void )
{
	STATUS         eRetCode = PASS;

	BYTE           FFEcuIdx;
	unsigned long  FFDTCIdx;
	unsigned long  FFDTC;
	unsigned long  FFDTCCount = 0;
	DTCASSR   *pFFDTC;

	unsigned long  DTCIdx;
	unsigned long  StrdDTC;
	unsigned long  StrdDTCCount = 0;
	DTCASSR   *pDTC;

	unsigned long  MtchdDTCCount = 0;
	BOOL           bMtchdDTC = FALSE;


	// Verify each Freeze Frame DTC is in the list of previous freeze frame DTCs
	for ( FFEcuIdx = 0;
	      FFEcuIdx < gNumOfECUs;
	      FFEcuIdx++ )
	{
		for ( FFDTCIdx = 0;
		      FFDTCIdx < gstResponse[FFEcuIdx].FFSize;
		      FFDTCIdx += sizeof ( DTCASSR ) )
		{
			bMtchdDTC = FALSE;

			pFFDTC = (DTCASSR *)(&gstResponse[FFEcuIdx].FF[FFDTCIdx]);
			FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
			if ( FFDTC != 0x000000 )
			{
				for ( DTCIdx = 0;
				      DTCIdx < gstResponse[FFEcuIdx].pFFDTCList->Size;
				      DTCIdx += sizeof ( DTCASSR ) )
				{
					pDTC = (DTCASSR*)(&gstResponse[FFEcuIdx].pFFDTCList->Dtc[DTCIdx]);
					StrdDTC = (pDTC->Dtc.HighByte << 16) + (pDTC->Dtc.MidByte << 8) + pDTC->Dtc.LowByte;
					if ( StrdDTC != 0x000000 )
					{
						if ( FFDTC == StrdDTC )
						{
							bMtchdDTC = TRUE;
							MtchdDTCCount++;
							break;  // leave DTC index for loop
						}
					}
				}  // end for DTC Index < DTC Size
	
				if ( bMtchdDTC == TRUE )
				{
					break;  // leave DTC ECU for loop
				}
			}  // end FFDTC != 0x0000
	
			if ( !bMtchdDTC && FFDTC != 0x000000 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Freeze Frame DTC %c%02X%02X %02X not found in list of previous Freeze Frame DTCs on this ECU\n",
				      GetEcuId (FFEcuIdx),
				      DTCTypeCharacter[(pFFDTC->Dtc.HighByte & 0xC0) >> 6],  // 1st character (P, C, B, U)
				      pFFDTC->Dtc.HighByte & 0x3F,                           // 2nd (0,1,2,3) and 3rd (0-F) characters
				      pFFDTC->Dtc.MidByte,                                   // 4th and 5th (0-F) characters
				      pFFDTC->Dtc.LowByte );                                 // 6th and 7th (0-F) characters
				eRetCode = FAIL;
			}

		}  // end for FFDTC index < FFSize
	}  // end for FF ECU

	return eRetCode;
}


/*******************************************************************************
**
**  Function:  VerifyFFData
**
**  Purpose:   Verify the data in the freeze frame is valid.
**
*******************************************************************************/
STATUS VerifyFFData ( void )
{
	STATUS           eRetCode = PASS;

	BYTE             EcuIdx;
	DTCASSR         *pFFDTC;
	unsigned long    FFDTC;

	unsigned long    DIDIdx;

	FREEZEFRAMEDATA *pDID;         // pointer to current DID in Freeze Frame data list
	WORD             DID;          // ID   of current DID in Freeze Frame data list
	unsigned short   DIDSize = 0;  // size of current DID in Freeze Frame data list


	// Verify each Freeze Frame data is valid
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		pFFDTC = (DTCASSR *)(&gstResponse[EcuIdx].FF[0]);
		FFDTC = (pFFDTC->Dtc.HighByte << 16) + (pFFDTC->Dtc.MidByte << 8) + pFFDTC->Dtc.LowByte;
		if ( FFDTC != 0x000000 )
		{
			// point to start of Freeze Frame DID data
			pDID = (FREEZEFRAMEDATA*)(&gstResponse[EcuIdx].FF[sizeof ( DTCASVR )]);

			for ( DIDIdx = 0;
			      DIDIdx < gstResponse[EcuIdx].FFSize;
			      DIDIdx += DIDSize )
			{
				DID = (pDID->PIDMSB << 8) + pDID->PIDLSB;
				
				// Find DID in lookup table
				// for ( x =0;
				//       x < size of lookup table
				//       x++ )
				// {
				//     if ( DID == lookup table[x] )
				//     {
				//         print formated data
				//     }
				// }
				// if ( required DID not in Freeze Frame data )
				// {
				//     failure;
				// }
			}  // end for (DTC Index < DTC Size_
		}  // end if ( FFDTC != 0x0000 )
	}  // end for ( EcuIdx )

	return eRetCode;
}
