//ParseSection.cpp

#include "stdafx.h"
#include "ParseSection.hpp"



//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : CParseSection 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
CParseSection::CParseSection (CString csFinalMDFFileName/* = ""*/)
{
	m_csFinalMDFFileName = csFinalMDFFileName;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : ParseFile 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::ParseFile (CString csInitialMDFFileName, CString csFinalMDFFileName/* = ""*/)
{
	//the final MDF file name can be passed in constructor or here
	if (!csFinalMDFFileName.IsEmpty ())
		m_csFinalMDFFileName = csFinalMDFFileName;

	CFileStatus status;

	BOOL bInitialMDFFileExists = CFile::GetStatus (csInitialMDFFileName, status);

	if (!bInitialMDFFileExists) //the file exists
	{
		AfxMessageBox ("Error, can't open the initial MDF file");
		return 0;
	}
	else //normal behavior
	{
		if (FillListOfInitialMDFSections (csInitialMDFFileName))
		{
			CreateTheFinalMDFFile ();
			//copy the unchanged sections
			while (!m_lstOfInitialUnchangeableMDFSections.IsEmpty ())
			{
				CString csSectionName = m_lstOfInitialUnchangeableMDFSections.RemoveHead ();
				CopySectionAsItIs (csInitialMDFFileName, csSectionName);
			}
			//copy the normal sections
			while (!m_lstOfInitialMDFSections.IsEmpty ())
			{
				CString csSectionName = m_lstOfInitialMDFSections.RemoveHead ();
				CopySectionWithAnalyze (csInitialMDFFileName, csSectionName);
			}
		}
		else
			ASSERT (FALSE);
	}

	return 1;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : CopySection 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::CopySection (CString csFileName, CString csSectionName, BOOL bCopyAsItIs/* = FALSE*/)
{
	//if section is already included skip it
	if (IsSectionIncluded (csSectionName))
		return 1;

	if (!IsFileNameAbsolute (csFileName))
		//the absolute path is computed using different roots
		//we assume here that the relative path fit only one root(we can't compute two or more valid paths 
		//using the same relative path and different roots)
		csFileName = ComputeAbsoluteFileName (csFileName);
	
	if (csFileName.IsEmpty ())
		return 0;

	CStringList lstSection;

	if (!ReadSection (csFileName, csSectionName, lstSection))
		return 0;

	if (!bCopyAsItIs)
		AnalyseSection (csFileName, lstSection);

	WriteSection (lstSection);

	return 1;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : ReadSection 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::ReadSection (CString csFileName, CString csSectionName, CStringList &rSection)
{
	ASSERT (!csFileName.IsEmpty ());
	UINT uOpenFlags = CFile::modeRead | CFile::typeText;
	CStdioFile file;

	if (!file.Open (csFileName, uOpenFlags))
		return 0;

	BOOL bReadOK = 0;
	//read every line and when you find the section copy it in the rSection
	CString csEntry;
	while (file.ReadString (csEntry)) //not eof
	{
		csEntry.TrimLeft ();
		csEntry.TrimRight ();
		if (0 < csEntry.GetLength ()) //not a blank line
		{
			//find a section
			BOOL bBeginOfSection = cBeginBracket == csEntry [0];
			if (bBeginOfSection)
			{
				int iOpenedBracketCount = 1;
				//remove the bracket
				CString csRedSectionName = csEntry.Mid (1);
				csRedSectionName = RemoveParameters (csRedSectionName);
				BOOL bSectionMatch = csRedSectionName == csSectionName;
				if (bSectionMatch)
				{
					ASSERT (rSection.IsEmpty ());
					rSection.AddTail (csEntry);
				}

				//read the whole section
				do{
					file.ReadString (csEntry);
					if (bSectionMatch)
						rSection.AddTail (csEntry);

					csEntry.TrimLeft ();
					csEntry.TrimRight ();
					if (csEntry.IsEmpty ())
						continue;

					//end bracket
					if (cEndBracket == csEntry [0]) iOpenedBracketCount--;
					//begin bracket
					if (cBeginBracket == csEntry [0]) iOpenedBracketCount++;

				}while (0 < iOpenedBracketCount);

				if (bSectionMatch)
				{
					//store this as a treated section in order not to include this the second time
					m_lstOfIncludedExternalReferences.AddTail (csSectionName);
					bReadOK = 1;
					break; //no need to read more
				}
			}
			//else go to next line
		}
		
	}
	
	file.Close ();
	if (!bReadOK)
	{
		CString csWarningMsg = "Can't find the section: " + csSectionName + " in the file: " + csFileName;
		AfxMessageBox (csWarningMsg);
	}
	return bReadOK;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : AnalyseSection 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::AnalyseSection (CString csFileName, CStringList &rSection)
{
	ASSERT (!rSection.IsEmpty ());
	//look for external references
	POSITION pos = rSection.GetHeadPosition ();
	while (NULL != pos)
	{
		POSITION oldPos = pos;
		//check every entry(line) in the section
		CString csEntry = rSection.GetNext (pos);
		int iInx = csEntry.Find (cExternalReferencesIndicator);
		BOOL bExternalReferences = -1 != iInx;
		if (bExternalReferences) // external references
		{
			//assume there is only one referenced section in the entry
			ASSERT (0 < iInx); //always there is something at the left
			int iBeginStringInx = csEntry.Find (cStringDelimitator);
			ASSERT (-1 != iBeginStringInx);
			//copy the keyword
			CString csEntryStartPart = csEntry.Left (iBeginStringInx);//don't include the delimitator
			CString csTextToAnalyze = csEntry.Mid (iBeginStringInx);
			while (!csTextToAnalyze.IsEmpty ())
			{
				int iFirstDelimitInx = csTextToAnalyze.Find (cStringDelimitator);
				BOOL bStringLeftForAnalyze = -1 != iFirstDelimitInx;
				if (!bStringLeftForAnalyze)
				{
					csEntryStartPart += csTextToAnalyze;
					break; //that's it
				}

				CString csLeftTrail = csTextToAnalyze.Left (iFirstDelimitInx + 1); //include the delimitator
				csTextToAnalyze = csTextToAnalyze.Mid (iFirstDelimitInx + 1);
				int iSubstringInx = csTextToAnalyze.Find (cStringDelimitator);
				ASSERT (-1 != iSubstringInx); //get the pair "
				CString csSubstring = csTextToAnalyze.Left (iSubstringInx); //don't include the delimitator
																			//but keep count of it
				csTextToAnalyze = csTextToAnalyze.Mid (iSubstringInx + 1); //get the right part, that will be analyzed next time
				int iSplitInx = csSubstring.Find (cExternalReferencesIndicator);
				BOOL bExternalReferencesFound = -1 != iSplitInx;
				if (!bExternalReferencesFound)
				{
					csEntryStartPart += csLeftTrail + csSubstring + cStringDelimitator;
					continue;
				}
				//if ext reference found

				CString csSectionsFileName = csSubstring.Left (iSplitInx);
				BOOL bReferenceInThisFile = cThisFileReference == csSectionsFileName;
				if (bReferenceInThisFile)
					csSectionsFileName = csFileName;

				CString csSectionName = csSubstring.Mid (iSplitInx + 1);
				if (!IsSectionIncluded (csSectionName))
					CopySectionWithAnalyze (csSectionsFileName, csSectionName);

				//recompute the entry and replace the file name by *
				CString csFullSectionName;
				if (bReferenceInThisFile)
				{
					csFullSectionName = csLeftTrail + csSubstring + cStringDelimitator;
				}
				else
				{
					csFullSectionName = csLeftTrail + cThisFileReference + cExternalReferencesIndicator 
										+ csSectionName + cStringDelimitator;
				}

				csEntryStartPart += csFullSectionName;
			}
			rSection.SetAt (oldPos, csEntryStartPart);
		}
	}

	return 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : WriteSection 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::WriteSection (CStringList &rSection)
{
	ASSERT (!rSection.IsEmpty ());
	ASSERT (!m_csFinalMDFFileName.IsEmpty ());
	CStdioFile file;
	//assume the file exists
	UINT uOpenFlags = CFile::modeWrite | CFile::typeText;
	if (!file.Open (m_csFinalMDFFileName, uOpenFlags))
		return 0;

	file.SeekToEnd ();
	while (!rSection.IsEmpty ())
	{
		CString csEntry = rSection.RemoveHead ();
		if (!csEntry.IsEmpty())
		{
			BOOL bOrdinaryEntryInSection = cBeginBracket != csEntry [0] || cEndBracket != csEntry [0];
			if (bOrdinaryEntryInSection)
				csEntry = "\t" + csEntry + "\n";
			else //begin and end section
				csEntry = csEntry + "\n";

			file.WriteString (csEntry);
		}
	}

	file.WriteString ("\n\n");
	file.Close ();
	return 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : FillListOfInitialMDFSections 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::FillListOfInitialMDFSections (CString csInitialMDFFileName)
{
	ASSERT (!csInitialMDFFileName.IsEmpty ());
	UINT uOpenFlags = CFile::modeRead | CFile::typeText;
	CStdioFile file;

	if (!file.Open (csInitialMDFFileName, uOpenFlags))
		return 0;

	//empty the lists before a new fill
	m_lstOfInitialUnchangeableMDFSections.RemoveAll ();
	m_lstOfInitialMDFSections.RemoveAll ();
	//read every line and when you find the section copy it in the rSection
	CString csEntry;
	while (file.ReadString (csEntry)) //not eof
	{
		csEntry.TrimLeft ();
		if (0 < csEntry.GetLength ()) //not a blank line
		{
			//find a section
			BOOL bBeginOfSection = cBeginBracket == csEntry [0];
			if (bBeginOfSection)
			{
				int iOpenedBracketCount = 1;
				//remove the bracket
				CString csRedSectionName = csEntry.Mid (1);
				csRedSectionName.TrimLeft ();
				csRedSectionName.TrimRight ();
				//read the whole section
				do{
					file.ReadString (csEntry);
					csEntry.TrimLeft ();
					//end bracket
					if (cEndBracket == csEntry [0]) iOpenedBracketCount--;
					//begin bracket
					if (cBeginBracket == csEntry [0]) iOpenedBracketCount++;

				}while (0 < iOpenedBracketCount);
				
				//exceptions at the rule
				if (szEditModificationsKW == csRedSectionName)
				{
					m_lstOfInitialUnchangeableMDFSections.AddTail (csRedSectionName);
					continue;
				}

				m_lstOfInitialMDFSections.AddTail (csRedSectionName);
			}
			//else ignore it
			
		}
			//else go to next line
	}
		

	file.Close ();

	return 1;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : CreateTheFinalMDFFile 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::CreateTheFinalMDFFile ()
{
	ASSERT (!m_csFinalMDFFileName.IsEmpty ());
	CStdioFile file;
	//assume the file exists
	UINT uOpenFlags = CFile::modeCreate | CFile::modeWrite | CFile::typeText;
	if (!file.Open (m_csFinalMDFFileName, uOpenFlags))
		return 0;

	CString csMDFHeader (";MDF file\n\n");
	file.WriteString (csMDFHeader);
	file.Close ();
	return 1;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : IsSectionIncluded
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::IsSectionIncluded (CString csSectionName)
{
	POSITION pos = m_lstOfIncludedExternalReferences.GetHeadPosition ();
	while (NULL != pos)
	{
		CString csExternSectionName = m_lstOfIncludedExternalReferences.GetNext (pos);
		if (csSectionName == csExternSectionName)
			return 1;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : ComputeAbsoluteFileName 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
CString CParseSection::ComputeAbsoluteFileName (CString csFileName)
{
	ASSERT (!IsFileNameAbsolute (csFileName));
	if (cBackslash == csFileName [0])
		csFileName = csFileName.Mid (1); //remove the useless backslash

	//assume we can compute only one absolute path
	POSITION pos = m_lstOfRoots.GetHeadPosition ();
	while (NULL != pos)
	{
		CString csRoot = m_lstOfRoots.GetNext (pos);
		//assume the root ends with a backslash
		CString csFilePath = csRoot + csFileName;
		CFileStatus status;
		if (CFile::GetStatus (csFilePath, status)) //verify if the file exists
			return csFilePath;
	}

	ASSERT (FALSE);
	return "";
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : IsFileNameAbsolute 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
int CParseSection::IsFileNameAbsolute (CString csFileName)
{
	csFileName.TrimLeft (); //make sure there are no pending blanks
	if (':' == csFileName [1])  //the path contains a drive
	{
#ifdef _DEBUG
		CFileStatus status;
		ASSERT (CFile::GetStatus (csFileName, status)); //the file exists
#endif //_DEBUG
		return 1;
	}

	return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : RemoveParameters 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
CString CParseSection::RemoveParameters (CString csRedSectionName)
{
	csRedSectionName.TrimLeft ();
	csRedSectionName.TrimRight ();
	int iInx = csRedSectionName.Find (cParameterBeginIndicator);
	BOOL bHasParameters = -1 != iInx;
	if (bHasParameters)
		csRedSectionName = csRedSectionName.Left (iInx);
		
	return csRedSectionName;

}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Method : AddRoot 
// Date   : 98-03
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Description :
// Author      : Stegaru Cristian - CPA2
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Modification :
// Date         :
// By           :
//////////////////////////////////////////////////////////////////////////////////////////////////////
void CParseSection::AddRoot (CString csRoot) 
{	
	csRoot.TrimRight ();
	//the roots have to end with a backslash
	if (cBackslash != csRoot [csRoot.GetLength () - 1])
		csRoot += cBackslash;

	m_lstOfRoots.AddTail (csRoot);
}


