#include "stdafx.h"
#include "HexEditCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

_AFX_RICHEDITEX_STATE::_AFX_RICHEDITEX_STATE()
{
	m_hInstRichEdit20 = NULL ;
}

_AFX_RICHEDITEX_STATE::~_AFX_RICHEDITEX_STATE()
{
	if( m_hInstRichEdit20 != NULL )
	{
		::FreeLibrary( m_hInstRichEdit20 ) ;
	}
}

_AFX_RICHEDITEX_STATE _afxRichEditStateEx ;

BOOL PASCAL AfxInitRichEditEx()
{
	if( ! ::AfxInitRichEdit() )
	{
		return FALSE ;
	}
	_AFX_RICHEDITEX_STATE* l_pState = &_afxRichEditStateEx ;
	if( l_pState->m_hInstRichEdit20 == NULL )
	{
		l_pState->m_hInstRichEdit20 = LoadLibraryA( "RICHED20.DLL" ) ;
	}
	return l_pState->m_hInstRichEdit20 != NULL ;
}

BOOL CHexEditCtrl::m_bInitCalled = false;

/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl

CHexEditCtrl::CHexEditCtrl() :
				m_DataMode(TEXT_MODE), 
				m_nDataLength(0),
				m_chBinaryChar((TCHAR)0xBF),
				m_nOldDataLength(0),
				m_bIsDataChangedDisabled(FALSE),
				CRichEditCtrl()
{
	if (m_bInitCalled==false)
	{
		::AfxInitRichEditEx();
		m_bInitCalled = true;
	}
}

BEGIN_MESSAGE_MAP(CHexEditCtrl, CRichEditCtrl)
	//{{AFX_MSG_MAP(CHexEditCtrl)
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHexEditCtrl message handlers

void CHexEditCtrl::SetDataMode(const DataMode &mode)
{
	m_bIsDataChangedDisabled = TRUE;
	long nStart, nEnd;
	GetSel(nStart,nEnd);
	if ((m_DataMode==HEX_MODE) && (mode==TEXT_MODE))			// hex to char
	{
		TCHAR chWinText[MAX_HEX_DATA];
		GetWindowText(chWinText,MAX_HEX_DATA-1);
		CString sText(chWinText);
		ConvertHexToData(sText);
		m_DataMode = mode;
		DataToScreen();
		SetSel(nStart/3,nEnd/3);
		SetFocus();
	} else if ((m_DataMode==TEXT_MODE) && (mode==HEX_MODE))		// char to hex
	{
		TCHAR chWinText[MAX_HEX_DATA];
		GetWindowText(chWinText,MAX_HEX_DATA-1);
		long nTextLength = GetWindowTextLength();
		CString sWindowText(chWinText);
		for (long nLoop=0; nLoop < nTextLength; nLoop++)
		{
			if (chWinText[nLoop] == m_chBinaryChar)	// it could be a binary character
			{
				SetSel(nLoop,nLoop+1);
				CHARFORMAT2 cf;
				memset(&cf,0,sizeof(CHARFORMAT2));
				cf.dwMask = CFM_STYLE;
				cf.cbSize = sizeof(CHARFORMAT2);
				SendMessage(EM_GETCHARFORMAT,SCF_SELECTION,(long)((CHARFORMAT2*)&cf) );
				sWindowText.SetAt(nLoop,(TCHAR)cf.sStyle);
			}
		}
		CString sData = CString(ConvertCharToHex(sWindowText,nTextLength));
		m_DataMode = mode;
		SetWindowText(sData);
		SetSel(nStart*3,nEnd*3);
		SetFocus();
	}
	m_bIsDataChangedDisabled = FALSE;
}

BOOL CHexEditCtrl::Create(DWORD in_dwStyle,  const RECT& in_rcRect,
                             CWnd* in_pParentWnd, UINT in_nID)
{
    CWnd* l_pWnd = this ;
    return l_pWnd->Create( _T( "RichEdit20A" ), NULL, in_dwStyle, 
                           in_rcRect, in_pParentWnd, in_nID );
}

