/***************************************************************************
 *   Property rights (C) 2004-2006 by EVER Sp. z o.o.                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include "ccomm.h"
#include "conf.h"
#include "common.h"

#define SNMP_DRIVER_BUILD 1
#include "snmp.h"

/* 
 * Global variables
 */

sdrv_config sdcSnmp;
int iSNMP_ExtError = 0;
CSNMP cSnmp;

// ping stat
stat_map pingStatMap = { "GET.OID_Ping", ET_STRING, &sdcSnmp.pingStat, NULL };

// available parameters
stat_map paramsStatMap[] = {
	{ "GET.OID_Transfer_Treshold_Upper", ET_UINT, &sdcSnmp.params.ui__pst_upper, &sdcSnmp.params.ui__pst_upper_div },
	{ "GET.OID_Transfer_Treshold_Lower", ET_UINT, &sdcSnmp.params.ui__pst_lower, &sdcSnmp.params.ui__pst_lower_div },
	{ "GET.OID_Transfer_Treshold_AvrUp", ET_UINT, &sdcSnmp.params.ui__pst_avrup, &sdcSnmp.params.ui__pst_avrup_div },
	{ "GET.OID_Transfer_Treshold_AvrLow", ET_UINT, &sdcSnmp.params.ui__pst_avrlow, &sdcSnmp.params.ui__pst_avrlow_div },
	{ "GET.OID_Frequency_Input", ET_UINT, &sdcSnmp.params.ui__frq_input, &sdcSnmp.params.ui__frq_input_div },
	{ "GET.OID_Frequency_Output", ET_UINT, &sdcSnmp.params.ui__frq_output, &sdcSnmp.params.ui__frq_output_div },
	{ "GET.OID_Voltage_Input", ET_UINT, &sdcSnmp.params.ui__vlt_input, &sdcSnmp.params.ui__vlt_input_div },
	{ "GET.OID_Voltage_Output", ET_UINT, &sdcSnmp.params.ui__vlt_output, &sdcSnmp.params.ui__vlt_output_div },
	{ "GET.OID_Voltage_Battery_Load", ET_UINT, &sdcSnmp.params.ui__vlt_batteryload, &sdcSnmp.params.ui__vlt_batteryload_div },
	{ "GET.OID_Voltage_Battery", ET_UINT, &sdcSnmp.params.ui__vlt_battery, &sdcSnmp.params.ui__vlt_battery_div },
	{ "GET.OID_Battery_Capacity", ET_UINT, &sdcSnmp.params.ui__prc_batterycapacity, &sdcSnmp.params.ui__prc_batterycapacity_div },
	{ "GET.OID_Power_Weight", ET_UINT, &sdcSnmp.params.ui__prc_powerweight, &sdcSnmp.params.ui__prc_powerweight_div },
	{ "GET.OID_Sensitivity_Voltage", ET_UINT, &sdcSnmp.params.ui__sns_voltage, &sdcSnmp.params.ui__sns_voltage_div },
	{ "GET.OID_Sensitivity_Frequency", ET_UINT, &sdcSnmp.params.ui__sns_frequency, &sdcSnmp.params.ui__sns_frequency_div },
	{ "GET.OID_Time_Autonomy", ET_UINT, &sdcSnmp.params.ui__tmv_autonomy, &sdcSnmp.params.ui__tmv_autonomy_div },
	{ "GET.OID_Time_OnBattery", ET_UINT, &sdcSnmp.params.ui__tmv_onbattery, &sdcSnmp.params.ui__tmv_onbattery_div },
	{ "GET.OID_Temperature_Inverter", ET_UINT, &sdcSnmp.params.ui__tmp_inverter, &sdcSnmp.params.ui__tmp_inverter_div },
	{ "GET.OID_Temperature_Casing", ET_UINT, &sdcSnmp.params.ui__tmp_casing, &sdcSnmp.params.ui__tmp_casing_div },
	{ "GET.OID_Temperature_Battery", ET_UINT, &sdcSnmp.params.ui__tmp_battery, &sdcSnmp.params.ui__tmp_battery_div },
	{ "GET.OID_Id_Family", ET_STRING, &sdcSnmp.params.sz__uid_family, NULL },
	{ "GET.OID_Id_Version", ET_STRING, &sdcSnmp.params.sz__uid_version, NULL },
	{ "GET.OID_Id_Model", ET_STRING, &sdcSnmp.params.sz__uid_model, NULL },
	{ "GET.OID_Load_Output", ET_UINT, &sdcSnmp.params.ui__load_output, &sdcSnmp.params.ui__load_output_div },
	{ "GET.OID_Extended_Modules", ET_UINT, &sdcSnmp.params.ui__ov_extmodules, &sdcSnmp.params.ui__ov_extmodules_div },
	{ "GET.OID_State_Battery", ET_UINT, &sdcSnmp.params.ui__batterystate, NULL },
	{ "GET.OID_Power_Effective", ET_UINT, &sdcSnmp.params.ui__pwr_effective, &sdcSnmp.params.ui__pwr_effective_div },
	{ "GET.OID_Power_Apparent", ET_UINT, &sdcSnmp.params.ui__pwr_apparent, &sdcSnmp.params.ui__pwr_apparent_div },
	{ "GET.OID_Power_Rlow_battery_tresholdeactive", ET_UINT, &sdcSnmp.params.ui__pwr_reactive, &sdcSnmp.params.ui__pwr_reactive_div },
	{ "GET.OID_Current_Output", ET_UINT, &sdcSnmp.params.ui__cur_output, &sdcSnmp.params.ui__cur_output_div },
	{ "GET.OID_Power_Coefficient", ET_UINT, &sdcSnmp.params.ui__pwr_coefficient, &sdcSnmp.params.ui__pwr_coefficient_div },
	{ "GET.OID_Ups_State", ET_ULONG, &sdcSnmp.ulUpsState, NULL },
	{ NULL, ET_INT, NULL, NULL }
};
// configuration items
stat_map setupStatMap[] = {
	{ "SET.OID_Transfer_Treshold_Upper", ET_UINT, &sdcSnmp.setup.ui__set_pst_upper, &sdcSnmp.setup.ui__set_pst_upper_div },
	{ "SET.OID_Transfer_Treshold_Lower", ET_UINT, &sdcSnmp.setup.ui__set_pst_lower, &sdcSnmp.setup.ui__set_pst_lower_div },
	{ "SET.OID_Transfer_Treshold_AvrUp", ET_UINT, &sdcSnmp.setup.ui__set_pst_avrup, &sdcSnmp.setup.ui__set_pst_avrup_div },
	{ "SET.OID_Transfer_Treshold_AvrLow", ET_UINT, &sdcSnmp.setup.ui__set_pst_avrlow, &sdcSnmp.setup.ui__set_pst_avrlow_div },
	{ "SET.OID_Audible_Alarm", ET_UINT, &sdcSnmp.setup.ui__set_aal_on, NULL },
	{ "SET.OID_Time_PowerFail_To_StandBy", ET_UINT, &sdcSnmp.setup.ui__set_tmr_powerfailtostandby, &sdcSnmp.setup.ui__set_tmr_powerfailtostandby_div },
	{ "SET.OID_Frequency_Input", ET_UINT, &sdcSnmp.setup.ui__set_frq_input, &sdcSnmp.setup.ui__set_frq_input_div },
	{ "SET.OID_Frequency_Output", ET_UINT, &sdcSnmp.setup.ui__set_frq_output, &sdcSnmp.setup.ui__set_frq_output_div },
	{ "SET.OID_Sensitivity_Voltage", ET_UINT, &sdcSnmp.setup.ui__set_sns_voltage, &sdcSnmp.setup.ui__set_sns_voltage_div },
	{ "SET.OID_Sensitivity_Frequency", ET_UINT, &sdcSnmp.setup.ui__set_sns_frequency, &sdcSnmp.setup.ui__set_sns_frequency_div },
	{ NULL, ET_INT, NULL, NULL }
};

