
//ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
//	FILE	: Process.cpp
//	AUTHOR	: Catalin Cocos
//__________________________________________________________________________________________________

#include "stdinc.h"
#include "process.h"
#include "app.h"
#include "Tree.h"
#include "resource.h"

BOOL volatile Stop;

HANDLE hProcessSemaphore, hStopSemaphore;
HANDLE hLogFile = INVALID_HANDLE_VALUE;


void SetTxtProp( COLORREF clr/* = RGB(0, 255, 0)*/, DWORD effects /*= 0*/)
{
	CHARFORMAT cf= { sizeof(CHARFORMAT) };
	cf.dwMask = CFM_BOLD|CFM_FACE|CFM_COLOR|CFM_SIZE;
	cf.dwEffects = effects;
	cf.yHeight = -10;
	cf.crTextColor = clr;
	cf.bCharSet = DEFAULT_CHARSET;
	cf.bPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
	strcpy(cf.szFaceName, "Arial");
	SendMessage(hWOut, EM_SETCHARFORMAT, SCF_SELECTION , (LPARAM) &cf );			// set the character format
}

void AddTxt( const char* text)
{
	CHARRANGE cr = { -1, -1};
	SendMessage(hWOut,EM_EXSETSEL, 0, (LPARAM) &cr );
	SendMessage(hWOut,EM_REPLACESEL, FALSE, (LPARAM) text);
	if(hLogFile != INVALID_HANDLE_VALUE)
	{
		DWORD bw;
		WriteFile(hLogFile, text, strlen(text), &bw, NULL );
	}
}


void AddItemChildren(CSTPArray<CDirInfo>* pData, HTREEITEM hChild)
{
	TV_ITEM ti = { TVIF_HANDLE|TVIF_STATE|TVIF_PARAM, hChild, 0, ~0 };
	TreeView_GetItem( hWTree, &ti);
	ti.state &= TVIS_STATEIMAGEMASK;
	CDirInfo* pDi = ((CDirInfo*)ti.lParam);
	if(pDi->GetSize() ||
	  ti.state == TI_STATE_FILES ||
	  ti.state == TI_STATE_RECURSE)
	{
		// add this item
		CDirInfo* pNewD = new CDirInfo((char*)pDi->FileName());
		if(ti.state == TI_STATE_FILES) pNewD->m_Flags = CDIR_ALL;
		if(ti.state == TI_STATE_RECURSE) pNewD->m_Flags = CDIR_RECURSE;
		if(!pNewD->m_Flags)
		{
			// add the file list
			pNewD->SetSize(pDi->GetSize());
			for(int i = pNewD->GetUpperBound(); i>=0; i-- )
				pNewD->SetAt(i, new CFileName( (char*)(*pDi)[i]->FileName() ));
		}
		BOOL NA;
		pData->SAdd(pNewD, NA, FALSE);
	}
	for( HTREEITEM hNext = TreeView_GetChild( hWTree, hChild); hNext; hNext = TreeView_GetNextSibling( hWTree, hNext))
		AddItemChildren( pData, hNext);
}

void AddFile( CSTPArray<CDirInfo>* pData, char* filename )
{
	if(!CnvEng.IsFileSupported(filename))
		return;
	char Dir[_MAX_PATH];
	char Fname[_MAX_FNAME];
	_splitpath(filename, Dir, NULL, Fname, NULL);
	_splitpath(filename, NULL, Dir+strlen(Dir), NULL, Fname+strlen(Fname));
	Dir[strlen(Dir) - 1] = 0;
	CDirInfo* pDi = new CDirInfo(Dir);
	int idx, ins;
	idx = pData->Index( pDi, &ins);
	if(idx>=0)
	{
		delete pDi; // we have a match
		if((*pData)[idx]->m_Flags) return;
		BOOL NA;
		CFileName * pName = new CFileName(Fname);
		(*pData)[idx]->SAdd(pName, NA, FALSE);
		if(NA) delete pName;
		return;
	}
	if(ins>0 &&	strlen((*pData)[ins-1]->FileName()) <= strlen(Dir) &&
		!_strnicmp((*pData)[ins-1]->FileName(), Dir, strlen((*pData)[ins-1]->FileName())) &&
		(*pData)[ins-1]->m_Flags == CDIR_RECURSE)
	{
		delete pDi; // this is a sub-folder of a recursive folder, we need no info on it
		return;
	}
	// we need to add the folder
	pDi->Add(new CFileName(Fname));	// add its first file...
	pData->InsertAt(ins, pDi, 1); // and add it in the list
}