CString CHexEditCtrl::ConvertCharToHex(CString Data, long nDataLength)
{
	CString sResult("");
	for (long nLoop=0; nLoop<nDataLength; nLoop++)
	{
		unsigned char ch = (unsigned char)Data.GetAt(nLoop);
		static const char *hex = "0123456789ABCDEF";
		unsigned char chHexA = hex[((unsigned char)(ch) >> 4) & 0x0f];
		unsigned char chHexB = hex[(unsigned char)(ch) & 0x0f];
		sResult += (char)chHexA;
		sResult += (char)chHexB;
		if (nLoop < nDataLength-1)
			sResult += " ";
	}
	return sResult;
}

TCHAR CHexEditCtrl::hex2int (CString hex)
{
	int i;
	int result;

#ifdef _UNICODE
	TCHAR* pChar = hex.GetBuffer(0);
	i = swscanf (pChar, _T("%x"), &result);
#else
	const char* pChar = hex.GetBuffer(0);
	i = sscanf (pChar, _T("%x"), &result);
#endif

	if (i==1)
		return result;
	return 0;
}

void CHexEditCtrl::ConvertHexToData(const CString &sHex)
{
	CString sEmpty("");
	CString sModHex = sHex;
	sModHex.Replace(_T(" "),_T(""));
	long nLength = sModHex.GetLength();
	if ((nLength % 2) != 0)
		return;
	for (int nFillLoop = 0; nFillLoop < (nLength/2); nFillLoop++)
		sEmpty += _T(" ");
	for (long nLoop=0; nLoop<(nLength/2); nLoop++)
	{
		CString sText = sModHex.Mid(nLoop*2,2);
		sEmpty.SetAt(nLoop,hex2int( sText ));
	}
	m_nDataLength = nLength/2;
	m_Data = sEmpty;
}

void CHexEditCtrl::SetData(CString Data, long nLength)
{
	m_bIsDataChangedDisabled = TRUE;
	long nStart, nEnd;
  if (!IsWindow (m_hWnd))
    return;
	GetSel(nStart,nEnd);

	m_Data = Data;
	m_nDataLength = nLength;

	CString sFontName("Lucida Console");
	CHARFORMAT2 cf;
	memset(&cf,0,sizeof(CHARFORMAT2));
	cf.cbSize = sizeof(CHARFORMAT2);
	for (int i = 0; i <= sFontName.GetLength()-1; i++)
		cf.szFaceName[i] = sFontName[i];
	int nPointSize = 10;
	nPointSize *= 20;	// convert from to twips
	cf.yHeight = nPointSize;
	cf.dwMask = CFM_FACE | CFM_SIZE;
	SendMessage(EM_SETCHARFORMAT,
				SCF_DEFAULT, // could be for all SCF_ALL
				(long)((CHARFORMAT2*)&cf) );

	DataToScreen();

	SetEventMask(ENM_CHANGE);
	SetSel(nStart,nEnd);
	m_bIsDataChangedDisabled = FALSE;
}

void CHexEditCtrl::GetData(CString& Data, const long nMaxLength, long& nLengthReturned)
{
	long nStart, nEnd;
	GetSel(nStart,nEnd);
	// update the data from the screen
	if (m_DataMode==HEX_MODE)
	{
		TCHAR chWinText[MAX_HEX_DATA];
		GetWindowText(chWinText,MAX_HEX_DATA-1);
		CString sText(chWinText);
		ConvertHexToData(sText);
	} else if (m_DataMode==TEXT_MODE)
	{
		TCHAR chWinText[MAX_HEX_DATA];
		GetWindowText(chWinText,MAX_HEX_DATA-1);
		CString sText(chWinText);
		m_Data = sText;
		m_nDataLength = GetWindowTextLength();
		for (long nLoop=0; nLoop < m_nDataLength; nLoop++)
		{
			if (m_Data.GetAt(nLoop) == m_chBinaryChar)	// it could be a binary character
			{
				SetSel(nLoop,nLoop+1);
				CHARFORMAT2 cf;
				memset(&cf,0,sizeof(CHARFORMAT2));
				cf.dwMask = CFM_STYLE;
				cf.cbSize = sizeof(CHARFORMAT2);
				SendMessage(EM_GETCHARFORMAT,SCF_SELECTION,(long)((CHARFORMAT2*)&cf) );
				m_Data.SetAt(nLoop, (CString((char)cf.sStyle)[0]) );//cf.sStyle);
			}
		}
	}

	// return the data
	long nLength = m_nDataLength;
	if (nLength > nMaxLength)
		nLength = nMaxLength;
	Data = m_Data;
	nLengthReturned = nLength;
	SetSel(nStart,nEnd);
}

