// ConsoleBox.cpp
//

#include "pch.h"
#include "ConsoleBox.h"
#include "IRPS.h"
#include "Platform.h"

#pragma warning(disable:4996)


/////////////////////////////////////////////////////////////////////////////
// CConsoleBox
// !!! Откуда я взял это уже не помню, но оно не совсем мне подходит.
// надо бы как-нибудь переделать
// и уже постепенно переделывается под свои нужды

IMPLEMENT_DYNAMIC(CConsoleBox, CEdit)

CConsoleBox::CConsoleBox()
    = default;

CConsoleBox::~CConsoleBox()
    = default;

BEGIN_MESSAGE_MAP(CConsoleBox, CEdit)
	ON_WM_CREATE()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

void CConsoleBox::CarryReturn(bool bLineFeed)
{
	SetCaretPos();

	if (bLineFeed)
	{
		AppendText(CRLF);
	}
	else
	{
		SetSel(LineIndex(), GetWindowTextLength());
		ReplaceSel(_T(""));
	}
}

int CConsoleBox::PrintF(LPCTSTR fmt, ...)
{
	int nSize = 64;
	int nPrint = 0;

	for (;;)
	{
		auto psz = std::vector<TCHAR>(nSize);

		if (psz.data())
		{
			va_list va;
			va_start(va, fmt);
			nPrint = _vsntprintf(psz.data(), nSize, fmt, va);

			if (nPrint >= 0)
			{
				CString str(psz.data());
				str.Replace(_T("\n"), CRLF);
				AppendText(str);
				break;
			}
		}
		else
		{
			break;
		}

		nSize *= 2;
	}

	return nPrint;
}

void CConsoleBox::PutChar(TCHAR ch)
{
	if (ch < 32)
	{
		// обработаем управляющие символы
		switch (ch)
		{
			// стрелка влево
			// стрелка вправо
			// стрелка вверх
			// стрелка вниз
			case 012: // перевод строки
				CarryReturn();
				break;
				// 015: // возврат каретки
		}
	}
	else
	{
		// все остальные - будем просто выводить
		TCHAR sch[2] = { ch, 0 };
		AppendText(sch);
	}
}

void CConsoleBox::SetCaretPos(int pos)
{
	AppendText();

	if (pos >= 0)
	{
		const int li = LineIndex() + pos;
		SetSel(li, li);
		ReplaceSel(_T(""));
	}
}

void CConsoleBox::GetCaretPos(int &nLine, int &nPos) const
{
	int nStart, nEnd;
	GetSel(nStart, nEnd);
	nLine = LineFromChar(nEnd);
	nPos = nEnd - LineIndex(nLine);
}


void CConsoleBox::AppendText(LPCTSTR lpszText)
{
	if (!lpszText)
	{
		lpszText = _T("");
	}

	const size_t nTextLimit = GetLimitText(); // получим длину текста едит контрола
	const size_t nStrLen = _tcslen(lpszText);
	const HLOCAL hndl = GetHandle();

	if (hndl)
	{
		// получим указатель на буфер текста едит контрола правильным образом
		const auto buf = reinterpret_cast<TCHAR *>(::LocalLock(hndl));

		if (buf)
		{
			size_t nTxtLen = _tcslen(buf); // размер текста в буфере

			// если новая добавляемая строка уже не влазит
			if (nTxtLen + nStrLen >= nTextLimit)
			{
				const size_t nOversize = nTxtLen + nStrLen - nTextLimit;
				// надо удалять строки из начала, до тех пор, пока не начнёт влазить
				size_t nPos = 0;
				TCHAR *p = buf;

				// найдём, сколько строк надо удалить
				while (nPos <= nOversize)
				{
					TCHAR *pf = _tcschr(p, _T('\n'));

					if (pf) // если нашлось
					{
						pf++; // передвинем за найденный символ, чтобы и его включать
						nPos += (pf - p);
						p = pf;
					}
					else
					{
						// если не нашлось, то удалить надо всё
						nPos = nTxtLen;
						break;
					}
				}

				SetSel(0, static_cast<int>(nPos));
				ReplaceSel(_T("")); // или заменить на пустую строку
				nTxtLen -= nPos; // после этого у нас строка в буфере должна уменьшиться на столько
			}

			SetSel(int(nTxtLen), int(nTxtLen));
			ReplaceSel(lpszText);
			//SendMessage(EM_SCROLLCARET, 0, 0);
			//Invalidate(FALSE);
		}

		::LocalUnlock(HLOCAL(hndl));
	}
}

bool CConsoleBox::IsEditingLine()
{
	const bool b = (LineFromChar() == (GetLineCount() - 1));
	return b;
}

void CConsoleBox::DoPaste()
{
	// не работает как надо
	if (OpenClipboard())
	{
		const auto pc = reinterpret_cast<LPCTSTR>(::GetClipboardData(CF_UNICODETEXT));

		if (pc)
		{
			CString str(pc);
			str.Replace(CRLF, _T("\r"));
			str.Replace(_T("\n"), _T("\r"));
			const int n = str.GetLength();

			for (int i = 0; i < n; ++i)
			{
				SendMessage(WM_UNICHAR, (WPARAM)str.GetAt(i), 0);
			}
		}

		CloseClipboard();
	}
}

/////////////////////////////////////////////////////////////////////////////
// CConsoleBox

BOOL CConsoleBox::PreCreateWindow(CREATESTRUCT &cs)
{
	// TODO
	cs.style |= ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL;
	return CEdit::PreCreateWindow(cs);
}

int CConsoleBox::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CEdit::OnCreate(lpCreateStruct) == -1)
	{
		return -1;
	}

	SetLimitText(1024 * 1024);
	// TODO
	return 0;
}

void CConsoleBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	const auto ptr = g_Platform.GetIRPSPtr();

	if (ptr)
	{
		// вот тут надо придумать способ как транслировать чаркоды в бкшные коды
		// а то полная фигня получается.
		ptr->ReceiveData(LOBYTE(nChar));
	}

	CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}


LRESULT CConsoleBox::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO
	if ((message == WM_CLEAR || message == WM_CUT) && !IsEditingLine())
	{
		return 0;
	}

	if (message == WM_PASTE)
	{
		DoPaste();
		return 0;
	}

	return CEdit::DefWindowProc(message, wParam, lParam);
}

