#include "ArecaWrapper.h"
#include <vector>
#include <time.h>

#define MAX_EVENT_BUFF_SIZE       512

#define		UNKNOWN_EVT_STR	"Unknown Event"

	
CArecaWrapper::CArecaWrapper(int nEventLevel)
    :  m_nEventLevel( nEventLevel )
{
    pthread_mutex_init(&mutex_lock, NULL);
}

CArecaWrapper::~CArecaWrapper()
{
    pthread_mutex_destroy(&mutex_lock);
    int nInterfaceIndex;
    CArclib         *ctrl;
    for (nInterfaceIndex =0; nInterfaceIndex < m_nTotalCtrl; nInterfaceIndex++)
	{
		ctrl = &m_Arc[nInterfaceIndex];
		LinuxIoctlInterface *out = (LinuxIoctlInterface*)ctrl->ArcGetInterface();
		delete out;
	}
}

// libacrlib에서 사용하는 pSYS_TIME 타입의 값을 time_t로 변환한다.
time_t CArecaWrapper::GetTime_t(pSYS_TIME evtTime)
{
	/*
    tm              tmpTime;
    
    tmpTime.tm_year = evtTime->u.tmYear+100;
	tmpTime.tm_mon = evtTime->u.tmMonth-1;
	tmpTime.tm_mday = evtTime->u.tmDate;
	tmpTime.tm_hour = evtTime->u.tmHour;
	tmpTime.tm_min = evtTime->u.tmMinute;
	tmpTime.tm_sec = evtTime->u.tmSecond;

	return mktime(&tmpTime); 
	*/

	time_t rawtime;
	struct tm * timeinfo;
	time ( &rawtime );
	timeinfo = gmtime ( &rawtime );

	timeinfo->tm_year = evtTime->u.tmYear+100;
	timeinfo->tm_mon = evtTime->u.tmMonth-1;
	timeinfo->tm_mday = evtTime->u.tmDate;
	timeinfo->tm_hour = evtTime->u.tmHour;
	timeinfo->tm_min = evtTime->u.tmMinute;
	timeinfo->tm_sec = evtTime->u.tmSecond;
	timeinfo->tm_isdst = 0; // disable daylight saving time

	time_t ret = mktime ( timeinfo );

	return ret;
}

/*
bool CArecaWrapper::DisableUpload()
{
	system( "touch /user/service/vmasd.disable" );

	struct stat st;

	if( lstat( "/user/service/vmasd.disable", &st ) != 0 )
	{
		ERROR_LOG( "can't touch vmasd.disable" );
		return false;

	}

	INFO_LOG( "disable upload." );

	return true;
}

bool CArecaWrapper::IsDisableUpload( int eventCategory, int eventId )
{
	switch( eventCategory )
	{
		case EVENT_DEVICE:		// device event
			/// The meaning of eventId
			///  0 : Device Inserted
			///  1 : Device Removed
			///  2 : Reading Error
			///  3 : Writing Error
			///  4 : ATA Ecc Error
			///  5 : Change ATA Mode
			///  6 : Time Out Error
			///  7 : Device Failed
			///  8 : PCI Parity Error
			///  9 : Dev Fail (SMART)
			/// 10 : Pass


		case EVENT_VOLUMESET:	// volumeset event
		case EVENT_RAIDSET:     // raidset event
	}
}
*/

