// convert.cpp : implementation file
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1997 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "wordpad.h"
#include "multconv.h"
#include "mswd6_32.h"
#include <algorithm>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#ifdef CONVERTERS
CConverter* CConverter::m_pThis = NULL;
#endif

#define BUFFSIZE 4096

CTrackFile::CTrackFile(CFrameWnd* pWnd) : CFile()
{
	m_nLastPercent = -1;
	m_dwLength = 0;
	m_pFrameWnd = pWnd;
	VERIFY(m_strComplete.LoadString(IDS_COMPLETE));
	VERIFY(m_strWait.LoadString(IDS_PLEASE_WAIT));
	VERIFY(m_strSaving.LoadString(IDS_SAVING));
//	OutputPercent(0);
}

CTrackFile::~CTrackFile()
{ 
	OutputPercent(100);
	if (m_pFrameWnd != NULL)
		m_pFrameWnd->SetMessageText(AFX_IDS_IDLEMESSAGE);
}

UINT CTrackFile::Read(void FAR* lpBuf, UINT nCount)
{
	UINT n = CFile::Read(lpBuf, nCount);
	if (m_dwLength != 0)
		OutputPercent((int)((GetPosition()*100)/m_dwLength));
	return n;
}

void CTrackFile::Write(const void FAR* lpBuf, UINT nCount)
{
	CFile::Write(lpBuf, nCount);
	OutputString(m_strSaving);
//	if (m_dwLength != 0)
//		OutputPercent((int)((GetPosition()*100)/m_dwLength));
}

void CTrackFile::OutputString(LPCTSTR lpsz)
{
	if (m_pFrameWnd != NULL)
	{
		m_pFrameWnd->SetMessageText(lpsz);
		CWnd* pBarWnd = m_pFrameWnd->GetMessageBar();
		if (pBarWnd != NULL)
			pBarWnd->UpdateWindow();
	}
}

void CTrackFile::OutputPercent(int nPercentComplete)
{
	if (m_pFrameWnd != NULL && m_nLastPercent != nPercentComplete)
	{
		m_nLastPercent = nPercentComplete;
		TCHAR buf[64];
		int n = nPercentComplete;
		wsprintf(buf, (n==100) ? m_strWait : m_strComplete, n);
		OutputString(buf);
	}
}

COEMFile::COEMFile(CFrameWnd* pWnd) : CTrackFile(pWnd)
{
}

UINT COEMFile::Read(void FAR* lpBuf, UINT nCount)
{
	UINT n = CTrackFile::Read(lpBuf, nCount);
	OemToCharBuffA((const char*)lpBuf, (char*)lpBuf, n);
	return n;
}

void COEMFile::Write(const void FAR* lpBuf, UINT nCount)
{
	CharToOemBuffA((const char*)lpBuf, (char*)lpBuf, nCount);
	CTrackFile::Write(lpBuf, nCount);
}

#ifdef CONVERTERS

HGLOBAL CConverter::StringToHGLOBAL(LPCSTR pstr)
{
	HGLOBAL hMem = NULL;
	if (pstr != NULL)
	{
		hMem = GlobalAlloc(GHND, (lstrlenA(pstr)*2)+1);
		char* p = (char*) GlobalLock(hMem);
		ASSERT(p != NULL);
		if (p != NULL)
			lstrcpyA(p, pstr);
		GlobalUnlock(hMem);
	}
	return hMem;
}

