/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: ResetSIDResponseData.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


BOOL  gbAllECUResetCompleted = FALSE;  // set if ResetSIDResponseData already done for physically addressed request


/*******************************************************************************
**
**  Function:  ResetSIDResponseData
**
**  Purpose:   Reset SID response data
**
*******************************************************************************/
STATUS ResetSIDResponseData ( PASSTHRU_MSG *pTxMsg, unsigned long Flags )
{
	BYTE HeaderSize;
	BYTE Data0Offset;
	BYTE Data1Offset;
	BYTE EcuIdx;
	unsigned long EcuId;
	STATUS eRetCode = PASS;


	// Set the response header size based on the protocol
	HeaderSize  = gstProtocolList[gProtocolIdx].HeaderSize;
	Data0Offset = HeaderSize + 1;
	Data1Offset = HeaderSize + 2;

	// calculate the response address (for physicall addressing)
	if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
	{
		EcuId = (pTxMsg->Data[0] << 24) +
		        (pTxMsg->Data[1] << 16);
		if ( pTxMsg->TxFlags & CAN_29BIT_ID )
		{
			// swap 2 lowest bytes
			EcuId += (pTxMsg->Data[3] << 8) +
			          pTxMsg->Data[2];
		}
		// 11-bit id
		else
		{
			// the repsonse id is 8 higher than the request id
			EcuId += (pTxMsg->Data[2] << 8) +
			          pTxMsg->Data[3] + 0x08;
		}
	}
	else
	{
		EcuId = (pTxMsg->Data[10] << 8) +
		         pTxMsg->Data[11];
	}


	// Reset SID response data for all ECUs
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUsForAlloc;
	      EcuIdx++ )
	{
		gstResponse[EcuIdx].bResponseReceived = FALSE;  // reset response received flag
		gstResponse[EcuIdx].ResponseTimeExtensionMsecs    = 0;
		gstResponse[EcuIdx].ResponsePendingDelayTimeMsecs = 0;
		gstResponse[EcuIdx].bFFReceived                   = FALSE;
		gstResponse[EcuIdx].bNAKReceived                  = FALSE;

		// Save the data in the appropriate SID/PID/MID/TID/INF
		switch ( pTxMsg->Data[HeaderSize] )
		{
			// OBD Services
			// Deprecated
			//SID $1 thru $A
			case 0x01:
			case 0x02:
			case 0x03:
			case 0x04:
			case 0x05:
			case 0x06:
			case 0x07:
			case 0x08:
			case 0x09:
			case 0x0A:

			// Reserved
			// SID 40, $B thru $F
			case 0x00:
			case 0x0B:
			case 0x0C:
			case 0x0D:
			case 0x0E:
			case 0x0F:
			{
				// Invalid services, do not reset anything
			}
			break;


			// UDS Services
			// SID $14  Clear DTCs
			case 0x14:
			{
				// No Data in reply to Clear Codes
				gstResponse[EcuIdx].ClrDTC[0] = 0;
				gstResponse[EcuIdx].ClrDTCSize = 0;
			}
			break;


			// SID $19  Read DTC Data
			case 0x19:
			{
				// Process message based on DID number MSB
				switch ( pTxMsg->Data[Data0Offset] )
				{
					// if this is a FFDTC (Snapshot) ID request
					case 0x03:
					{
					}
					break;

					// if this is FFDTC (Snapshot) Record by DTC Number request
					case 0x04:
					{
						gstResponse[EcuIdx].FFSize = 0;
						memset ( &gstResponse[EcuIdx].FF[0],
						         0,
						         sizeof ( gstResponse[EcuIdx].FF ) );
					}
					break;

					// if this is a ReportDTCExtDataRecordByDTCNumber request
					case 0x06:
					{
						// IF this is a IUMPR Data request
						if ( pTxMsg->Data[HeaderSize + 5] == 0x91 )
						{
							memset ( &gstResponse[EcuIdx].DTCIUMPRCurrent,
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCIUMPRCurrent ) );
						}  // end IF this is a IUMPR Data Request

						   // IF this is a Test Result Data Request
						else if ( pTxMsg->Data[HeaderSize + 5] == 0x92 )
						{
							memset ( &gstResponse[EcuIdx].DTCTestResultCurrent,
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCTestResultCurrent ) );
						}  // end IF this is a Test Result Data Request

						   // IF this is a SMAD Data Rquest
						else if ( pTxMsg->Data[HeaderSize + 5] == 0x93 )
						{
							memset ( &gstResponse[EcuIdx].DTCSMADCurrent,
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCSMADCurrent) );
						}  // end IF this is a SMAD Data Request

						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $06 response data contains unknown Extended Data ID\n",
							      GetEcuId ( EcuIdx ) );
							eRetCode = FAIL;
						}
					}
					break;

					// ReportDTCExtDataIdentification (DTCs supporting ExtData 0x91, 0x92 or 0x93)
					case 0x1A:
					{
						// IF this is a IUMPR List
						if ( pTxMsg->Data[HeaderSize + 2] == 0x91 )
						{
							memset ( &gstResponse[EcuIdx].DTCIUMPRList[0],
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCIUMPRList ) );

							gstResponse[EcuIdx].bDTCIUMPRSupported = FALSE;
							gstResponse[EcuIdx].DTCIUMPRCount      = 0;
							
						}  // end IF this is a IUMPR List

						   // IF this is a Test Result List
						else if ( pTxMsg->Data[HeaderSize + 2] == 0x92 )
						{
							memset ( &gstResponse[EcuIdx].DTCTestResultList[0],
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCTestResultList ) );

							gstResponse[EcuIdx].bDTCTestResultSupported = FALSE;
							gstResponse[EcuIdx].DTCTestResultCount      = 0;
						}  // end IF this is a Test Result List

						// IF this is a SMAD List
						else if ( pTxMsg->Data[HeaderSize + 2] == 0x93 )
						{
							memset ( &gstResponse[EcuIdx].DTCSMADList[0],
							         0,
							         sizeof ( gstResponse[EcuIdx].DTCSMADList ) );

							gstResponse[EcuIdx].bDTCSMADSupported = FALSE;
							gstResponse[EcuIdx].DTCSMADCount      = 0;
						}  // end IF this is a SMAD List

						else
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $19 LEV $1A response data contains unknown Extended Data ID $%02X\n",
							      GetEcuId ( EcuIdx ),
							      pTxMsg->Data[HeaderSize + 2] );
							eRetCode = FAIL;
						}
					}  //end case 0x1A ReportDTCExtDataIdentification (DTCs supporting ExtData 0x91)
					break;

					// if this is DTC by Mask Record request
					case 0x42:
					{
						// if this is a Pending DTC request
						if ( pTxMsg->Data[HeaderSize + 3] == 0x04 )
						{
							memset ( &gstResponse[EcuIdx].PendDTCHeader,
							         0,
							         sizeof ( gstResponse[EcuIdx].PendDTCHeader ) );
							memset ( gstResponse[EcuIdx].PendDTC,
							         0,
							         sizeof ( gstResponse[EcuIdx].PendDTC ) );
						}
						// else this is a Confirmed DTC request
						else
						{
							memset ( &gstResponse[EcuIdx].ConfDTCHeader,
							         0,
							         sizeof ( gstResponse[EcuIdx].ConfDTCHeader ) );
							memset ( gstResponse[EcuIdx].ConfDTC,
							         0,
							         sizeof ( gstResponse[EcuIdx].ConfDTC ) );
						}
					}
					break;

					// if this is a DTC with Permanent Status request
					case 0x55:
					{
						gstResponse[EcuIdx].bPermDTCSupported = FALSE;
						gstResponse[EcuIdx].PermDTCSize = 0;
						memset ( gstResponse[EcuIdx].PermDTC,
						         0,
						         sizeof ( gstResponse[EcuIdx].PermDTC ) );
					}
					break;

					// ReportDTCInformationByDTCReadinessGroupIdentifier
					case 0x56:
					{
						memset ( &gstResponse[EcuIdx].DTCReadinessGroupCurrent,
						         0,
						         sizeof ( gstResponse[EcuIdx].DTCReadinessGroupCurrent ) );

						gstResponse[EcuIdx].DTCReadinessGroupCurrentCount = 0;
						gstResponse[EcuIdx].bDTCReadinessGroupSupported = FALSE;
					}  //end case 0x56 ReportDTCInformationByDTCReadinessGroupIdentifier
					break;
				}  // end case 0x19 switch ( pTxMsg->Data[Data0Offset] )
			}  // end case 0x19
			break;

			// SID $22  Read Data PID, MID, INF
			case 0x22:
			{
				// Process message based on DID number MSB
				switch ( pTxMsg->Data[Data0Offset] )
				{
					// PID
					case 0xF4:
					case 0xF5:
					{
						// Process message based on DID number LSB (PID)
						switch ( pTxMsg->Data[Data1Offset] )
						{
							case 0x00:
							{
								if ( pTxMsg->Data[Data0Offset] == 0xF4 )
								{
									// IF called from VerifyECUCommunication or DetermineProtocol,
									if ( gbVerifyLink == TRUE ||
									     gbDetermineProtocol == TRUE )
									{
										gstResponse[EcuIdx].bLinkActive = FALSE;
									}
									else if ( gReverseOrderStateArray[EcuIdx] == NOT_REVERSE_ORDER )
									{
										// Clear out the support data when PID 0x00 is requested
										gstResponse[EcuIdx].PIDSupportSize = 0;

										memset ( &gstResponse[EcuIdx].PIDSupport[0],
										         0,
										         sizeof ( ID_SUPPORT ) );
									}
								}
							}
							// 0x00 falls through

							case 0x20:
							case 0x40:
							case 0x60:
							case 0x80:
							case 0xA0:
							case 0xC0:
							case 0xE0:
							{
								if ( gReverseOrderStateArray[EcuIdx] == REVERSE_ORDER_REQUESTED )
								{
									// indicate first reverse order response received and SupportSize cleared
									gReverseOrderStateArray[EcuIdx] = REVERSE_ORDER_RESPONSE;

									if ( pTxMsg->Data[Data0Offset] == 0xF5 &&
									     pTxMsg->Data[Data1Offset] == 00 )
									{
										// Clear out the support data when PID 0x00 is requested
										gstResponse[EcuIdx].PIDSupportSize = 0;
									}

									memset ( &gstResponse[EcuIdx].PIDSupport[pTxMsg->Data[Data1Offset]>>5],
									         0,
									         sizeof ( ID_SUPPORT ) );
								}
							}
							// 0x00-0xE0 falls through

							default:
							{
								// All other requests reset the PID data
								gstResponse[EcuIdx].PIDSize = 0;

								memset ( gstResponse[EcuIdx].PID,
								         0,
								         sizeof ( gstResponse[EcuIdx].PID ) );
							}
							break;
						}  // end case 0xF4/5 switch ( TxMsg->Data[Data1Offset] )
					}  // end case 0xF4/5
					break;

					// MID
					case 0xF6:
					{
						// Process message based on MID number (DID LSB)
						switch ( pTxMsg->Data[Data1Offset] )
						{
							case 0x00:
							{
								// Clear out the support data when MID 0x00 is requested
								gstResponse[EcuIdx].MIDSupportSize = 0;
								memset ( gstResponse[EcuIdx].MIDSupport,
								         0,
								         sizeof ( gstResponse[EcuIdx].MIDSupport ) );
							}
							// 0x00 falls through

							case 0x20:
							case 0x40:
							case 0x60:
							case 0x80:
							case 0xA0:
							case 0xC0:
							case 0xE0:
							{
								// Don't do anything with other MID support requests
							}
							// 0x00-0xE0 falls through

							default:
							{
								// All other requests reset the MID data
								gstResponse[EcuIdx].MIDSize = 0;
								memset ( gstResponse[EcuIdx].MID,
								         0,
								         sizeof ( gstResponse[EcuIdx].MID ) );
							}
							break;
						}  // end case 0xF6 switch ( TxMsg->Data[Data1Offset] )
					}  // end case 0xF6
					break;

					// INF
					case 0xF8:
					{
						// Process message based on PID number (DID LSB)
						switch ( pTxMsg->Data[Data1Offset] )
						{
							case 0x00:
							{
								// Clear out the support data when PID 0x00 is requested
								gstResponse[EcuIdx].INFSupportSize = 0;
								memset ( gstResponse[EcuIdx].INFSupport,
								         0,
								         sizeof ( gstResponse[EcuIdx].INFSupport ) );
							}
							// 0x00 falls through

							case 0x20:
							case 0x40:
							case 0x60:
							case 0x80:
							case 0xA0:
							case 0xC0:
							case 0xE0:
							{
								// Don't do anything with other PID support requests
							}
							// 0x00-0xE0 falls through

							default:
							{
								// Process message based on DID number LSB (INF)
								if ( pTxMsg->Data[Data1Offset] == 0x10 )
								{
									// IF called from VerifyECUCommunication or DetermineProtocol,
									if ( gbVerifyLink == TRUE ||
									     gbDetermineProtocol == TRUE )
									{
										gstResponse[EcuIdx].bLinkActive = FALSE;
									}
								}

								// All other requests reset the PID data
								gstResponse[EcuIdx].INFSize = 0;
								memset ( gstResponse[EcuIdx].INF,
								         0,
								         sizeof ( gstResponse[EcuIdx].INF ) );
							}
							break;
						}  // end case 0xF8 switch ( TxMsg->Data[Data1Offset] )
					}  // end case 0xF8
					break;
				} // end case 0x22 switch ( TxMsg->Data[Data0Offset] )
			}  // end case 0x22
			break;

			// SID $31  Control of On-Board System, Test, or Component
			case 0x31:
			{
			}
			break;

			default:
			{
				// Unexpected reset of SID data
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  Unexpected reset of data for SID $%02X\n",
				      GetEcuId ( EcuIdx ),
				      pTxMsg->Data[HeaderSize] );
				eRetCode = FAIL;
			}

		}  // end switch ( TxMsg->Data[HeaderSize] )

	}  // end for ( EcuIdx )

	return eRetCode;
}