//int CArecaWrapper::GetEvent(int nEventFlag, char *pszResult)
bool CArecaWrapper::GetEvent(int nEventFlag, list<CEventData>& listRet)
{
	CArclib         *ctrl;
	ARC_STATUS      stat = ARC_SUCCESS;
	bool             gotEvent = false;
	int             nCount = 0;             // total number of event objects we got
	int             i;
	int             nCurEvtLevel = EVT_SERIOUS;
	sSYSTEM_INFO    sysInfo;
	BYTE            ptr1[64];
	char            tmpStr[64];
	char            eventStrBuf[MAX_EVENT_BUFF_SIZE];
	int				eventCategory = 0;
	int				eventId = 0;
	
	//the time event occurred, defined in arclib.h
    pSYS_TIME evtTime = (pSYS_TIME)new sSYS_TIME;
	    
    pthread_mutex_lock(&mutex_lock);
	if (m_nTotalCtrl == 0) {
        LOG(LERR, "No Raild controller was found.");
		pthread_mutex_unlock(&mutex_lock);
		if( evtTime ) 
		{
			delete evtTime;
		}
		return false;
	}

	ctrl = &m_Arc[0];

	memset(ptr1, 0, sizeof(ptr1));
	memset(tmpStr, 0, sizeof(tmpStr));
	memset(eventStrBuf, 0, sizeof(eventStrBuf));

	// event object defined in arclib.h
	pEVENT_DATA pEventObject = (pEVENT_DATA)new sEVENT_DATA;
	stat = ctrl->ArcGetSysInfo(&sysInfo);
	if( stat != ARC_SUCCESS )
	{
        LOG(LERR, "ArcGetSysInfo Failed: %d",stat);    
		pthread_mutex_unlock(&mutex_lock);
		if( pEventObject )
		{
			delete pEventObject;
		}
		if( evtTime )
		{
			delete evtTime;
		}
		return false;
	}
	/* We should call ArcSetTime() to correct the raid-subsystem time
		Call ArcGetReqEventPage() to get the required event page from raid subsystem.
		There are 4 event pages reside in the raid subsystem and each contains 16 event objects.
	*/
	memset(pEventObject, 0, sizeof(sEVENT_DATA));
	stat = ctrl->ArcGetReqEventPage(nEventFlag, &nCount);
	if (stat == ARC_SUCCESS) 
	{
		if (nCount != 0) 
		{
			for ( i = nCount - 1; i >= 0; i--) 
			{
				// Call ArcGetEventObject function to extract the individual event object from event page
				if (ctrl->ArcGetEventObject( i, pEventObject )) 
				{
                    ::memset(eventStrBuf, 0, MAX_EVENT_BUFF_SIZE);
					eventCategory = (int)pEventObject->evtCategory;
					eventId = pEventObject->evtType;

                    *((LONG *)evtTime) = pEventObject->evtTime;
					// Parse the event object we got
						switch ((int)pEventObject->evtCategory) 
						{
							/*
							  The member "evtStr" of event object structure contains the string to indicate
							  which raidset owns this event object 
							  */
							case EVENT_RAIDSET:     // raidset event
								nCurEvtLevel = pEventObject->evtType;
								memcpy(tmpStr, pEventObject->evtStr, 16);
								if ( nCurEvtLevel > RS_EVT_NO_EVENT )
								{
									strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
								}
								else
								{
									strcpy( (char *)ptr1, htmRaidEvent[pEventObject->evtType]);
								}                                           
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s",1, tmpStr, ptr1);
								gotEvent = true;
								break;
								/*
								   The member "evtStr" of event object structure contains the string to indicate
									which volumeset owns this event object
								*/
							case EVENT_VOLUMESET: // volumeset event
								nCurEvtLevel = pEventObject->evtType;
								memcpy(tmpStr, pEventObject->evtStr, 16);
								if ( nCurEvtLevel >= VS_EVT_TOTALS)
								{   
									strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
								}                                               
								else                                                                        
								{   
									strcpy( (char *)ptr1, htmVolEvent[pEventObject->evtType]);
								}
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s", 1, tmpStr, ptr1);
								gotEvent = true;
								break;

							case EVENT_DEVICE:  // device event
								nCurEvtLevel = pEventObject->evtType;
								if( sysInfo.gsiTargetType == TARGET_SAS )
								{
									memcpy(tmpStr, pEventObject->evtStr, 16);
								}
								else
								{
									sprintf(tmpStr, "IDE Channel #%2d", pEventObject->evtChannel + 1);
								}

								if ( nCurEvtLevel >= DEV_EVT_TOTALS)
								{
									strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
								}
								else
								{
									strcpy( (char *)ptr1, htmDevEvent[pEventObject->evtType]);
								}
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s", 1
										, tmpStr, ptr1 );
								gotEvent = true;
								break;

							case EVENT_HOST:    // host event
								//this is Host SCSI events 
								nCurEvtLevel = pEventObject->evtType;
								if ( sysInfo.gsiScsiHostChannels)
								{
									if ( nCurEvtLevel >= SCSI_EVT_TOTALS )
									{
										strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
									}                             
									else
									{
										strcpy( (char *)ptr1, htmScsiHostEvent[pEventObject->evtType]);
									}
								} else {
									if ( nCurEvtLevel >= SCSI_EVT_TOTALS )
									{   
										strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
									}                                                                   
									else
									{   
										strcpy( (char *)ptr1, htmIdeHostEvent[pEventObject->evtType]);
									}   
								}

								// CHG 2020-10-05 huibong FreeBSD 12.x 컴파일시 발생하는 warning 코드 수정 (#33225)
								// - FreeBSD 12.x 의 gcc 9.3 컴파일시 sprintf 사용 관련 buffer size 문제로 warning 발생
								// - flow 상으로는 buffer overflow 발생 가능성 없음
								// - 하지만.. 컴파일시마다 warning 발생하므로.. 수정 결정
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s", 1, eventCat[pEventObject->evtCategory], ptr1 );

								gotEvent = true;
								break;

							case EVENT_HW_MONITOR:  // hardware monitor event
								nCurEvtLevel = pEventObject->evtType;
								memcpy(tmpStr, eventCat[pEventObject->evtCategory], 16);
								if ( nCurEvtLevel >= HW_EVT_TOTALS )
								{
									strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
								}
								else
								{
									strcpy( (char *)ptr1, htmHwMonEvent[pEventObject->evtType]);
								}
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s", 1
										, tmpStr, ptr1 );
								gotEvent = true;
								break;

							case EVENT_NEW_83782D:
								memcpy(tmpStr, pEventObject->evtStr, 16);
								if ( pEventObject->evtType >= GHM_TOTALS )
								{
									strcpy( (char *)ptr1, UNKNOWN_EVT_STR);
								}
								else
								{
									strcpy( (char *)ptr1, htmNewEventStr[pEventObject->evtType]);
								}
								sprintf((char *)eventStrBuf, "Controller#%d: %s %s",1
										, tmpStr, ptr1 );
								gotEvent = true;
								break;
							case EVENT_NO_EVENT: 
								// 이벤트가 없는 경우
								gotEvent = true;
							default:								
								continue;
						}// end switch

				}// end if

				// 모든 이벤트를 결과 list에 추가필요 수정 2015-06-26
				CEventData objEvent;

				objEvent.m_evtTime = GetTime_t(evtTime);
				objEvent.m_szEventData = eventStrBuf; 
				objEvent.SetEventType( eventCategory, eventId );

				listRet.push_back(objEvent);


			}// end for
		} else {
			_LOG(LWAR, "Event empty.");
			gotEvent = true;
		}
	}// end if


	if (pEventObject) {
		delete pEventObject;
	}

	if (evtTime) {
		delete evtTime;
	}
	pthread_mutex_unlock(&mutex_lock);
	return gotEvent;
}