CConverter::CConverter(LPCSTR pszLibName, CFrameWnd* pWnd) : CTrackFile(pWnd)
{
	USES_CONVERSION;
	m_hBuff = NULL;
	m_pBuf = NULL;
	m_nBytesAvail = 0;
	m_nBytesWritten = 0;
	m_nPercent = 0;
	m_hEventFile = NULL;
	m_hEventConv = NULL;
	m_bDone = TRUE;
	m_bConvErr = FALSE;
	m_hFileName = NULL;
	OFSTRUCT ofs;
	if (OpenFile(pszLibName, &ofs, OF_EXIST) == HFILE_ERROR)
	{
		m_hLibCnv = NULL;
		return;
	}
	m_hLibCnv = LoadLibraryA(pszLibName);
	if (m_hLibCnv < (HINSTANCE)HINSTANCE_ERROR)
		m_hLibCnv = NULL;
	else
	{
		LoadFunctions();
		ASSERT(m_pInitConverter != NULL);
		if (m_pInitConverter != NULL)
		{
			CString str = AfxGetAppName();
			str.MakeUpper();
			VERIFY(m_pInitConverter(AfxGetMainWnd()->GetSafeHwnd(), T2CA(str)));
		}
	}
}

CConverter::CConverter(CFrameWnd* pWnd) : CTrackFile(pWnd)
{
	m_pInitConverter = NULL;
	m_pIsFormatCorrect = NULL;
	m_pForeignToRtf = NULL;
	m_pRtfToForeign = NULL;
	m_bConvErr = FALSE;
	m_hFileName = NULL;
}

CConverter::~CConverter()
{
	if (!m_bDone) // converter thread hasn't exited
	{
		WaitForConverter();
		m_nBytesAvail = 0;
		VERIFY(ResetEvent(m_hEventFile));
		m_nBytesAvail = 0;
		SetEvent(m_hEventConv);
		WaitForConverter();// wait for DoConversion exit
		VERIFY(ResetEvent(m_hEventFile));
	}

	if (m_hEventFile != NULL)
		VERIFY(CloseHandle(m_hEventFile));
	if (m_hEventConv != NULL)
		VERIFY(CloseHandle(m_hEventConv));
	if (m_hLibCnv != NULL)
		FreeLibrary(m_hLibCnv);
	if (m_hFileName != NULL)
		GlobalFree(m_hFileName);
}

void CConverter::WaitForConverter()
{
	// while event not signalled -- process messages
	while (MsgWaitForMultipleObjects(1, &m_hEventFile, FALSE, INFINITE, 
		QS_SENDMESSAGE) != WAIT_OBJECT_0)
	{
		MSG msg;
		PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
	}
}

void CConverter::WaitForBuffer()
{
	// while event not signalled -- process messages
	while (MsgWaitForMultipleObjects(1, &m_hEventConv, FALSE, INFINITE, 
		QS_SENDMESSAGE) != WAIT_OBJECT_0)
	{
		MSG msg;
		PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
	}
}

UINT CConverter::ConverterThread(LPVOID)
{
	ASSERT(m_pThis != NULL);

	HRESULT hRes = OleInitialize(NULL);
	ASSERT(hRes == S_OK || hRes == S_FALSE);
	m_pThis->DoConversion();
	OleUninitialize();

	return 0;
}

BOOL CConverter::IsFormatCorrect(LPCTSTR pszFileName)
{
	USES_CONVERSION;
	int nRet;
	if (m_hLibCnv == NULL || m_pIsFormatCorrect == NULL)
		return FALSE;

	char buf[_MAX_PATH];
	strcpy(buf, T2CA(pszFileName));

	CharToOemA(buf, buf);

	HGLOBAL hFileName = StringToHGLOBAL(buf);
	HGLOBAL hDesc = GlobalAlloc(GHND, 256); 
	ASSERT(hDesc != NULL);
	nRet = m_pIsFormatCorrect(hFileName, hDesc);
	GlobalFree(hDesc);
	GlobalFree(hFileName);
	return (nRet == 1) ? TRUE : FALSE;
}

// static callback function
int CALLBACK CConverter::WriteOutStatic(int cch, int nPercentComplete)
{
	ASSERT(m_pThis != NULL);
	return m_pThis->WriteOut(cch, nPercentComplete);
}

int CALLBACK CConverter::WriteOut(int cch, int nPercentComplete)
{
	ASSERT(m_hBuff != NULL);
	m_nPercent = nPercentComplete;
	if (m_hBuff == NULL)
		return -9;
	if (cch != 0)
	{
		WaitForBuffer();
		VERIFY(ResetEvent(m_hEventConv));
		m_nBytesAvail = cch;
		SetEvent(m_hEventFile);
		WaitForBuffer();
	}
	return 0; //everything OK
}

