/*
 *	@doc INTERNAL
 *
 *	@module	RTFLOG.CPP - RichEdit RTF log
 *
 *		Contains the code for the RTFLog class which can be used 
 *		to log the number of times RTF tags are read by the RTF reader
 *		for use in coverage testing
 *
 *	Authors:<nl>
 *		Created for RichEdit 2.0:	Brad Olenick
 *
 *	Copyright (c) 1995-1996, Microsoft Corporation. All rights reserved.
 */

#include "_common.h"
#include "_rtflog.h"
#include "tokens.h"

extern INT cKeywords;
extern const KEYWORD rgKeyword[];

/*
 *	CRTFLog::CRTFLog()
 *	
 *	@mfunc
 *		Constructor - 
 *			1.  Opens a file mapping to log hit counts, creating
 *					the backing file if neccessary
 *			2.  Map a view of the file mapping into memory
 *			3.  Register a windows message for change notifications
 *
 */
CRTFLog::CRTFLog() : _rgdwHits(NULL), _hfm(NULL), _hfile(NULL)
{
#ifndef PEGASUS
	const char cstrMappingName[] = "RTFLOG";
	const char cstrWM[] = "RTFLOGWM";
	const int cbMappingSize = sizeof(ELEMENT) * ISize();

	BOOL fNewFile = FALSE;

	// check for existing file mapping
	if(!(_hfm = OpenFileMappingA(FILE_MAP_ALL_ACCESS,
								TRUE,
								cstrMappingName)))
	{
		// no existing file mapping

		// get the file with which to create the file mapping

		// first, attempt to open an existing file
		if(!(_hfile = CreateFileA(LpcstrLogFilename(),
								GENERIC_READ | GENERIC_WRITE,
								0,
								NULL,
								OPEN_EXISTING,
								FILE_ATTRIBUTE_NORMAL,
								NULL)))
		{
			// no existing file, attempt to create new
			if(!(_hfile = CreateFileA(LpcstrLogFilename(),
										GENERIC_READ | GENERIC_WRITE,
										0,
										NULL,
										OPEN_ALWAYS,
										FILE_ATTRIBUTE_NORMAL,
										NULL)))
			{
				return;
			}

			fNewFile = TRUE;
		}

		if(!(_hfm = CreateFileMappingA(_hfile,
									NULL,
									PAGE_READWRITE,
									0,
									cbMappingSize,
									cstrMappingName)))
		{
			return;
		}
	}

	LPVOID lpv;
	if(!(lpv = MapViewOfFile(_hfm, 
							FILE_MAP_ALL_ACCESS, 
							0,
							0,
							cbMappingSize)))
	{
		return;
	}

	// register windows message for change notifications
	SideAssert(_uMsg = RegisterWindowMessageA(cstrWM));

	// memory-mapped file is now mapped to _rgdwHits
	_rgdwHits = (PELEMENT)lpv;

	// zero the memory-mapped file if we created it new
	// (Win95 gives us a new file w/ garbage in it for some reason)
	if(fNewFile)
	{
		Reset();
	}		
#endif	
}


/*
 *	CRTFLog::Reset()
 *	
 *	@mfunc
 *		Resets the hitcount of each element in the log to 0
 *
 */
void CRTFLog::Reset()
{
	if(!FInit())
	{
		return;
	}

	for(INDEX i = 0; i < ISize(); i++)
	{
		(*this)[i] = 0;
	}

	// notify clients of change
	ChangeNotifyAll();
}


/*
 *	CRTFLog::UGetWindowMsg
 *
 *	@mdesc
 *		Returns the window message id used for change notifications
 *
 *	@rdesc
 *		UINT		window message id
 *
 *	@devnote
 *		This should be inline, but the AssertSz macro doesn't compile
 *		properly on the Mac if its placed in a header file
 *
 */
UINT CRTFLog::UGetWindowMsg() const
{
	AssertSz(FInit(), "CRTFLog::UGetWindowMsg():  CRTFLog not initialized properly");

	return _uMsg;
}


