/*	Cache.c - Caching of MGMT structures (read/write)
*
*******************************************************************************
* Copyright (c) 1996-97 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This  software  contains  proprietary  and  confidential   information   of *
* Digi International  Inc.   By  accepting  transfer  of this copy, Recipient *
* agrees to retain this software in  confidence,  to  prevent  disclosure  to *
* others,  and  to make no  use of this software other than that for which it *
* was delivered.  This is an unpublished copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1996-97                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************
*
*/

#include "DS_RAS.h"
#include "Cache.h"
#include "List.h"
#include "ToString.h"
#include "Report.h"
#include "dxbtypes.h"
#include "mgmtshim.h"
#include "mgmthlc.h"

static LIST lstHead;

#define	REFRESH_RATE_DEFAULT	15
#define	REFRESH_RATE_ALWAYS		0
#define	REFRESH_RATE_NEVER		-1

typedef struct
{
	size_t iOffsetToGeneric1;
	size_t iOffsetToGeneric2;
}
CacheInstanceData_t;

typedef struct
{
	uint16 const nCommand;
	uint16 const cb;
	size_t const iOffsetToUserData;
	CacheInstanceData_t cid;
}
CacheRequest_t;

typedef struct
{
	uint16 const cb;
	size_t const iOffsetToUserData;
}
CacheResponse_t;

typedef struct
{
	CacheRequest_t creq;
	CacheResponse_t crsp;
}
CacheGroup_t;

typedef struct
{
	DWORD rateRefresh;
	CacheGroup_t cgRead;
	CacheGroup_t cgWrite;
}		
CacheInfo_t;

#define	DEFAULT_REFRESH		15

typedef enum
{
	DataIndex_ReadRequest,
	DataIndex_ReadResponse,
	DataIndex_WriteRequest,
	DataIndex_WriteResponse,
	DataIndexCount
};

typedef struct
{
	CacheObject_t co;
	CacheInfo_t * pci;
	time_t timeRefresh;
	BOOL fDirty;
	void * pvData[DataIndexCount];
}
CacheData_t;

typedef struct
{
	UINT const eType;
	CacheInfo_t ci;
}
CacheInfoEntry_t;

static CacheInfoEntry_t acie[] =
{
	{
		SHIM(MGMT_SHIM_VER),
		{
			REFRESH_RATE_NEVER,
			{
				{ GET_MGMT_SHIM_VER },
				{ sizeof (get_mgmt_shim_ver_response_t) }
			},
		}
	},
	{
		SHIM(MGMT_BOARD_NO),
		{
			REFRESH_RATE_NEVER,
			{
				{ GET_MGMT_BOARD_NO },
				{ sizeof (get_mgmt_board_no_response_t) }
			}
		}
	},
	{
		SHIM(MGMT_BOARD_INFO),
		{
			REFRESH_RATE_NEVER,
			{
				{ GET_MGMT_BOARD_INFO },
				{ sizeof (get_mgmt_board_info_response_t), offsetof(get_mgmt_board_info_response_t, BoardInfo) }
			}
		}
	},
	{
		SHIM(MGMT_DRV_VERS),
		{
			REFRESH_RATE_NEVER,
			{
				{ GET_MGMT_DRV_VERS },
				{ sizeof (get_mgmt_drv_vers_response_t), offsetof(get_mgmt_drv_vers_response_t, versions) }
			}
		}
	},
	{
		MGMT_PROTOCOL_VERSION,
		{
			DEFAULT_REFRESH,
			{
				{ GET_MGMT_PROTOCOL_VERSION },
				{ sizeof (get_mgmt_protocol_ver_response_t) }
			}
		}
	},
	{
		ADAPTER_INFO,
		{
			DEFAULT_REFRESH,
			{
				{ GET_ADAPTER_INFO },
				{ sizeof (get_adapter_info_t) }
			}
		}
	},
	{
		ADAPTER_STATE,
		{
			DEFAULT_REFRESH,
			{
				{ GET_ADAPTER_STATE },
				{ sizeof (get_adapter_state_t) }
			}
		}
	},
	{
		MODEM_NATIONALITY,
		{
			DEFAULT_REFRESH,
			{
				{ GET_MODEM_NATIONALITY },
				{ sizeof (get_modem_nationality_response_t) }
			},
			{
				{ SET_MODEM_NATIONALITY, sizeof (set_modem_nationality_request_t) }
			}
		}
	},
	{
		DS1_CONFIG,
		{
			DEFAULT_REFRESH,
			{
				{ GET_DS1_CONFIG, sizeof (get_ds1_config_request_t), 0, { offsetof(get_ds1_config_request_t, port) } },
				{ sizeof (get_ds1_config_response_t), offsetof(get_ds1_config_response_t, config) }
			},
			{
				{ SET_DS1_CONFIG, sizeof (set_ds1_config_request_t), offsetof(set_ds1_config_request_t, config), { offsetof(set_ds1_config_request_t, port) } }
			}
		}
	},
	{
		DS1_EXT_CONFIG,
		{
			DEFAULT_REFRESH,
			{
				{ GET_DS1_EXT_CONFIG, sizeof (get_ds1_ext_config_request_t), 0, { offsetof(get_ds1_ext_config_request_t, port) } },
				{ sizeof (get_ds1_ext_config_response_t), offsetof(get_ds1_ext_config_response_t, config) }
			},
			{
				{ SET_DS1_EXT_CONFIG, sizeof (set_ds1_ext_config_request_t), offsetof(set_ds1_ext_config_request_t, config), { offsetof(set_ds1_ext_config_request_t, port) } }
			}
		}
	}
};

