// FontCache.cpp : Implementation of cFontCache
#include "stdafx.h"
#include "Inject.h"
#include "FontCache.h"

#include "Manager.h"

/////////////////////////////////////////////////////////////////////////////
// cFontCache

cFontCache::~cFontCache()
{
	cManager::_p->removeFont( this );
}

#define BUFFER_WIDTHCHARS 32
#define BUFFER_HEIGHTCHARS 8
#define BUFFER_CHARCOUNT (BUFFER_WIDTHCHARS * BUFFER_HEIGHTCHARS)

bool cFontCache::checkBuffer()
{
	if( m_pBuffer.p )
	{
		VARIANT_BOOL bWasLost;
		m_pBuffer->get_WasLost( &bWasLost );
		if( bWasLost )
			m_pBuffer.Release();
	}

	if( m_pBuffer.p )
		// The character buff is intact
		return true;

	// Get a dummy DC from the primary surface
	CComPtr< ICanvas > pSurf;

	if( FAILED(cManager::_p->GetPrimarySurface( &pSurf )) )
		return false;

	// Madar: This can happen if you set an edit-text during plugin initialization.
	if (!pSurf)
	{
		return false;
	}

	HDC hdc;
	pSurf->GetDC( &hdc );

	// Drakier: reset error, try to create font, then check for error.
	SetLastError(ERROR_SUCCESS);
	HFONT hfnt = ::CreateFontIndirect( &m_lf );
	if (GetLastError() != ERROR_SUCCESS)
	{
		_ASSERTE(GetLastError());
		// Drakier: try arial instead
		strcpy(m_lf.lfFaceName, "Arial");
		SetLastError(ERROR_SUCCESS);
		HFONT hfnt = ::CreateFontIndirect( &m_lf );
		if (GetLastError() != ERROR_SUCCESS)
			return false;
	}

	HFONT hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );


	TEXTMETRIC tm;
	::GetTextMetrics( hdc, &tm );

	m_szCharCell.cy = tm.tmHeight;
	m_szCharCell.cx = tm.tmMaxCharWidth;

	// Create the buffer
	SIZE szBuffer = { m_szCharCell.cx * BUFFER_WIDTHCHARS, m_szCharCell.cy * BUFFER_HEIGHTCHARS };
	cManager::_p->CreateCanvas( &szBuffer, &m_pBuffer );

	INT widths[ BUFFER_CHARCOUNT ];
	BOOL b = ::GetCharWidth( hdc, 0, ( BUFFER_CHARCOUNT - 1 ), widths );
	_ASSERTE( b );
	DWORD dwError = ::GetLastError();

	for( short nChar = 0; nChar < BUFFER_CHARCOUNT; ++ nChar )
	{
		// TODO: Render the chars
		// Figure out the character cell size
		m_nWidths[ nChar ].m_nWidth = widths[ nChar ];
	}

	// TODO: Render all the chars into the buffer

	::SelectObject( hdc, hfntOld );
	::DeleteObject( hfnt );

	pSurf->ReleaseDC();

	return true;
}

STDMETHODIMP cFontCache::DrawText( LPPOINT ppt, BSTR strText, long clr, ICanvas *pCanvas )
{
/*	_ASSERTE( ppt != NULL );
	_ASSERTE( strText != NULL );
	_ASSERTE( pCanvas != NULL );

	if( !checkBuffer() )
		return E_FAIL;

	USES_CONVERSION;

	LPCTSTR szText = OLE2T( strText );

	// TODO: Draw this a lot faster from a cached bitmap value

	HDC hdc;
	pCanvas->GetDC( &hdc );

	// Draw the text
	HFONT hfnt = ::CreateFontIndirect( &m_lf ),
		hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );

	::SetBkMode( hdc, TRANSPARENT );
	::SetTextColor( hdc, clr );
	::SetTextAlign( hdc, TA_TOP | TA_LEFT );

	LPCSTR szAnsi = OLE2A( strText );
	int cbLength = ::strlen( szAnsi );

	INT *pDeltas = new INT[ cbLength ];

	// Fill in the character deltas
	const char *i_src = szAnsi;
	INT *i_end_deltas = pDeltas + cbLength;
	for( INT *i_delta = pDeltas; i_delta != i_end_deltas; ++ i_delta, ++ i_src )
		*i_delta = m_nWidths[ *i_src ].m_nWidth;

	if( m_bFontSmoothing )
		SystemParametersInfo( SPI_SETFONTSMOOTHING, FALSE, NULL, 0 );

	::ExtTextOut( hdc, ppt->x, ppt->y, 0, NULL, szAnsi, cbLength, pDeltas );

	if( m_bFontSmoothing )
		SystemParametersInfo( SPI_SETFONTSMOOTHING, TRUE, NULL, 0 );

	delete[] pDeltas;

	::SelectObject( hdc, hfntOld );
	::DeleteObject( hfnt );

	pCanvas->ReleaseDC();

	return S_OK;*/
	return DrawTextEx( ppt, strText, clr, 0, 0, pCanvas );
}

