/////////////////////////////////////////////////////////////////////
//
// MODULE: SERVCHAN.CPP
//
// PURPOSE: Provides the implementation of the 
//          CStatChanRecordProcessor class 
//          These methods parse and collection schedule and channel
//          data that is then stored into the GuideStore using the
//          gsServices and gsChannels methods.
//
/////////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "errcodes.h"
#include <Afxtempl.h>
#include <crtdbg.h>
#include <fstream.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
#include "guidestore.h"
#include "services.h"
#include "channels.h"
#include "programs.h"
#include "schedules.h"
#include "wtmsload.h"
#include "tmsparse.h"
#include "servchan.h"
#define TIMING 0
#include "..\..\timing.h"

#ifdef _DUMP_LOADER
CString csDiagMil;
extern HANDLE hDiagFile;
extern char szDiagBuff[1024];
int         ChannelCnt = 0;
int         TimeSlotCnt = 0;
extern DWORD dwBytesWritten;
#endif


SDataFileFields ssfStatChanFieldInfo[StatChanRec_Fields] = 
{ 
  TRUE, 10,
  TRUE, 40,
  TRUE, 5,
  TRUE, 10,
  TRUE, 10
};



// CStatChanRecordProcessor
//
CStatChanRecordProcessor::CStatChanRecordProcessor(int nNumFields, 
												 SDataFileFields ssfFieldsInfo[])
{
	m_Init();
	m_pFieldsDesc = ssfFieldsInfo;
	m_nNumFields  = nNumFields;
	m_ChannelIDMap.InitHashTable(MAX_CHANNELS, TRUE);
}

CStatChanRecordProcessor::CStatChanRecordProcessor()
{
    m_Init();
	m_pFieldsDesc = ssfStatChanFieldInfo;
    m_nNumFields  = StatChanRec_Fields;
	m_ChannelIDMap.InitHashTable(MAX_CHANNELS, TRUE);
}


/////////////////////////////////////////////////////////////////////////
//
//  METHOD: m_Process
//
//  PARAMETERS: [IN] lpData    - Data to be processed
//              [IN] nDataSize - Size of data
//
//  PURPOSE: Processes the Data stream for Channel records
//           Once Channel records have been parsed and
//           prepared for table updation signals end of channel records
//           to the file processor
//
/////////////////////////////////////////////////////////////////////////
//
int CStatChanRecordProcessor::m_Process(LPCTSTR lpData, int nDataSize, int* pBytesProcessed)
{
    int nFields = 0;
	int nFieldSize = 0;
	int nParsePos = 0;
	int nBytesProcessed = 0;

	// Parameter validation
	//
	if (NULL == lpData || 0 == nDataSize)
	{
        return ERROR_INVALID_ARGS;
	}
    else
	{
		 *pBytesProcessed = 0;

         // Data Integrity Check
         //
         ASSERT( _CrtIsValidPointer((const void *)lpData, nDataSize, FALSE) );

		 {
             // Data is valid - Start processing
			 //
             do
			 {
	 			 if (STATE_RECORD_INCOMPLETE == m_nState)
				 {
				     nFields = m_nCurrField;
					 nFieldSize = m_nCurrFieldSize;
				 }

                 for(;nParsePos < nDataSize; nParsePos++)
				 {
					 DeclarePerfTimer("CStatChanRecordProcessor");
					 MSG msg;
					 while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
					 {
						TranslateMessage(&msg);
						DispatchMessage(&msg);
					 }
					 PerfTimerReset();
                     if (lpData[nParsePos] == EOC)
					 {
						 // End of field
						 //
                         if (nParsePos > 0 && (lpData[nParsePos-1] != EOC))
						 {
							 // TODO ASSERT(nFields <= StationRec_Fields);
                             
							 m_szValidField[nFieldSize] = '\0';
					         m_cmRecordMap[nFields] =  m_szValidField;
							 nFieldSize = 0;
						 }
						 else
						 {
                             //nFieldDataPos = nParsePos;
							 nFieldSize = 0;
							 m_nCurrFieldSize = 0;
						 }

						 nFields++;        
						 m_nCurrField++;
	 				 }
                     else if (lpData[nParsePos] == EOR)
					 {
						 // End of the record
						 //
						 DeclarePerfTimer("CStatChanRecordProcessor");
						 PerfTimerReset();
						 if (UPDATE_SUCCEEDED != m_UpdateDatabase(m_cmRecordMap))
						 {
							 if (STATE_RECORD_ERROR == m_nState)
							 {
                                 m_nState = STATE_RECORDS_FAIL;
                                 return m_nState;
							 }
							 else
                                 m_nState = STATE_RECORD_ERROR;
						 }
						 PerfTimerDump("m_UpdateDatabase");

						 m_cmRecordMap.RemoveAll();
						 m_nCurrField = nFields = 0;
						 m_nCurrFieldSize = nFieldSize = 0;
					     continue;
	 				 }
                     else if (lpData[nParsePos] == EOT)
					 {
						 // End of the table
						 //
			             m_nState = STATE_RECORDS_PROCESSED;
						 nParsePos += 2*sizeof(TCHAR);
						 break;
	 				 }
					 else
					 {
						 // Valid field data
						 //
						 m_szValidField[nFieldSize] =  lpData[nParsePos];
                         nFieldSize++;
						 m_nCurrFieldSize++;
					 }
					 PerfTimerDump("for loop");
				 }

			 } while ((nParsePos < nDataSize) && (m_nState != STATE_RECORDS_PROCESSED));


			 *pBytesProcessed = nParsePos;

			 if (m_nState != STATE_RECORDS_PROCESSED)
			 {
				 if (lpData[--nParsePos] == EOR)
				 {
                     // More records
					 //
			         m_nState = STATE_RECORDS_INCOMPLETE;                      
				 }
				 else
				 {
                     // record incomplete
					 //
			         m_nState = STATE_RECORD_INCOMPLETE;                      					 
				 }
                  
			 }
		 }
         
	}
	
	return m_nState;

}