/*
 *	CRTFLog::operator[]
 *
 *	@mdesc
 *		Returns reference to element i of RTF log (l-value)
 *
 *	@rdesc
 *		ELEMENT &			reference to element i of log
 *
 *	@devnote
 *		This should be inline, but the AssertSz macro doesn't compile
 *		properly on the Mac if its placed in a header file
 *
 */
CRTFLog::ELEMENT &CRTFLog::operator[](INDEX i)
{
	AssertSz(i < ISize(), "CRTFLog::operator[]:  index out of range");
	AssertSz(FInit(), "CRTFLog::operator[]:  CRTFLog not initialized properly");

	return _rgdwHits[i]; 
}


/*
 *	CRTFLog::operator[]
 *
 *	@mdesc
 *		Returns reference to element i of RTF log (r-value)
 *
 *	@rdesc
 *		const ELEMENT &	reference to element i of log
 *		
 *	@devnote
 *		This should be inline, but the AssertSz macro doesn't compile
 *		properly on the Mac if its placed in a header file
 *
 */
const CRTFLog::ELEMENT &CRTFLog::operator[](INDEX i) const
{
	AssertSz(i < ISize(), "CRTFLog::operator[]:  index out of range");
	AssertSz(FInit(), "CRTFLog::operator[]:  CRTFLog not initialized properly");

	return _rgdwHits[i]; 
}


/*
 *	CRTFLog::LpcstrLogFilename()
 *	
 *	@mfunc
 *		Returns name of file to be used for log
 *
 *	@rdesc
 *		LPCSTR		pointer to static buffer containing file name
 */
LPCSTR CRTFLog::LpcstrLogFilename() const
{
	const char cstrLogFilename[] = "RTFLOG";
	static char szBuf[MAX_PATH] = "";
#ifndef PEGASUS
	if(!szBuf[0])
	{
		DWORD cchLength;
		char szBuf2[MAX_PATH];

		SideAssert(cchLength = GetTempPathA(MAX_PATH, szBuf2));

		// append trailing backslash if neccessary
		if(szBuf2[cchLength - 1] != '\\')
		{
			szBuf2[cchLength] = '\\';
			szBuf2[cchLength + 1] = 0;
		}

		wsprintfA(szBuf, "%s%s", szBuf2, cstrLogFilename);
	}
#endif
	return szBuf;
}


/*
 *	CRTFLog::IIndexOfKeyword(LPCSTR lpcstrKeyword, PINDEX piIndex)
 *	
 *	@mfunc
 *		Returns the index of the log element which corresponds to
 *		the RTF keyword, lpcstrKeyword
 *
 *	@rdesc
 *		BOOL		flag indicating whether index was found
 */
BOOL CRTFLog::IIndexOfKeyword(LPCSTR lpcstrKeyword, PINDEX piIndex) const
{
	INDEX i;

	for(i = 0; i < ISize(); i++)
	{
		if(strcmp(lpcstrKeyword, rgKeyword[i].szKeyword) == 0)
		{
			break;
		}
	}

	if(i == ISize())
	{
		return FALSE;
	}

	if(piIndex)
	{
		*piIndex = i;
	}

	return TRUE;
}


/*
 *	CRTFLog::IIndexOfToken(TOKEN token, PINDEX piIndex)
 *	
 *	@mfunc
 *		Returns the index of the log element which corresponds to
 *		the RTF token, token
 *
 *	@rdesc
 *		BOOL		flag indicating whether index was found
 */
BOOL CRTFLog::IIndexOfToken(TOKEN token, PINDEX piIndex) const
{
	INDEX i;

	for(i = 0; i < ISize(); i++)
	{
		if(token == rgKeyword[i].token)
		{
			break;
		}
	}

	if(i == ISize())
	{
		return FALSE;
	}

	if(piIndex)
	{
		*piIndex = i;
	}

	return TRUE;
}