STDMETHODIMP cFontCache::DrawTextEx( LPPOINT ppt, BSTR strText, long clr1, long clr2, long lFlags, ICanvas *pCanvas )
{
	_ASSERTE( ppt != NULL );
	_ASSERTE( strText != NULL );
	_ASSERTE( pCanvas != NULL );

	if( !checkBuffer() )
		return E_FAIL;

	USES_CONVERSION;

	LPCTSTR szText = OLE2T( strText );

	// TODO: Draw this a lot faster from a cached bitmap value

	HDC hdc;
	pCanvas->GetDC( &hdc );

	// Draw the text
	// Drakier: reset error, try to create font, then check for error.
	SetLastError(ERROR_SUCCESS);
	HFONT hfnt = ::CreateFontIndirect( &m_lf );
	if (GetLastError() != ERROR_SUCCESS)
	{
		_ASSERTE(GetLastError());
		// Drakier: try arial instead
		strcpy(m_lf.lfFaceName, "Arial");
		SetLastError(ERROR_SUCCESS);
		HFONT hfnt = ::CreateFontIndirect( &m_lf );
		if (GetLastError() != ERROR_SUCCESS)
			return E_FAIL;
	}

	HFONT hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );

	::SetBkMode( hdc, TRANSPARENT );
	::SetTextAlign( hdc, TA_TOP | TA_LEFT );

	LPCSTR szAnsi = OLE2A( strText );
	int cbLength = ::strlen( szAnsi );

	INT *pDeltas = new INT[ cbLength ];

	// Fill in the character deltas
	const char *i_src = szAnsi;
	INT *i_end_deltas = pDeltas + cbLength;
	for( INT *i_delta = pDeltas; i_delta != i_end_deltas; ++ i_delta, ++ i_src )
		*i_delta = m_nWidths[ *i_src ].m_nWidth;

	if( !(lFlags & eAA) )
		if( m_bFontSmoothing )
			SystemParametersInfo( SPI_SETFONTSMOOTHING, FALSE, NULL, 0 );

	if( lFlags & eOutlined )
	{
		::SetTextColor( hdc, clr2 );

		::ExtTextOut( hdc, ppt->x - 1, ppt->y - 1, 0, NULL, szAnsi, cbLength, pDeltas );
		::ExtTextOut( hdc, ppt->x - 1, ppt->y + 1, 0, NULL, szAnsi, cbLength, pDeltas );
		::ExtTextOut( hdc, ppt->x + 1, ppt->y - 1, 0, NULL, szAnsi, cbLength, pDeltas );
		::ExtTextOut( hdc, ppt->x + 1, ppt->y + 1, 0, NULL, szAnsi, cbLength, pDeltas );
	}

	::SetTextColor( hdc, clr1 );
	::ExtTextOut( hdc, ppt->x, ppt->y, 0, NULL, szAnsi, cbLength, pDeltas );

	if( !(lFlags & eAA) )
		if( m_bFontSmoothing )
			SystemParametersInfo( SPI_SETFONTSMOOTHING, TRUE, NULL, 0 );

	delete[] pDeltas;

	::SelectObject( hdc, hfntOld );
	::DeleteObject( hfnt );

	pCanvas->ReleaseDC();

	return S_OK;
}

STDMETHODIMP cFontCache::MeasureText( BSTR strText, LPSIZE pszExt )
{
	_ASSERTE( strText != NULL );
	_ASSERTE( pszExt != NULL );

	if( !checkBuffer() )
		return E_FAIL;

	USES_CONVERSION;

	LPCSTR szText = OLE2T( strText ),
		i_end_text = szText + strlen( szText );

	pszExt->cy = m_szCharCell.cy;
	pszExt->cx = 0;

	for( LPCSTR i_text = szText; i_text != i_end_text; ++ i_text )
		pszExt->cx += m_nWidths[ *i_text ].m_nWidth;

	return S_OK;
}

STDMETHODIMP cFontCache::HitTest(BSTR szText, long nPos, long *nIndex)
{
	_ASSERTE( szText != NULL );
	_ASSERTE( nIndex != NULL );

	if( !checkBuffer() )
		return E_FAIL;

	USES_CONVERSION;

	LPCSTR szAText = OLE2T( szText ),
		i_end_text = szAText + strlen( szAText );

	for( LPCSTR i_text = szAText; i_text != i_end_text; ++ i_text )
	{
		short nWidth = m_nWidths[ *i_text ].m_nWidth;
		if( nPos < nWidth )
		{
			*nIndex = ( i_text - szAText );
			return S_OK;
		}

		nPos -= nWidth;
	}

	*nIndex = -1;

	return S_OK;
}