/////////////////////////////////////////////////////////////////////////
//
//  METHOD: m_UpdateDatabase
//
//  PARAMETERS: [IN] cmStationRecord  - map containing record fields
//
//  PURPOSE: Updates the Guide Store
//
/////////////////////////////////////////////////////////////////////////
//
int CStatChanRecordProcessor::m_UpdateDatabase(CRecordMap& cmRecord)
{
	int iRet = ERROR_UPDATE;
	CString csName, csCallLetters, csNetwork;
	CString csStationNumStr, csChannelNumStr;
	CWtmsloadApp* ctmsLoapApp = (CWtmsloadApp*) AfxGetApp();
	TCHAR   ConvBuffer[20] = {0};	
	IServicePtr pTMSService = NULL;
	IChannelPtr pTMSChannel = NULL;
	IChannelPtr pTMSChannelMatch = NULL;

	// Lookup the Name entry in the StationRecord Map
	//
    cmRecord.Lookup(StatChanRec_Name, csName);
    csName = csName.Left(ssfStatChanFieldInfo[StatChanRec_Name].nWidth);
	csName.TrimLeft();
	csName.TrimRight();

	// Lookup the Number entry in the StationRecord Map
	//
    cmRecord.Lookup(StatChanRec_Num, csStationNumStr);
	csStationNumStr.TrimLeft();
	csStationNumStr.TrimRight();

	// Lookup the Call Letters entry in the StationRecord Map
	//
	cmRecord.Lookup(StatChanRec_CallLetters, csCallLetters);
	csCallLetters = csCallLetters.Left(4);
	csCallLetters.MakeUpper();

	// Lookup the Network entry in the StationRecord Map
	//
	cmRecord.Lookup(StatChanRec_NetworkID, csNetwork);
	csNetwork.TrimLeft();
	csNetwork.TrimRight();

    // Lookup the Channel Number
	//
	cmRecord.Lookup(StatChanRec_ChannelNumber, csChannelNumStr);
	csChannelNumStr.TrimLeft();
	csChannelNumStr.TrimRight();

	// A new service will be added if the Service is for a Channel that does not already
	// have the "ServiceID" associated with it
	//
	pTMSChannelMatch = ctmsLoapApp->m_pgsChannels.FindChannelMatch(
		csChannelNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_ChannelNumber].nWidth),
		csStationNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_Num].nWidth));

	if (NULL != pTMSChannelMatch)
	{
			// Add the "channel + service" data for use by ScheduleEntries
			//
		    IServicePtr pServiceMatch = NULL;
            pServiceMatch = pTMSChannelMatch->GetService();

			m_ChannelIDMap[csChannelNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_ChannelNumber].nWidth)] 
				= _ltot(pServiceMatch->GetID(), ConvBuffer, 10);

			// Get the corresponding service ID
			//
			CString csChannelID(_ltot(pServiceMatch->GetID(), ConvBuffer, 10));

			// The hash table key is based on StationNum and ChannelNum
			//
			CString csStatChan = csStationNumStr + "," + csChannelNumStr;

			// Make the corresponding Hash table entry
			//
			m_ChannelIDMap.SetAt(csStatChan, csChannelID); // TODO: Add exception handling
	#ifdef _DUMP_LOADER
		wsprintf(szDiagBuff, "Saving #%d Channel ID for %s : %d\r\n", ChannelCnt, csStatChan, cct.ChannelID());
		if (NULL != hDiagFile)
		{
			::WriteFile(hDiagFile, szDiagBuff, lstrlen(szDiagBuff), &dwBytesWritten, 0);
		}
    #endif

		iRet = UPDATE_SUCCEEDED; 
	}
	else
	{
		ITuningSpace* pTuningSpace = ctmsLoapApp->m_pgsChannelLineup.GetTuningSpace();

		if (NULL != pTuningSpace)
		{
			ITuneRequest* pTuneRequest = NULL;
			
			// Create an empty tune request
			//
            pTuningSpace->CreateTuneRequest(&pTuneRequest);

			if (NULL != pTuneRequest)
			{
				IChannelTuneRequest*  pChannelTuneRequest = NULL;

				// Get a specialized tune request for Analog TV
				//
                pTuneRequest->QueryInterface(__uuidof(IChannelTuneRequest),
                        reinterpret_cast<void**>(&pChannelTuneRequest));

				if (NULL != pChannelTuneRequest)
				{
					// Set the Channel number
					//
					pChannelTuneRequest->put_Channel(_ttoi(csChannelNumStr));

					// Make the new service entry
					//
					pTMSService = ctmsLoapApp->m_pgsServices.AddService(pTuneRequest,
						 csCallLetters.GetBuffer(ssfStatChanFieldInfo[StatChanRec_CallLetters].nWidth),
						 csStationNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_Num].nWidth),
						 csName.GetBuffer(ssfStatChanFieldInfo[StatChanRec_Name].nWidth),
						 csNetwork.GetBuffer(ssfStatChanFieldInfo[StatChanRec_NetworkID].nWidth),
						 0,    
						 0);   

				}
			}
        }


	#ifdef _DUMP_LOADER
		ChannelCnt++;
	#endif

		long lChannelNumber = _ttol(csChannelNumStr);

		if (NULL != pTMSService)
		{
			// Add the channel
			//
			pTMSChannel = ctmsLoapApp->m_pgsChannels.AddChannel(pTMSService, 
					csStationNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_Num].nWidth),
					csChannelNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_ChannelNumber].nWidth), 
					lChannelNumber);
		}
		else
			return ERROR_UPDATE_ADD;

		if (NULL != pTMSChannel)
		{
			// Add the "channel + service" data for use by ScheduleEntries
			//
			m_ChannelIDMap[csChannelNumStr.GetBuffer(ssfStatChanFieldInfo[StatChanRec_ChannelNumber].nWidth)] = 
				_ltot(pTMSService->GetID(), ConvBuffer, 10);

			CString csChannelID(_ltot(pTMSService->GetID(), ConvBuffer, 10));
			CString csStatChan = csStationNumStr + "," + csChannelNumStr;

			// Make the corresponding Hash table entry
			//
			m_ChannelIDMap.SetAt(csStatChan, csChannelID);
	#ifdef _DUMP_LOADER
			wsprintf(szDiagBuff, "Saving #%d Channel ID for %s : %d\r\n", ChannelCnt, csStatChan, cct.ChannelID());
			if (NULL != hDiagFile)
			{
				::WriteFile(hDiagFile, szDiagBuff, lstrlen(szDiagBuff), &dwBytesWritten, 0);
			}
    #endif

			iRet = UPDATE_SUCCEEDED; 
		}
		else
			return ERROR_UPDATE_ADD;
	}

	return iRet; 
}

long CStatChanRecordProcessor::m_GetChannelID(LPCTSTR lpChannelNum)
{
	CString csChannelID;

	m_ChannelIDMap.Lookup(lpChannelNum, csChannelID);
    return _ttol(csChannelID);
}

int CStatChanRecordProcessor::m_GetState()
{
	return m_nState;
}

int CStatChanRecordProcessor::m_GetError()
{
	return m_nError;
}

int CStatChanRecordProcessor::m_GetErrorString(CString& csErrStr)
{
	if (m_nError)
	{
		// If there is an error to return 
		//
        return csErrStr.LoadString(m_nError);

	}

	return FALSE;
}