int CALLBACK CConverter::ReadInStatic(int /*flags*/, int nPercentComplete)
{
	ASSERT(m_pThis != NULL);
	return m_pThis->ReadIn(nPercentComplete);
}

int CALLBACK CConverter::ReadIn(int /*nPercentComplete*/)
{
	ASSERT(m_hBuff != NULL);
	if (m_hBuff == NULL)
		return -8;

	SetEvent(m_hEventFile);
	WaitForBuffer();
	VERIFY(ResetEvent(m_hEventConv));

	return m_nBytesAvail;
}

BOOL CConverter::DoConversion()
{
	USES_CONVERSION;
	m_nLastPercent = -1;
//	m_dwLength = 0; // prevent Read/Write from displaying
	m_nPercent = 0;

	ASSERT(m_hBuff != NULL);
	ASSERT(m_pThis != NULL);
	HGLOBAL hDesc = StringToHGLOBAL("");
	HGLOBAL hSubset = StringToHGLOBAL("");

	int nRet;
	if (m_bForeignToRtf)
	{
		ASSERT(m_pForeignToRtf != NULL);
		ASSERT(m_hFileName != NULL);
		nRet = m_pForeignToRtf(m_hFileName, NULL, m_hBuff, hDesc, hSubset, 
			(LPFNOUT)WriteOutStatic);
		// wait for next CConverter::Read to come through
		WaitForBuffer();
		VERIFY(ResetEvent(m_hEventConv));
	}
	else if (!m_bForeignToRtf)
	{
		ASSERT(m_pRtfToForeign != NULL);
		ASSERT(m_hFileName != NULL);
		nRet = m_pRtfToForeign(m_hFileName, NULL, m_hBuff, hDesc, 
			(LPFNIN)ReadInStatic);
		// don't need to wait for m_hEventConv
	}

	GlobalFree(hDesc);
	GlobalFree(hSubset);
	if (m_pBuf != NULL)
		GlobalUnlock(m_hBuff);
	GlobalFree(m_hBuff);

	if (nRet != 0)
		m_bConvErr = TRUE;

	m_bDone = TRUE;
	m_nPercent = 100;
	m_nLastPercent = -1;

	SetEvent(m_hEventFile);

	return (nRet == 0);
}

void CConverter::LoadFunctions()
{
	m_pInitConverter = (PINITCONVERTER)GetProcAddress(m_hLibCnv, "InitConverter32");
	m_pIsFormatCorrect = (PISFORMATCORRECT)GetProcAddress(m_hLibCnv, "IsFormatCorrect32");
	m_pForeignToRtf = (PFOREIGNTORTF)GetProcAddress(m_hLibCnv, "ForeignToRtf32");
	m_pRtfToForeign = (PRTFTOFOREIGN)GetProcAddress(m_hLibCnv, "RtfToForeign32");
}
#endif

///////////////////////////////////////////////////////////////////////////////