static CacheInfo_t * GetCacheInfo(CacheObject_t const * const pco)
{
	CacheInfo_t * pci = NULL;

	int i;

	for (i = 0; i < ElementCount(acie); i++)
	{
		CacheInfoEntry_t * const pcie = &acie[i];

		if (pco->eType == pcie->eType)
		{
			pci = &pcie->ci;
			break;
		}
	}

	return pci;
}

static BOOL AllocateCacheMemory(void * * const ppv, size_t const cb)
{
	BOOL fSuccess = TRUE;

	if (cb != 0)
	{
		void * pv = malloc(cb);

		if (pv == NULL)
			fSuccess = FALSE;
		else
			*ppv = pv;
	}

	return fSuccess;
}

static void AssignCacheInstanceData(CacheObject_t const * const pco, CacheInstanceData_t const * const pcs, BYTE * const pbData)
{
	if (pco->iGeneric1 != 0)
	{
		uint32 * const pGeneric1 = (uint32 *) (pbData + pcs->iOffsetToGeneric1);

		*pGeneric1 = pco->iGeneric1;
	}

	if (pco->iGeneric2 != 0)
	{
		uint32 * const pGeneric2 = (uint32 *) (pbData + pcs->iOffsetToGeneric2);

		*pGeneric2 = pco->iGeneric2;
	}
}

static CacheData_t * GetCacheData(CacheObject_t const * const pco)
{
	CacheData_t * pcd = Find(&lstHead, pco, sizeof *pco, offsetof(CacheData_t, co));
	void * pvReadRequest = NULL;
	void * pvReadResponse = NULL;
	void * pvWriteRequest = NULL;
	void * pvWriteResponse = NULL;

	if (pcd == NULL)
	{
		CacheData_t cd = { 0 };
		CacheInfo_t * const pci = GetCacheInfo(pco);
		
		if (pci == NULL) goto Error;

		if (!AllocateCacheMemory(&pvReadRequest,	pci->cgRead.creq.cb))	goto Error;
		if (!AllocateCacheMemory(&pvReadResponse,	pci->cgRead.crsp.cb))	goto Error;
		if (!AllocateCacheMemory(&pvWriteRequest,	pci->cgWrite.creq.cb))	goto Error;
		if (!AllocateCacheMemory(&pvWriteResponse,	pci->cgWrite.crsp.cb))	goto Error;

		AssignCacheInstanceData(pco, &pci->cgRead.creq.cid, pvReadRequest);
		AssignCacheInstanceData(pco, &pci->cgWrite.creq.cid, pvWriteRequest);

		cd.co = *pco;
		cd.pci = pci;
		cd.pvData[DataIndex_ReadRequest] = pvReadRequest;
		cd.pvData[DataIndex_ReadResponse] = pvReadResponse;
		cd.pvData[DataIndex_WriteRequest] = pvWriteRequest;
		cd.pvData[DataIndex_WriteResponse] = pvWriteResponse;

		pcd = Add(&lstHead, &cd, sizeof cd);
		if (pcd == NULL) goto Error;
	}

Error:

	if (pcd == NULL)
	{
		if (pvReadRequest == NULL)		free(pvReadRequest);
		if (pvReadResponse == NULL)		free(pvReadResponse);
		if (pvWriteRequest == NULL)		free(pvWriteRequest);
		if (pvWriteResponse == NULL)	free(pvWriteResponse);
	}

	return pcd;
}

BOOL FlushWriteCache(void)
{
	return TRUE;
}

void ClearWriteCache(void)
{
}