// additional-specific items
char spec_battery_low_id[] = "GET.OID_Battery_Capacity";


int snmp_ioctl(long lCommand, void *lpvBuff, int *lpiBuffSize)
{
	switch(lCommand)
	{
	case IOCTL_INIT: 
		return SNMP_DoInit(lpvBuff, lpiBuffSize);

	case IOCTL_UNINIT:
		return SNMP_DoUnInit(lpvBuff, lpiBuffSize);
		
	case IOCTL_GETCONFIGFILENAME:
		return SNMP_DoGetConfigFileName(lpvBuff, lpiBuffSize);

	case IOCTL_AUTOCONFIGURE: 
		return SNMP_DoAutoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_CONFIGURE: 
		return SNMP_DoConfigure(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSINFOMASK: 
		return SNMP_DoGetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSINFOMASK:
		return SNMP_DoSetUpsInfoMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATESMASK:
		return SNMP_DoGetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSTATESMASK:
		return SNMP_DoSetUpsStateMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSTATE:
		return SNMP_DoGetUpsState(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMSMASK:
		return SNMP_DoGetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSPARAMSMASK:
		return SNMP_DoSetUpsParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSPARAMS:
		return SNMP_DoGetUpsParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMSMASK:
		return SNMP_DoGetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMSMASK:
		return SNMP_DoSetUpsSetupParamsMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSSETUPPARAMS:
		return SNMP_DoGetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_SET_UPSSETUPPARAMS:
		return SNMP_DoSetUpsSetupParams(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTICMASK:
		return SNMP_DoGetUpsCharacteristicMask(lpvBuff, lpiBuffSize);

	case IOCTL_GET_UPSCHARACTERISTIC:
		return SNMP_DoGetUpsCharacteristic(lpvBuff, lpiBuffSize);

	case IOCTL_GET_DRIVER_INFO:
		return SNMP_DoGetDriverInfo(lpvBuff, lpiBuffSize);

	case IOCTL_GET_ERRORNO:
		return SNMP_DoGetExtendedError(lpvBuff, lpiBuffSize);

	case IOCTL_TESTUPSLINK:
		return SNMP_DoTestUpsLink(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMSCOUNT:
		return SNMP_DoGetConfigParamsCount(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAMS:
		return SNMP_DoGetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_GETCONFIGPARAM:
		return SNMP_DoGetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAMS:
		return SNMP_DoSetConfigParams(lpvBuff, lpiBuffSize);

	case IOCTL_SETCONFIGPARAM:
		return SNMP_DoSetConfigParam(lpvBuff, lpiBuffSize);

	case IOCTL_UPDATECONFIG:
		return SNMP_DoUpdateConfig(lpvBuff, lpiBuffSize);

	case IOCTL_GETDRIVERMODE:
		return SNMP_DoGetDriverMode(lpvBuff, lpiBuffSize);

	case IOCTL_SHUTDOWNUPS:
		return SNMP_DoUpsShutdown(lpvBuff, lpiBuffSize);

	default:
		return IOCTL_ERROR_CMD_NOTSUPPORTED;
	}
}

// Function name	: SNMP_DoInit
// Description		: Inicjalizacja drivera
int SNMP_DoInit( void *lpvBuff, int *lpiBuffSize )
{
	int iRet;
	
	iSNMP_ExtError = 0;
	if (SNMP_ReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
		
	iRet = cSnmp.Init((unsigned char*)sdcSnmp.szDevAddress, sdcSnmp.uiPortNumber);
	if (iRet != SNMP_ERROR_SUCCESS) {
		//printf("INIT ERROR: %d\n", iRet);
		iSNMP_ExtError = IOCTL_ERROR_COMM_INITFAIL;
		return IOCTL_ERROR;
	}

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoUnInit
// Description		: Deinicjalizacja drivera
int SNMP_DoUnInit( void *lpvBuff, int *lpiBuffSize )
{
	SNMP_ReleaseStatTable(sdcSnmp);
	cSnmp.UnInit();
	iSNMP_ExtError=0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoUnInit
// Description		: 
int SNMP_DoGetConfigFileName( void *lpvBuff, int *lpiBuffSize )
{
	if (lpvBuff != NULL) {
		if ( *lpiBuffSize >= strlen(DRIVER_CONFIG_FILE) )
			strcpy((char *)lpvBuff, DRIVER_CONFIG_FILE );
		else
			return IOCTL_ERROR_INVALIDARGUMENT;
	} else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoAutoConfigure
// Description		: Self configure
int SNMP_DoAutoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	/* TODO */
	strcpy(sdcSnmp.szDevAddress, "192.168.0.1");
	sdcSnmp.uiPortNumber = 0;
	strcpy(sdcSnmp.szReadPassword, "public");
	strcpy(sdcSnmp.szWritePassword, "public");
	strcpy(sdcSnmp.szTrapPassword, "public");
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoConfigure
// Description		: Configure driver to work properly with UPS
int SNMP_DoConfigure( void *lpvBuff, int *lpiBuffSize )
{
	/* TODO */
	strcpy(sdcSnmp.szDevAddress, "192.168.0.1");
	sdcSnmp.uiPortNumber = 0;
	strcpy(sdcSnmp.szReadPassword, "public");
	strcpy(sdcSnmp.szWritePassword, "public");
	strcpy(sdcSnmp.szTrapPassword, "public");
	
	SNMP_DoUnInit(NULL, NULL);
	SNMP_DoInit(NULL, NULL);

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetUpsInfoMask
// Description		: Zwraca informacje o wypelnieniu struktury informacji o UPS-ie
int SNMP_DoGetUpsInfoMask( void *lpvBuff, int *lpiBuffSize )
{
	unsigned long ulMask = UI_PARAMETERS | UI_UPS_STATE;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoSetUpsInfoMask
// Description		: 
int	SNMP_DoSetUpsInfoMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetUpsStateMask
// Description		: 
int	SNMP_DoGetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask = US_POWERON | US_POWERFAIL | US_STANDBY | /*US_OVERLOAD | US_AVRUP |
		US_AVRLOWER | US_BATTERYDEPLETED | */US_WATCH/* | US_SHORT | US_INTERNALERROR | US_BATTERYLOW |
		US_INVERTERFAILURE | US_TEMPERATURETOOHIGH | US_TEMPERATURETOOLOW | US_BADEXTERNALMODULES*/;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoSetUpsStateMask
// Description		: 
int	SNMP_DoSetUpsStateMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetUpsState
// Description		: Return actual UPS state
int	SNMP_DoGetUpsState(void *lpvBuff, int *lpiBuffSize)
{
	int iRet;
	unsigned long ulUpsState = 0;

	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof(ulUpsState))
			return IOCTL_ERROR_INVALIDARGUMENT;

	if (SNMP_ReadGetStats(sdcSnmp) != 1)
		return IOCTL_ERROR_COMM_FAILTORECEIVE;
	if (SNMP_StatMap_Compile(paramsStatMap, sdcSnmp) != 1)
		return IOCTL_ERROR;
	
	// low battery support
	if (spec_battery_low_id != NULL) {
		int i = 0;
		while (paramsStatMap[i].name != NULL){
			if (!strcasecmp(spec_battery_low_id, paramsStatMap[i].name)) {
				unsigned int ulbv = 0;
				memcpy(&ulbv, paramsStatMap[i].res_addr, sizeof(unsigned int));
				if (ulbv <= sdcSnmp.uiLowBatteryTreshold)
					flag_set(&sdcSnmp.ulUpsState, US_BATTERYLOW);
				break;
			}
			i++;
		}
	}
	
	memcpy(lpvBuff, &sdcSnmp.ulUpsState, *lpiBuffSize);

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetUpsParamsMask
// Description		: 
int	SNMP_DoGetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	__int64 ulMask = 0 | UP_PST_UPPER | UP_PST_LOWER | UP_PST_AVRUP | UP_PST_AVRLOW |
		UP_FRQ_INPUT | UP_FRQ_OUTPUT | UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | 
		UP_PRC_BATTERYCAPACITY | UP_PRC_POWERWEIGHT | UP_TMV_AUTONOMY | UP_TMP_INVERTER | 
		UP_TMP_CASING | UP_UID_FAMILY | UP_UID_MODEL;

	if ( *lpiBuffSize == sizeof( __int64 ) )
		memcpy( lpvBuff, &ulMask, sizeof( __int64 ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoSetUpsParamsMask
// Description		: 
int	SNMP_DoSetUpsParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetUpsParams
// Description		: 
int	SNMP_DoGetUpsParams(void *lpvBuff, int *lpiBuffSize)
{
	int iRet;
	supsp sup;

	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof( supsp ))
			return IOCTL_ERROR_INVALIDARGUMENT;

	if (SNMP_ReadGetStats(sdcSnmp) != 1)
		return IOCTL_ERROR_COMM_FAILTORECEIVE;
	if (SNMP_StatMap_Compile(paramsStatMap, sdcSnmp) != 1)
		return IOCTL_ERROR;
	
	sdcSnmp.params.iMask = 0 | UP_PST_UPPER | UP_PST_LOWER | UP_PST_AVRUP | UP_PST_AVRLOW |
		UP_FRQ_INPUT | UP_FRQ_OUTPUT | UP_VLT_INPUT | UP_VLT_OUTPUT | UP_VLT_BATTERY | 
		UP_PRC_BATTERYCAPACITY | UP_PRC_POWERWEIGHT | UP_TMV_AUTONOMY | UP_TMP_INVERTER | 
		UP_TMP_CASING | UP_UID_FAMILY | UP_UID_MODEL;
	
	if (!strlen(sdcSnmp.params.sz__uid_family))
		sprintf( sdcSnmp.params.sz__uid_family, "%s", DRIVER_UPS_FAMILY );
	
	memcpy( lpvBuff, &sdcSnmp.params, sizeof(sdcSnmp.params) );

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetUpsSetupParamsMask
// Description		: 
int	SNMP_DoGetUpsSetupParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoSetUpsSetupParamsMask
// Description		: 
int	SNMP_DoSetUpsSetupParamsMask(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetUpsSetupParams
// Description		: 
int	SNMP_DoGetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoSetUpsSetupParams
// Description		: 
int	SNMP_DoSetUpsSetupParams(void *lpvBuff, int *lpiBuffSize)
{
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetDriverInfo
// Description		: 
int	SNMP_DoGetDriverInfo(void *lpvBuff, int *lpiBuffSize)
{
	if (*lpiBuffSize != sizeof(sdrv_info))
		return IOCTL_ERROR_INVALIDARGUMENT;
	sprintf( (( lpsdrv_info )lpvBuff )->szName, "%s DRIVER", DRIVER_UPS_PREFIX );
	sprintf( (( lpsdrv_info )lpvBuff )->szFamily, "%s", DRIVER_UPS_FAMILY );
	((lpsdrv_info)lpvBuff)->eLink = ul_ethernet;
	((lpsdrv_info)lpvBuff)->uiVersionMajor = DRIVER_VERSION_MAJOR;
	((lpsdrv_info)lpvBuff)->uiVersionMinor = DRIVER_VERSION_MINOR;
	strcpy(((lpsdrv_info)lpvBuff)->szCfgFileName, DRIVER_CONFIG_FILE);

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetExtendedError
// Description		: Get extended error information - return value of last error
int	SNMP_DoGetExtendedError(void *lpvBuff, int *lpiBuffSize)
{
	return iSNMP_ExtError;
}

// Function name	: SNMP_DoGetUpsCharacteristicMask
// Description		: 
int	SNMP_DoGetUpsCharacteristicMask(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulMask=0;

	if ( *lpiBuffSize == sizeof( unsigned long ) )
		memcpy( lpvBuff, &ulMask, sizeof( unsigned long ) );
	else
		return IOCTL_ERROR_INVALIDARGUMENT;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetUpsCharacteristic
// Description		: 
int	SNMP_DoGetUpsCharacteristic(void *lpvBuff, int *lpiBuffSize)
{
	int iRet;
	supsch such;

	if (lpiBuffSize != NULL)
		if (*lpiBuffSize < sizeof( supsch ))
			return IOCTL_ERROR_INVALIDARGUMENT;

	memset( &such, 0, sizeof(supsch) );
	such.ulMask = 0;
	
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoTestUpsLink
// Description		: 
int	SNMP_DoTestUpsLink(void *lpvBuff, int *lpiBuffSize)
{
	if (SNMP_ReadPingStat(sdcSnmp) == 1)
		return IOCTL_ERROR_SUCCESS;
	else
		return IOCTL_ERROR_COMM_LINKFAIL;
}

// Function name	: SNMP_DoGetConfigParamsCount
// Description		: 
int	SNMP_DoGetConfigParamsCount(void *lpvBuff, int *lpiBuffSize)
{
	unsigned long ulCount = INT_MAX_SETUPITEMS;

	if ( lpiBuffSize != NULL )
		if ( *lpiBuffSize < sizeof( unsigned long ) )
			return IOCTL_ERROR_INVALIDARGUMENT;

	memcpy( lpvBuff, &ulCount, sizeof( ulCount ) );

	iSNMP_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetConfigParams
// Description		: 
int	SNMP_DoGetConfigParams(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoSetConfigParams
// Description		: Apply configuration from external program or just using parametrized interface
int	SNMP_DoSetConfigParams(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoGetConfigParam
// Description		: 
int	SNMP_DoGetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoSetConfigParam
// Description		: 
int	SNMP_DoSetConfigParam(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_ReadConfig
// Description		: Read configuration from file
int	SNMP_ReadConfig()
{
	char *fp;
	fp = get_config_filepath(DRIVER_CONFIG_FILE);
	if (fp==NULL) {
		return IOCTL_ERROR;
	}
	CConf *pCfgDev = new CConf;
	if (pCfgDev->init(fp)!=CONF_SUCCESS) {
		free(fp);
		delete pCfgDev;
		return IOCTL_ERROR_CONFIG_READFAIL;
	} else {
		free(fp);
		pCfgDev->parse_config();
	}
	
	char szBuf[128] = "", *s;
	strcpy(sdcSnmp.szDevAddress, ((s=pCfgDev->getcfgitemvalue("snmp_card_address"))=="")?"192.168.0.1":s);
	strcpy(sdcSnmp.szReadPassword, ((s=pCfgDev->getcfgitemvalue("password_read"))=="")?"public":s);
	strcpy(sdcSnmp.szWritePassword, ((s=pCfgDev->getcfgitemvalue("password_write"))=="")?"public":s);
	strcpy(sdcSnmp.szTrapPassword, ((s=pCfgDev->getcfgitemvalue("password_trap"))=="")?"public":s);
	sdcSnmp.uiPortNumber = atoi(((s=pCfgDev->getcfgitemvalue("local_port_number"))=="")?"0":s);
	sdcSnmp.uiOpTimeout = atoi(((s=pCfgDev->getcfgitemvalue("snmp_operation_timeout"))=="")?"60":s);
	sdcSnmp.uiLowBatteryTreshold = atoi(((s=pCfgDev->getcfgitemvalue("low_battery_treshold"))=="")?"10":s);
	
	
	SNMP_ReleaseStatTable(sdcSnmp);
	if (SNMP_CreateStatTable(*pCfgDev, sdcSnmp) != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
	
	delete pCfgDev;
	
	return IOCTL_ERROR_SUCCESS;
}

int SNMP_ReleaseStatTable(sdrv_config& sc)
{
	int j=0;
	for (int i=0; i<sc.statsGetCount; i++) {
		if (sc.statsGet[i].oid.lpbSnmpData!=NULL) {
			delete [] sc.statsGet[i].oid.lpbSnmpData;
			j++;
		}
	}
	for (int i=0; i<sc.statsSetCount; i++) {
		if (sc.statsSet[i].oid.lpbSnmpData!=NULL) {
			delete [] sc.statsSet[i].oid.lpbSnmpData;
			j++;
		}
	}
	//printf("Released %d memory allocations!\n", j);
	return IOCTL_ERROR_SUCCESS;
}

int SNMP_CreateStatTable(CConf& pc, sdrv_config& sc)
{
	char *s, *soid, *div, szbuff[256];
	int i, isc;
	unsigned int uiDiv;
	
	// PING stat
	if (pingStatMap.name != NULL) {
		s = pc.getcfgitemvalue(pingStatMap.name);
		if (s != "") {
			strcpy(sc.pingStat.szId, pingStatMap.name);
			uint ntci = strlen(pingStatMap.name);
			if (ntci>=MAX_STATID) ntci=MAX_STATID-1;
			sc.pingStat.szId[ntci] = '\0';
			
			soid = strtok(s, "/");
			if (soid != "" && soid != NULL && strlen(soid)>0) {
				//printf("PING:%s; ", pingStatMap.name);
				strcpy(sc.pingStat.szOID, soid);
				//printf("OID:%s; ", sc.pingStat.szOID);
				//printf("\n");
			}
			if (strlen(sc.pingStat.szOID)>0) {
				sc.pingStat.oid.ulLength = 0;
				sc.pingStat.oid.lpbSnmpData = new unsigned char[256];
				if (cSnmp.ConvertStringToOID(&sc.pingStat.oid, sc.pingStat.szOID)==SNMP_ERROR){
					printf("Snmp OID Conversion error! (%s)\n", sc.pingStat.szOID);
				}
			}
		}
	}
	
	// INFO Parameters
	memset(sc.statsGet, 0, sizeof(sc.statsGet));
	i = 0;
	isc = 0;
	while(paramsStatMap[i].name != NULL) {
		s = pc.getcfgitemvalue(paramsStatMap[i].name);
		if ((s != "") && (isc < MAX_GET_STATS)) {
			strcpy(sc.statsGet[isc].szId, paramsStatMap[i].name);
			uint ntci = strlen(paramsStatMap[i].name);
			if (ntci>=MAX_STATID) ntci=MAX_STATID-1;
			sc.statsGet[isc].szId[ntci] = '\0';
			
			if (paramsStatMap[i].div_addr != NULL) {
				div = strchr(s, '/');
				if (div != NULL)
					uiDiv = (unsigned int)strtoul(div+1, NULL, 10);
				else
					uiDiv = 1;
				memcpy(paramsStatMap[i].div_addr, &uiDiv, sizeof(unsigned int));
			}
			soid = strtok(s, "/");
			if (soid != "" && soid != NULL && strlen(soid)>0) {
				//printf("STAT:%s; ", paramsStatMap[i].name);
				strcpy(sc.statsGet[isc].szOID, soid);
				//printf("OID:%s; ", sc.statsGet[isc].szOID);
				sprintf(szbuff, "REMAP.%s", sc.statsGet[isc].szId);
				//printf("NAME:%s; ", szbuff);
				s = pc.getcfgitemvalue(szbuff);
				//printf("RESULT:%s; ", s);
				if ((s != "") && (s != NULL)) {
					sprintf(sc.statsGet[isc].szMap, "[%s]", s);
					//printf("REMAP:%s; ", sc.statsGet[isc].szMap);
				}
				//printf("\n");
			}
			if (strlen(sc.statsGet[isc].szOID)>0) {
				sc.statsGet[isc].oid.ulLength = 0;
				sc.statsGet[isc].oid.lpbSnmpData = new unsigned char[256];
				if (cSnmp.ConvertStringToOID(&sc.statsGet[isc].oid, sc.statsGet[isc].szOID)==SNMP_ERROR){
					printf("Snmp OID Conversion error! (%s)\n", sc.statsGet[isc].szOID);
				}
				isc++;
			}
		}	
		i++;
	}
	sc.statsGetCount = isc;
	
	// SETUP parameters	
	memset(sc.statsSet, 0, sizeof(sc.statsSet));
	i = 0;
	isc = 0;
	while(setupStatMap[i].name != NULL) {
		s = pc.getcfgitemvalue(setupStatMap[i].name);
		if ((s != "") && (isc < MAX_SET_STATS)) {
			strcpy(sc.statsSet[isc].szId, setupStatMap[i].name);
			uint ntci = strlen(setupStatMap[i].name);
			if (ntci>=MAX_STATID) ntci=MAX_STATID-1;
			sc.statsSet[isc].szId[ntci] = '\0';
			
			if (setupStatMap[i].div_addr != NULL) {
				div = strchr(s, '/');
				if (div != NULL)
					uiDiv = (unsigned int)strtoul(div+1, NULL, 10);
				else
					uiDiv = 1;
				memcpy(setupStatMap[i].div_addr, &uiDiv, sizeof(unsigned int));
			}
			soid = strtok(s, "/");
			if (soid != "" && soid != NULL && strlen(soid)>0) {
				//printf("STAT:%s; ", setupStatMap[i].name);
				strcpy(sc.statsSet[isc].szOID, soid);
				//printf("OID:%s; ", sc.statsSet[isc].szOID);
				sprintf(szbuff, "REMAP.%s", sc.statsSet[isc].szId);
				//printf("NAME:%s; ", szbuff);
				s = pc.getcfgitemvalue(szbuff);
				//printf("RESULT:%s; ", s);
				if ((s != "") && (s != NULL)) {
					sprintf(sc.statsSet[isc].szMap, "[%s]", s);
					//printf("REMAP:%s; ", sc.statsSet[isc].szMap);
				}
				//printf("\n");
			}
			if (strlen(sc.statsSet[isc].szOID)>0) {
				sc.statsSet[isc].oid.ulLength = 0;
				sc.statsSet[isc].oid.lpbSnmpData = new unsigned char[256];
				if (cSnmp.ConvertStringToOID(&sc.statsSet[isc].oid, sc.statsSet[isc].szOID)==SNMP_ERROR){
					printf("Snmp OID Conversion error! (%s)\n", sc.statsSet[isc].szOID);
				}
				isc++;
			}
		}	
		i++;
	}
	sc.statsSetCount = isc;

	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoUpdateConfig
// Description		: 
int	SNMP_DoUpdateConfig(void *lpvBuff, int *lpiBuffSize)
{
	if (SNMP_ReadConfig() != IOCTL_ERROR_SUCCESS)
		return IOCTL_ERROR_CONFIG_READFAIL;
	else {
		SNMP_DoUnInit(NULL,NULL);
		SNMP_DoInit(NULL,NULL);
	}
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Function name	: SNMP_DoGetDriverMode
// Description		: 
// Return type		: int
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int	SNMP_DoGetDriverMode(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_CMD_NOTSUPPORTED;
}

// Function name	: SNMP_DoUpsShutdown
// Description		: 
// Return type		: int 
// Argument         : void *lpvBuff
// Argument         : int *lpiBuffSize
int SNMP_DoUpsShutdown(void *lpvBuff, int *lpiBuffSize)
{
	iSNMP_ExtError = 0;
	return IOCTL_ERROR_SUCCESS;
}

// Reparse declared "stat" map and fill associated structures
int SNMP_StatMap_ParseByType(stat_map *sm, void *lpvBuff)
{
	return 1;	// success
}

int SNMP_StatMap_Compile(stat_map *sm, sdrv_config& sc)
{
	int i=0, isc=0;
	char szTemp[256]="", szValBuf[256]="";
	unsigned long ulValBuff=0;
	
	while(sm[i].name != NULL) {
		for (int m=0; m<sc.statsGetCount; m++) {
			if (!strcasecmp(sm[i].name, sc.statsGet[m].szId)) {
				
				// parse data
				switch(sc.statsGet[m].iType) {
				case DT_OCTET_STRING: {
						//printf("Data: %s; Type: OCTET_STRING\n", sc.statsGet[m].szId);
						strncpy(szValBuf, (const char*)sc.statsGet[m].bDataBuff, sc.statsGet[m].iSize);
						szValBuf[sc.statsGet[m].iSize]='\0';
						if(sm[i].type!=ET_STRING)
							continue;
						else
							memcpy(sm[i].res_addr, szValBuf, strlen(szValBuf));
						break;
					}
					
				case DT_INTEGER:
				case PT_Counter:
				case PT_Gauge: 
				case PT_TimeTicks: {
						memcpy(&ulValBuff, sc.statsGet[m].bDataBuff, sizeof(unsigned long));
						switch(sm[i].type) {
							case ET_BYTE: {
								BYTE bVal=(BYTE)ulValBuff;
								memcpy(sm[i].res_addr, &bVal, sizeof(BYTE));
								//printf("Data: %s; Val: %ld\n", sc.statsGet[m].szId, ulValBuff);
								break;
							}
							case ET_INT: {
								int iVal=(int)ulValBuff;
								memcpy(sm[i].res_addr, &iVal, sizeof(int));
								//printf("Data: %s; Val: %ld\n", sc.statsGet[m].szId, ulValBuff);
								break;
							}
							case ET_UINT: {
								unsigned int uiVal=(unsigned int)ulValBuff;
								memcpy(sm[i].res_addr, &uiVal, sizeof(unsigned int));
								//printf("Data: %s; Val: %ld\n", sc.statsGet[m].szId, ulValBuff);
								break;
							}
							case ET_LONG: {
								long lVal=(long)ulValBuff;
								memcpy(sm[i].res_addr, &lVal, sizeof(long));
								//printf("Data: %s; Val: %ld\n", sc.statsGet[m].szId, ulValBuff);
								break;
							}
							case ET_ULONG: {
								unsigned long ulVal=(unsigned long)ulValBuff;
								memcpy(sm[i].res_addr, &ulVal, sizeof(unsigned long));
								//printf("Data: %s; Val: %ld\n", sc.statsGet[m].szId, ulValBuff);
								break;
							}
						}
						
						// Remap value
						if (strlen(sc.statsGet[m].szMap)>2) {
							char *tmp = strdup(sc.statsGet[m].szMap);
							bool bHex = false;
							if(tmp!=NULL) {
								char *tok = strtok(tmp, ":");
								if (tok!=NULL && !strcasecmp(tok, "hex")) {
									bHex = true;
									tok = strtok(NULL, ",; ");
								}
								while (tok!=NULL) {
									unsigned long ulVal_a = strtoul(tok, NULL, bHex?16:10);
									unsigned long ulVal_b = 0;
									
									char *valb = strrchr(tok, '=');
									if (valb!=NULL) {
										ulVal_b = strtoul(valb+1, NULL, bHex?16:10);
										if ((ulValBuff & ulVal_a)==ulVal_a) {
											flag_clear(&ulValBuff, ulVal_a);
											flag_set(&ulValBuff, ulVal_b);
										}
									}
									tok = strtok(NULL, ",; ");
								}
								free(tmp);
							}
						}
						break;
					}
					
				default:
					break;
				}
				// break the loop
				break;
			}
		}
		i++;
	}
	
	return 1;	// success
}

// Read all configured information stats (GET_)
int SNMP_ReadGetStats(sdrv_config& sc)
{
	int iRes=0, iSize=0, iType=0;
	unsigned char bBuff[SNMP_MAX_PDU_LENGTH] = {0};

	//printf("Reading %d stats.\n", sc.statsGetCount);
	for (int i=0; i<sc.statsGetCount; i++){
		
		// do not try to read unavailable stat if previous read returned NoSuchName error
		if (sc.statsGet[i].iErrorStatus == SNMP_ERROR_NOSUCHNAME)
			continue;
		
		// Main snmp frame
		iRes = cSnmp.CreateFrame(1, sc.szReadPassword, GetRequest, MON_REQUESTID+i, sc.statsGet[i].oid);
		if (iRes != SNMP_ERROR_SUCCESS) {
			//printf("Frame creating error! (%d)\n", iRes);
			return -1;
		} else {
			iRes = cSnmp.SendPdu();
			if (iRes != SNMP_ERROR_SUCCESS) {
				//printf("Frame sending error! (%s)\n", sc.statsGet[i].szOID);
				break;
			} else {
				memset(bBuff, 0, sizeof(bBuff));
				iRes = cSnmp.Receive(&cSnmp.sPduRec, &iType, &iSize, bBuff, sc.uiOpTimeout);
				if (iRes == SNMP_ERROR_SUCCESS) {
					if (cSnmp.sPduRec.iErrorStatus != 0) {
						//printf("Pdu Error! (%s, %d) -> OID: %s\n", cSnmp.GetErrorString(cSnmp.sPduRec.iErrorStatus), 
						//	cSnmp.sPduRec.iErrorIndex, sc.statsGet[i].szOID);
					} else {
						int iIdx = cSnmp.sPduRec.ulRequestId - MON_REQUESTID;
						if (iIdx>=0) {
							sc.statsGet[iIdx].iSize = iSize;
							sc.statsGet[iIdx].iType = iType;
							memcpy(sc.statsGet[iIdx].bDataBuff, bBuff, SNMP_MAX_PDU_LENGTH);
						}
					}
				} else {
					if (cSnmp.sPduRec.iErrorStatus == SNMP_ERROR_NOSUCHNAME)
						sc.statsGet[i].iErrorStatus = SNMP_ERROR_NOSUCHNAME;
					
					//printf("Snmp frame receiving error!\nOID: %s\nError: %s(%d)", sc.statsGet[i].szOID, 
					//	cSnmp.GetErrorString(iRes), iRes);
					
					//if (iRes <= SNMP_ERROR_SOCKET_DISCONNECT)
					return -1;
				}
			}
		}
	}
	
	return 1;
}

int SNMP_ReadPingStat(sdrv_config& sc)
{
	int iRes=0, iSize=0, iType=0;
	unsigned char bBuff[SNMP_MAX_PDU_LENGTH] = {0};

	//printf("Reading ping stat.\n");
		// do not try to read unavailable stat if previous read returned NoSuchName error
	if (sc.pingStat.iErrorStatus == SNMP_ERROR_NOSUCHNAME)
		return -1;
	
	// Main snmp frame
	iRes = cSnmp.CreateFrame(1, sc.szReadPassword, GetRequest, MON_REQUESTID, sc.pingStat.oid);
	if (iRes != SNMP_ERROR_SUCCESS) {
		//printf("Frame creating error! (%d)\n", iRes);
		return -1;
	} else {
		iRes = cSnmp.SendPdu();
		if (iRes != SNMP_ERROR_SUCCESS) {
			//printf("Frame sending error! (%s)\n", sc.pingStat.szOID);
			return -1;
		} else {
			memset(bBuff, 0, sizeof(bBuff));
			iRes = cSnmp.Receive(&cSnmp.sPduRec, &iType, &iSize, bBuff, sc.uiOpTimeout);
			if (iRes == SNMP_ERROR_SUCCESS) {
				if (cSnmp.sPduRec.iErrorStatus != 0) {
					//printf("Pdu Error! (%s, %d) -> OID: %s\n", cSnmp.GetErrorString(cSnmp.sPduRec.iErrorStatus), 
					//	cSnmp.sPduRec.iErrorIndex, sc.pingStat.szOID);
					return -1;
				} else {
					if (cSnmp.sPduRec.ulRequestId == MON_REQUESTID) {
						sc.pingStat.iSize = iSize;
						sc.pingStat.iType = iType;
						memcpy(sc.pingStat.bDataBuff, bBuff, SNMP_MAX_PDU_LENGTH);
					}
				}
			} else {
				if (cSnmp.sPduRec.iErrorStatus == SNMP_ERROR_NOSUCHNAME)
					sc.pingStat.iErrorStatus = SNMP_ERROR_NOSUCHNAME;
					
				//printf("Snmp frame receiving error!\nOID: %s\nError: %s(%d)", sc.pingStat.szOID, 
				//	cSnmp.GetErrorString(iRes), iRes);
					
				return -1;
			}
		}
	}

	return 1;
}

// Read all configured setup stats (SET_)
int SNMP_ReadSetStats(sdrv_config& sc)
{
	for (int i = 0; i < sc.statsSetCount; i++){
		;
	}
	return 1;
}