void AddTextCollection( CSTPArray<CDirInfo>* pData, char* filename )
{
	CTextParser P;								// the file parser object
	if(!P.AccessFile(filename))					// try to acces the txt file
		return;
	P.SetType( "\\._-", CTP_TEXT);
	P.SetType( "^", CTP_LINE_COMMENT);
	char Dir[_MAX_PATH];
	_splitpath(filename, Dir, NULL, NULL, NULL);
	_splitpath(filename, NULL, Dir+strlen(Dir), NULL, NULL);
	Dir[strlen(Dir) - 1] = 0;
	char* bksl = strrchr( Dir, '\\');
	if(bksl) bksl[1] = 0;
	else *Dir = 0;
	char* Word = NULL, *NextWord;
	DWORD i = P.Advance();
	while(!(i & CTP_EOF ))
	{
		Word = P.Word();
		char* Begin = strchr( Word, ')');
		if(Begin) Word = Begin+1;
		char FullName[_MAX_PATH+_MAX_FNAME];
		strcpy( FullName, Dir);
		strcat( FullName, Word );
		AddFile( pData, FullName);
		do
		{
			i = P.Advance();
			NextWord = P.Word();
			if(Begin = strchr( NextWord, ')')) NextWord = Begin+1;
		}
		while( !(i & CTP_EOF ) && !strcmpi(Word, NextWord)); 		
	}
}

void AddLogCollection( CSTPArray<CDirInfo>* pData, char* filename )
{
	CTextParser P;								// the file parser object
	if(!P.AccessFile(filename))					// try to acces the log file
		return;
	P.SetType( "\\._-", CTP_TEXT);
	P.SetType( "*", CTP_LINE_COMMENT);
	DWORD i = P.Advance();
	while(!(i & CTP_EOF ))
	{
		if(!strcmp(P.Word(), "Obtaining"))
		{
			i = P.Advance();
			if( !(i & CTP_EOF ) )
			AddFile( pData, P.Word());
		}
		i = P.Advance();
	}
}


unsigned long Lines, Files, Directories, TotalFiles, Errors;
float Length, Output;
SYSTEMTIME systime;
BOOL DirectoryNotDisplayed;
CDirInfo* pCrtDir;