BOOL CConverter::Open(LPCTSTR pszFileName, UINT nOpenFlags,
	CFileException* pException)
{
	USES_CONVERSION;
	// we convert to oem and back because of the following case
	// test(c).txt becomes testc.txt in OEM and stays testc.txt to Ansi
	char buf[_MAX_PATH];
	strcpy(buf, T2CA(pszFileName));
	CharToOemA(buf, buf);
	OemToCharA(buf, buf);

	LPTSTR lpszFileNameT = A2T(buf);

	// let's make sure we could do what is wanted directly even though we aren't
	m_bCloseOnDelete = FALSE;
	m_hFile = (UINT)hFileNull;

	BOOL bOpen = CFile::Open(lpszFileNameT, nOpenFlags, pException);
	CFile::Close();
	if (!bOpen)
		return FALSE;

	m_bForeignToRtf = !(nOpenFlags & (CFile::modeReadWrite | CFile::modeWrite));

	// check for reading empty file
	if (m_bForeignToRtf)
	{
		CFileStatus stat;
		if (CFile::GetStatus(lpszFileNameT, stat) && stat.m_size == 0)
			return TRUE;
	}

	//set security attributes to inherit handle
	SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
	//create the events
	m_hEventFile = CreateEvent(&sa, TRUE, FALSE, NULL);
	m_hEventConv = CreateEvent(&sa, TRUE, FALSE, NULL);
	//create the converter thread and create the events

	CharToOemA(buf, buf);
	ASSERT(m_hFileName == NULL);
	m_hFileName = StringToHGLOBAL(buf);

	m_pThis = this;
	m_bDone = FALSE;
	m_hBuff = GlobalAlloc(GHND, BUFFSIZE);
	ASSERT(m_hBuff != NULL);

	AfxBeginThread(ConverterThread, this, THREAD_PRIORITY_NORMAL, 0, 0, &sa);

	return TRUE;
}

// m_hEventConv -- the main thread signals this event when ready for more data
// m_hEventFile -- the converter signals this event when data is ready

UINT CConverter::Read(void FAR* lpBuf, UINT nCount)
{
	ASSERT(m_bForeignToRtf);
	if (m_bDone)
		return 0;
	// if converter is done
	int cch = nCount;
	BYTE* pBuf = (BYTE*)lpBuf;
	while (cch != 0)
	{
		if (m_nBytesAvail == 0)
		{
			if (m_pBuf != NULL)
				GlobalUnlock(m_hBuff);
			m_pBuf = NULL;
			SetEvent(m_hEventConv);
			WaitForConverter();
			VERIFY(ResetEvent(m_hEventFile));
			if (m_bConvErr)
				AfxThrowFileException(CFileException::generic);
			if (m_bDone)
				return nCount - cch;
			m_pBuf = (BYTE*)GlobalLock(m_hBuff);
			ASSERT(m_pBuf != NULL);
		}
		int nBytes = std::min(cch, m_nBytesAvail);
		memcpy(pBuf, m_pBuf, nBytes);
		pBuf += nBytes;
		m_pBuf += nBytes;
		m_nBytesAvail -= nBytes;
		cch -= nBytes;
		OutputPercent(m_nPercent);
	}
	return nCount - cch;
}

void CConverter::Write(const void FAR* lpBuf, UINT nCount)
{
	ASSERT(!m_bForeignToRtf);

	m_nBytesWritten += nCount;
	while (nCount != 0)
	{
		WaitForConverter();
		VERIFY(ResetEvent(m_hEventFile));
		if (m_bConvErr)
			AfxThrowFileException(CFileException::generic);
		m_nBytesAvail = std::min(nCount, BUFFSIZE);
		nCount -= m_nBytesAvail;
		BYTE* pBuf = (BYTE*)GlobalLock(m_hBuff);
		ASSERT(pBuf != NULL);
		memcpy(pBuf, lpBuf, m_nBytesAvail);
		GlobalUnlock(m_hBuff);
		SetEvent(m_hEventConv);
	}
	OutputString(m_strSaving);
}

LONG CConverter::Seek(LONG lOff, UINT nFrom)
{
	if (lOff != 0 && nFrom != current)
		AfxThrowNotSupportedException();
	return 0;
}

DWORD CConverter::GetPosition() const
{
	return 0;
}

void CConverter::Flush()
{
}

void CConverter::Close()
{
}

void CConverter::Abort()
{
}

DWORD CConverter::GetLength() const
{
	ASSERT_VALID(this);
	return 1;
}

CFile* CConverter::Duplicate() const
{
	AfxThrowNotSupportedException();
	return NULL;
}

void CConverter::LockRange(DWORD, DWORD)
{
	AfxThrowNotSupportedException();
}

void CConverter::UnlockRange(DWORD, DWORD)
{
	AfxThrowNotSupportedException();
}

void CConverter::SetLength(DWORD)
{
	AfxThrowNotSupportedException();
}