int CArecaWrapper::DiscoveryDevice(void)
{
	dev_t dev;
	char buf[64];
	mode_t mode = 0666;
	unsigned int nTotalCtrl = 0;
	ARC_STATUS      stat = ARC_SUCCESS;
	sSYSTEM_INFO    sysInfo;

	mode |= S_IFCHR;

	for (int i = 0; i < 4; i++) {
		memset(buf, 0, sizeof(buf));
		sprintf(buf,"/dev/arcmsr%d", i);
		unlink(buf);
	}

	for (int mymajor = 255; mymajor > 161; mymajor--) 
	{
		for (int myminor = 0; myminor < 4; myminor++) 
		{
			LinuxIoctlInterface *out = new LinuxIoctlInterface();
			memset(buf, 0, sizeof(buf));
			sprintf(buf,"/dev/arcmsr%d", nTotalCtrl);
			dev = makedev(mymajor, myminor);
			mknod(buf, mode, dev);
			if (out->init(nTotalCtrl)) {
				stat = m_Arc[nTotalCtrl++].ArcInitSession(out);
				if (stat != ARC_SUCCESS) {
					_LOG(LERR, "ArcInitSession Failed : %d",stat);
					if( out )
					{
						delete out;
						out = NULL;
					}
					unlink(buf);
					return 0;
				}
			} else {
				delete out;
				out = NULL;
				unlink(buf);
			}
		}
		if (nTotalCtrl) {
			break;
		}
	}
    
	return nTotalCtrl;
}

int CArecaWrapper::InitArecaControllers(void)
{
	int nCount = 0;
	int nInterfaceIndex = 0;
	m_nTotalCtrl = DiscoveryDevice();
    _LOG(LDBG, "Total Count: %d", m_nTotalCtrl);

	if(m_nTotalCtrl == 0)
	{
		return -1;
	} 

	for (nInterfaceIndex =0; nInterfaceIndex < m_nTotalCtrl; nInterfaceIndex++)
	{
		m_Arc[nInterfaceIndex].ArcGetReqEventPage(FLAG_ALL, &nCount);
	}

	return m_nTotalCtrl;
}