void ProcessFile( char* szFile)
{
	char OutputName[_MAX_PATH +10];
	strcpy(OutputName, szFile);
	strcat(OutputName, BINEXTENSION);

	switch(OperatingMode)
	{
	case ID_MODE_CLEAN:
		{
			char Ext[_MAX_EXT];
			char fName[_MAX_FNAME];
			_splitpath(szFile, NULL, NULL, fName, Ext );
			if(!strcmp (Ext, BINEXTENSION) && (!CnvEng.IsFilteringActive() || CnvEng.IsFileSupported( fName )))
			{
				if(DirectoryNotDisplayed)
				{
					AddTxt("\r\n");//Processing ");
					AddTxt( pCrtDir->FileName());
					AddTxt( " ...\r\n");
					DirectoryNotDisplayed = FALSE;
				}
				Files ++;
				AddTxt("\t");
				AddTxt(fName);
				AddTxt(Ext);
				if(!DeleteFile(szFile)) AddTxt(" Error: Deletion failed!");
				AddTxt("\r\n");
			}
		}
		break;
	case ID_MODE_NORMAL:
		{
			HANDLE hOut = CreateFile( OutputName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if(hOut != INVALID_HANDLE_VALUE)
			{
				HANDLE hIn = CreateFile( szFile, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
				if( hIn != INVALID_HANDLE_VALUE )
				{
					BY_HANDLE_FILE_INFORMATION iIn, iOut;
					GetFileInformationByHandle( hIn, &iIn);
					GetFileInformationByHandle( hOut, &iOut);
					CloseHandle(hIn);
					if(CompareFileTime(&iIn.ftLastWriteTime, &iOut.ftLastWriteTime) <= 0 )
					{
						// the file is up to date
						CloseHandle(hOut);
						break;
					}
				}
				CloseHandle(hOut);
			}
		}
	case ID_MODE_FORCED:
		{
			DWORD len, Outlen;
			unsigned line;
			char Ext[_MAX_EXT];
			char fName[_MAX_FNAME];
			_splitpath(szFile, NULL, NULL, fName, Ext );
			
			if(DirectoryNotDisplayed)
			{
				AddTxt("\r\n");//Processing ");
				AddTxt( pCrtDir->FileName());
				AddTxt( " ...\r\n");
				DirectoryNotDisplayed = FALSE;
			}
			
			AddTxt("\t");
			AddTxt(fName);
			AddTxt(Ext);
			Files ++;

			SetTxtProp( RGB(255, 0, 0), CFE_BOLD );
			int TheERROR = CnvEng.Convert( szFile, OutputName, &line, &len, &Outlen );
			switch(TheERROR )
			{
			case SB_ERR_OUTPUT_FILEOPEN:  AddTxt("  Error! Unable to create the output file.");	break;
			case SB_ERR_FILEOPEN: AddTxt("  Error! Unable to open the file.");	break;
			case SB_ERR_BAD_FILE_EXTENSION:	AddTxt("  Error! This file type is not currently supported.");	break;
			case SB_ERR_PARAMETER_MISSING:
				AddTxt("  Error (line ");
				itoa( line, OutputName, 10);
				AddTxt( OutputName );
				AddTxt( ")! Parameter Missing.");
				break;
			case SB_ERR_UNKNOWN_DIRECTIVE:
				AddTxt("  Error (line ");
				itoa( line, OutputName, 10);
				AddTxt( OutputName );
				AddTxt( ")! Unknown Directive.");
				break;
			case SB_ERR_UNKNOWN_SECTION:
				AddTxt("  Error (line ");
				itoa( line, OutputName, 10);
				AddTxt( OutputName );
				AddTxt( ")! Unknown Section Type.");
				break;
			case SB_ERR_UNKNOWN_ENTRY:
				AddTxt("  Error (line ");
				itoa( line, OutputName, 10);
				AddTxt( OutputName );
				AddTxt( ")! Unknown Entry Type.");
				break;
			case SB_ERR_UNEXPECTED_EOF:
				AddTxt("  Error (line ");
				itoa( line, OutputName, 10);
				AddTxt( OutputName );
				AddTxt( ")! Unexpected End of File.");
				break;
			}
			SetTxtProp();
			AddTxt("\r\n");
			Lines += line;
			Length += ((float)len)/1024/1024;
			Output += ((float)Outlen)/1024/1024;
			if(TheERROR)
				Errors++;
		}
		break;
	}
}

void ProcessDirectory( CDirInfo* pDi )
{
	char buffer[_MAX_PATH];

	DirectoryNotDisplayed = TRUE;
	pCrtDir = pDi;

//	AddTxt("\r\n");//Processing ");
//	AddTxt( pDi->FileName());
//	AddTxt( " ...\r\n");

	Directories ++;
	if(pDi->m_Flags)
	{
		CTypedPtrArray<CDirInfo*> arRecFolders;
		arRecFolders.SetGranularity(5);
		
		strcpy(buffer, pDi->FileName());
		strcat(buffer, "\\*.*");
		WIN32_FIND_DATA fd;
		HANDLE hSearch = FindFirstFile( buffer, &fd );
		for(BOOL Found = (hSearch != INVALID_HANDLE_VALUE);	Found && !Stop;	Found = FindNextFile( hSearch, &fd))
		{
			if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				//we found a sub-directory
				if(pDi->m_Flags == CDIR_RECURSE && *fd.cFileName != '.')
				{
					strcpy( buffer, pDi->FileName());
					strcat( buffer, "\\");
					strcat( buffer, fd.cFileName);
					//create the new structure
					CDirInfo* pNewDir = new CDirInfo( buffer );
					pNewDir->m_Flags = CDIR_RECURSE;
					arRecFolders.Add(pNewDir);
				}
			}
			else
			{
				//we found a file
				TotalFiles++;
				if(OperatingMode == ID_MODE_CLEAN || CnvEng.IsFileSupported(fd.cFileName))
				{
					strcpy( buffer, pDi->FileName());
					strcat( buffer, "\\");
					strcat( buffer, fd.cFileName);
					ProcessFile( buffer ); // convert the file
				}
			}
			if(WaitForSingleObject(hStopSemaphore, 0) == WAIT_OBJECT_0) Stop = TRUE;
		}
		if(hSearch != INVALID_HANDLE_VALUE) FindClose(hSearch);
		for( int i = 0, k = arRecFolders.GetSize(); i<k && !Stop; i++) // process the subdirectories
		{
			ProcessDirectory(arRecFolders[i]);
			if(WaitForSingleObject(hStopSemaphore, 0) == WAIT_OBJECT_0) Stop = TRUE;
		}
		arRecFolders.DeleteAll();
	}
	else for(int i = 0, k = pDi->GetSize(); i<k && !Stop; i++)
	{
		TotalFiles++;
		//process the selected files
		strcpy( buffer, pDi->FileName());
		strcat( buffer, "\\");
		strcat( buffer, (*pDi)[i]->FileName());
		if(OperatingMode == ID_MODE_CLEAN)
			strcat( buffer,BINEXTENSION );
		ProcessFile( buffer ); // convert the file
		if(WaitForSingleObject(hStopSemaphore, 0) == WAIT_OBJECT_0) Stop = TRUE;
	}
}

void __cdecl ProcessThread(void* pParam)
{
	char buffer[_MAX_PATH];
	strcpy(buffer, CrtCfg);
	char* ext = strrchr(buffer, '.');
	if(ext) strcpy(ext, ".log");
	else strcat(buffer, ".log");
	hLogFile = CreateFile( buffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	SetWindowText(hWOut, "");
	AddTxt("------------  Configuration: ");
	AddTxt( CrtCfg );
	AddTxt("  ------------\r\n");

	Output = Length = 0.f;
	Errors = TotalFiles = Lines = Files = Directories = 0;
	GetSystemTime( &systime );

	CSTPArray<CDirInfo>* pData = (CSTPArray<CDirInfo>*) pParam;
	//--------------------------------------------------------
	for(int i = 0, k = pData->GetSize(); i<k && !Stop; i++)
	{
		ProcessDirectory( (*pData)[i] );
		if(WaitForSingleObject(hStopSemaphore, 0) == WAIT_OBJECT_0) Stop = TRUE;
	}
	//--------------------------------------------------------

	pData->DeleteAll();
	delete pData;
	// compute the elapsed time
	SYSTEMTIME finalTime;
	GetSystemTime( &finalTime );

	AddTxt("\r\n");
	if(Stop) AddTxt( "Processing canceled by user.\r\n" );
	AddTxt("--------------------------------------\r\n");
	AddTxt("Processing Mode:\t");
	switch(OperatingMode)
	{
	case ID_MODE_NORMAL: AddTxt(" NORMAL\r\n"); break;
	case ID_MODE_FORCED: AddTxt(" FORCED\r\n"); break;
	case ID_MODE_CLEAN: AddTxt(" CLEAN-UP\r\n"); break;
	}
	sprintf( buffer,"Total Directories :\t%u\r\n", Directories);
	AddTxt( buffer );
	sprintf( buffer,"Total Files :\t\t%u\r\n", TotalFiles);
	AddTxt( buffer );
	sprintf( buffer,"Processed Files :\t%u\r\n",Files);
	AddTxt( buffer );
	sprintf( buffer,"Processed Lines :\t%u\r\n",Lines);
	AddTxt( buffer );
	sprintf( buffer,"Read Data (MB) :\t%.3f\r\n",Length);
	AddTxt( buffer );
	sprintf( buffer,"Output Data (MB) :\t%.3f\r\n",Output);
	AddTxt( buffer );
	int hour = finalTime.wHour - systime.wHour;
	int minute = finalTime.wMinute - systime.wMinute + hour*60;
	int second = finalTime.wSecond - systime.wSecond;
	if( second<0)
	{
		minute--;
		second+=60;
	}
	sprintf( buffer,"Processing Time :\t%i:%02i\r\n",minute,second);
	AddTxt( buffer );
	AddTxt("--------------------------------------\r\n");
	if(Errors)
	{
		SetTxtProp( RGB(255, 0, 0), CFE_BOLD );
		sprintf( buffer,"\t%u Error%s.\r\n",Errors, Errors>1?"s":"");
		AddTxt( buffer );
		SetTxtProp();
	AddTxt("--------------------------------------\r\n");
	}
	if(hLogFile != INVALID_HANDLE_VALUE) CloseHandle(hLogFile);
	PlaySound( "SystemAsterisk", NULL, SND_ALIAS|SND_NOWAIT|SND_ASYNC);
	Processing = FALSE;
	ReleaseSemaphore( hProcessSemaphore, 1, NULL);
	OnIdle();
}

void Process( int What )
{
	// initializing data...
	HCURSOR hOldCursor = SetCursor( LoadCursor(NULL, IDC_WAIT));
	SetWindowText( hWStatusbar, "Initializing, please wait...");
	
	CSTPArray<CDirInfo>* pData = new CSTPArray<CDirInfo>;
	pData->SetGranularity(20);

	if(What & PROCESS_SELECTION)
		// set the tree selection
		for(HTREEITEM hChild = TreeView_GetRoot(hWTree); hChild; hChild = TreeView_GetNextSibling(hWTree, hChild))
			AddItemChildren( pData, hChild);
	if(What & PROCESS_COLLECTIONS)
		for( int i = ListView_GetItemCount( hWList ) -1 ; i>=0; i-- )
		{
			LV_ITEM it = {LVIF_STATE|LVIF_PARAM, i, 0, 0, ~0};
			ListView_GetItem( hWList, &it);
			if(it.state & LSTATE_CHECKED)
			{
				char Ext[_MAX_EXT];
				_splitpath((char*)it.lParam, NULL, NULL, NULL, Ext);
				if(!strcmpi(Ext+1, "txt"))
					AddTextCollection( pData, (char*)it.lParam);
				else if(!strcmpi(Ext+1, "log"))
					AddLogCollection( pData, (char*)it.lParam);
			}
		}

	SetCursor( hOldCursor );
	SetWindowText( hWStatusbar, "Ready");

	Processing = TRUE;
	Stop = FALSE;
	WaitForSingleObject(hStopSemaphore, 0);
	WaitForSingleObject(hProcessSemaphore, 0); // reset the semaphores state
	OnIdle();
	_beginthread(ProcessThread, 0, pData); // launch the processing
//	SetFocus( hWOut );
}

void StopProcessing()
{
	if(!Processing) return;
	ReleaseSemaphore( hStopSemaphore, 1, NULL);
}