BOOL ReadCachedObject(CacheObject_t const * const pco, CacheValue_t const * const pcv, AsnAny * const pValue, AsnInteger * const piaErrorStatus)
{
	BOOL fSuccess = FALSE;
	CacheData_t * const pcd = GetCacheData(pco);

	//DebugMessage3("ReadCachedObject: %s, Adapter %u, Generic %u\n", CacheTypeToString(pco->eType), pco->iBoard, pco->iGeneric);
	if (pcd == NULL)
	{
		DebugFailure4("ReadCachedObject: Unable to get cache data for %s, Adapter %u, Generic1 %u, Generic2 %u\n", CacheTypeToString(pco->eType), pco->iBoard, pco->iGeneric1, pco->iGeneric2);
		*piaErrorStatus = SNMP_ERRORSTATUS_GENERR;
		goto Error;
	}

	{
		CacheInfo_t const * const pci = pcd->pci;
		CacheGroup_t const * const pcg = &pci->cgRead;
		CacheRequest_t const * const pcreq = &pcg->creq;
		CacheResponse_t const * const pcrsp = &pcg->crsp;
		void const * const pvData = ((uint8 *) pcd->pvData[DataIndex_ReadResponse]) + pcrsp->iOffsetToUserData + pcv->iOffset;
		BOOL fRefresh = FALSE;

		//DebugMessage5("ReadCachedObject: PCD = %p, OffsetToUserData = %u, iOffset = %u, cb = %u, Data = %p\n", pcd, pcrsp->iOffsetToUserData, pcv->iOffset, pcv->cb, pvData);

		if (pci->rateRefresh == REFRESH_RATE_NEVER)
		{
			fRefresh = (pcd->timeRefresh == 0);
		}
		else
		{
			if (time(NULL) > pcd->timeRefresh)
				fRefresh = TRUE;
		}

		if (fRefresh)
		{
			uint32 nResult = MgmtCommand(pco->iBoard, pcreq->nCommand, pcd->pvData[DataIndex_ReadRequest], pcreq->cb, pcd->pvData[DataIndex_ReadResponse], pcrsp->cb);
			
			if (nResult != MGMT_OK)
			{
				enum { Data_Result, Data_Command, Data_Adapter, Data_Count };
				char const * apszData[Data_Count];

				apszData[Data_Result]	= MgmtResultToString(nResult);
				apszData[Data_Command]	= MgmtCommandToString(pcreq->nCommand);
				apszData[Data_Adapter]	= UnsignedToString(pco->iBoard);

				DebugFailure3("Shim failed with %s on a %s to adapter %s", apszData[Data_Result], apszData[Data_Command], apszData[Data_Adapter]);
				ReportFailureToUser(DS_RAS_MGMT_FAILURE, Data_Count, apszData);
				DebugMessage1("Request data follows:\n%s", RawBytesToString(pcd->pvData[DataIndex_ReadRequest], pcreq->cb));
				DebugMessage1("Response data follows:\n%s", RawBytesToString(pcd->pvData[DataIndex_ReadResponse], pcrsp->cb));
				goto Error;
			}

			pcd->timeRefresh = time(NULL) + pcd->pci->rateRefresh;		
		}

		pValue->asnType = pcv->eAsnType;
		switch (pValue->asnType)
		{
			case ASN_RFC1155_COUNTER:
			{
				if (pcv->cb != sizeof (int32))
					goto Error;

				/* no break; */
			}

			case ASN_INTEGER:
			{
				uint8 const * const pData = pvData;

				switch (pcv->cb)
				{
					case sizeof (int8):
					{
						int8 data;

						memcpy(&data, pData, pcv->cb);
						pValue->asnValue.number = data;
						break;
					}

					case sizeof (int16):
					{
						int16 data;

						memcpy(&data, pData, pcv->cb);
						pValue->asnValue.number = data;
						break;
					}

					case sizeof (int32):
					{
						int32 data;

						memcpy(&data, pData, pcv->cb);
						pValue->asnValue.number = data;
						break;
					}
				}

				*piaErrorStatus = SNMP_ERRORSTATUS_NOERROR;
				break;
			}

			case ASN_OCTETSTRING:
			{
				uint8 const * const pbData = pvData;
				size_t const cbData = pcv->cb;

				pValue->asnValue.string.stream = SnmpUtilMemAlloc(cbData);
				if (pValue->asnValue.string.stream == NULL) goto Error;

				memcpy(pValue->asnValue.string.stream, pbData, cbData);
				pValue->asnValue.string.length = cbData;
				pValue->asnValue.string.dynamic = TRUE;

				*piaErrorStatus = SNMP_ERRORSTATUS_NOERROR;
				break;
			}

			case ASN_INTERNAL_DISPSTRING:
			{
				char const * const pcData = pvData;
				size_t const cbData = pcv->cb + 1;

				pValue->asnValue.string.stream = SnmpUtilMemAlloc(cbData);
				if (pValue->asnValue.string.stream == NULL) goto Error;

				strncpy(pValue->asnValue.string.stream, pcData, cbData);
				pValue->asnValue.string.stream[cbData - 1] = '\0';

				pValue->asnValue.string.length = strlen(pValue->asnValue.string.stream);
				pValue->asnValue.string.dynamic = TRUE;

				/* Override our internal type. */
				pValue->asnType = ASN_RFC1213_DISPSTRING;

				*piaErrorStatus = SNMP_ERRORSTATUS_NOERROR;
				break;
			}

			default:
			{
				*piaErrorStatus = SNMP_ERRORSTATUS_BADVALUE;
				
				break;
			}

		}
	}

	fSuccess = TRUE;

Error:

	return fSuccess;
}

BOOL WriteCachedObject(CacheObject_t const * const pco, CacheValue_t const * const pcv, AsnAny * const pValue, AsnInteger * const piaErrorStatus)
{
	return FALSE;
}

void DeleteCache(void)
{
	Clear(&lstHead);
}