void CHexEditCtrl::DataToScreen()
{
	CString sToDisplay("");
	if (m_DataMode == TEXT_MODE)
	{
		// for each character, if its printable then display it otherwise display the binary character
		for (long nLoop=0; nLoop < m_Data.GetLength(); nLoop++)
		{
			TCHAR ch = m_Data.GetAt(nLoop);
			if (isprint(ch))
				sToDisplay += (TCHAR)ch;
			else
				sToDisplay += (TCHAR)m_chBinaryChar;
		}
		SetWindowText(sToDisplay);
		for (long nLoop=0; nLoop < m_nDataLength; nLoop++)
		{
			int MASK = CFM_STYLE;
			SetSel(nLoop,nLoop+1);
			CHARFORMAT2 cf;
			memset(&cf,0,sizeof(CHARFORMAT2));
			cf.cbSize = sizeof(CHARFORMAT2);
			SendMessage(EM_GETCHARFORMAT,SCF_SELECTION,(long)((CHARFORMAT2*)&cf) );
			cf.cbSize = sizeof(CHARFORMAT2);
			unsigned char chData = (unsigned char)m_Data.GetAt(nLoop);
			cf.sStyle = chData;
			cf.dwMask = MASK;
			SendMessage(EM_SETCHARFORMAT,
						SCF_SELECTION, // could be for all SCF_ALL
						(long)((CHARFORMAT2*)&cf) );
		}
	} else
	if (m_DataMode == HEX_MODE)
	{
		sToDisplay = ConvertCharToHex(m_Data,m_nDataLength);
		SetWindowText(sToDisplay);
	}
}

void CHexEditCtrl::SetDataSelection(const long nFirst, const long nLength)
{
	long nNewFirst = nFirst;
	long nNewLength = nLength;
	if (m_DataMode==HEX_MODE)
	{
		nNewFirst = nFirst * 3;
		nNewLength = (nLength * 3) - 1;
		if (nNewLength<0)
			nNewLength = 0;
	}
	SetSel(nNewFirst,nNewFirst+nNewLength);
	SetFocus();
}

void CHexEditCtrl::SetBinaryCharacter(TCHAR ch)
{
	m_chBinaryChar = ch;
}

BOOL CHexEditCtrl::IsDataChanged()
{
	if (m_bIsDataChangedDisabled)
		return FALSE;

	// update the internal data
	long nLengthReturned = 0;
	GetData(m_Data, MAX_HEX_DATA, nLengthReturned);

	// since the last call to this function, has the data changed ?
	BOOL bResult = FALSE;

	if (m_nOldDataLength!=m_nDataLength)
		bResult = TRUE;
	else
	{
		if (m_OldData != m_Data)
			bResult = TRUE;
	}
	m_OldData = m_Data;
	m_nOldDataLength = m_nDataLength;
	return bResult;
}

BOOL CHexEditCtrl::OnWndMsg (UINT message, WPARAM Key, LPARAM lParam, LRESULT* pResult)
{
//       case WM_KEYDOWN:
  if (message ==  WM_CHAR)
  {
    if (m_DataMode == HEX_MODE)
    {
       if ((Key >= '0' && Key <= '9') ||
          (Key >= 'a' && Key <= 'f') ||
          (Key >= 'A' && Key <= 'F') ||
          (Key == ' ') ||
          (Key == 0x08) || // Backspace
          (Key == '\t'))
       {
       }
       else
       {
         MessageBeep (0xFFFFFFFF);
         return (TRUE);
       }
    }
  }
  return CRichEditCtrl::OnWndMsg (message, Key, lParam, pResult);
}