#include "FileZilla.h"
#include "StatusView.h"
#include <wx/wupdlock.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define MAX_LINECOUNT 1000

BEGIN_EVENT_TABLE(CStatusView, wxWindow)
	EVT_SIZE(CStatusView::OnSize)
	EVT_MENU(XRCID("ID_CLEARALL"), CStatusView::OnClear)
	EVT_MENU(XRCID("ID_COPYTOCLIPBOARD"), CStatusView::OnCopy)
END_EVENT_TABLE()

class CFastTextCtrl : public wxTextCtrl
{
public:
	CFastTextCtrl(wxWindow* parent)
		: wxTextCtrl(parent, -1, _T(""), wxDefaultPosition, wxDefaultSize,
					 wxNO_BORDER | wxVSCROLL | wxTE_MULTILINE |
					 wxTE_READONLY | wxTE_RICH | wxTE_RICH2 | wxTE_NOHIDESEL |
					 wxTAB_TRAVERSAL)
	{
	}
#ifdef __WXMSW__
	// wxTextCtrl::Remove is somewhat slow, this is a faster version
	virtual void Remove(long from, long to)
	{
		DoSetSelection(from, to, false);

		m_updatesCount = -2;        // suppress any update event

		::SendMessage((HWND)GetHandle(), EM_REPLACESEL, 0, (LPARAM)_T(""));
	}
#endif

	DECLARE_EVENT_TABLE();

	void OnText(wxCommandEvent& event)
	{
		// Do nothing here.
		// Having this event handler prevents the event from propagating up the
		// window hierarchy which saves a few CPU cycles.
	}

#ifdef __WXMSW__
	void OnNavigationKey(wxNavigationKeyEvent& event)
	{
		wxWindow* parent = GetParent();
		event.SetEventObject(parent);
		parent->ProcessEvent(event);
	}
#else
	void OnKeyDown(wxKeyEvent& event)
	{
		if (event.GetKeyCode() != WXK_TAB)
		{
			event.Skip();
			return;
		}

		wxWindow* parent = GetParent();

		wxNavigationKeyEvent navEvent;
		navEvent.SetEventObject(parent);
		navEvent.SetDirection(!event.ShiftDown());
		navEvent.SetFromTab(true);
		navEvent.ResumePropagation(1);
		parent->ProcessEvent(navEvent);
	}
#endif
};

BEGIN_EVENT_TABLE(CFastTextCtrl, wxTextCtrl)
	EVT_TEXT(wxID_ANY, CFastTextCtrl::OnText)
#ifdef __WXMSW__
	EVT_NAVIGATION_KEY(CFastTextCtrl::OnNavigationKey)
#else
	EVT_KEY_DOWN(CFastTextCtrl::OnKeyDown)
#endif
END_EVENT_TABLE()


CStatusView::CStatusView(wxWindow* parent, wxWindowID id)
	: wxWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER)
{
	m_pTextCtrl = 0;
	m_pTextCtrl = new CFastTextCtrl(this);
	m_pTextCtrl->SetFont(GetFont());

	m_pTextCtrl->Connect(wxID_ANY, wxEVT_CONTEXT_MENU, wxContextMenuEventHandler(CStatusView::OnContextMenu), 0, this);

	m_nLineCount = 0;

	InitDefAttr();
}

CStatusView::~CStatusView()
{
}

void CStatusView::OnSize(wxSizeEvent &event)
{
	if (m_pTextCtrl)
	{
		m_pTextCtrl->SetSize(GetClientSize());
	}
}

void CStatusView::AddToLog(CLogmsgNotification *pNotification)
{
	AddToLog(pNotification->msgType, pNotification->msg);
}

void CStatusView::AddToLog(enum MessageType messagetype, wxString message)
{
#ifndef __WXGTK__
	wxWindowUpdateLocker *pLock = 0;
#endif //__WXGTK__
	wxString prefix;

	if (m_nLineCount)
		prefix = _T("\n");

	if (m_nLineCount == MAX_LINECOUNT)
	{
#ifndef __WXGTK__
		pLock = new wxWindowUpdateLocker(m_pTextCtrl);
#endif //__WXGTK__
		m_pTextCtrl->Remove(0, m_lineLengths.front() + 1);
		m_lineLengths.pop_front();
	}
	else
		m_nLineCount++;

	m_pTextCtrl->SetDefaultStyle(m_attributeCache[messagetype].attr);

	prefix += m_attributeCache[messagetype].prefix;

	int lineLength = m_attributeCache[messagetype].len + message.Length();

	if (m_rtl)
	{
		// Unicode control characters that control reading direction
		const wxChar LTR_MARK = 0x200e;
		//const wxChar RTL_MARK = 0x200f;
		const wxChar LTR_EMBED = 0x202A;
		//const wxChar RTL_EMBED = 0x202B;
		//const wxChar POP = 0x202c;
		//const wxChar LTR_OVERRIDE = 0x202D;
		//const wxChar RTL_OVERRIDE = 0x202E;

		if (messagetype == Command || messagetype == Response || messagetype >= Debug_Warning)
		{
			// Commands, responses and debug message contain English text,
			// set LTR reading order for them.
			prefix += LTR_MARK;
			prefix += LTR_EMBED;
			lineLength += 2;
		}
	}

	m_lineLengths.push_back(lineLength);

	m_pTextCtrl->AppendText(prefix + message);

#ifndef __WXGTK__
	delete pLock;
#endif //__WXGTK__
}

void CStatusView::InitDefAttr()
{
	// Measure withs of all types
	wxClientDC dc(this);

	int maxWidth = 0;
	wxCoord width = 0;
	wxCoord height = 0;
	dc.GetTextExtent(_("Error:"), &width, &height);
	maxWidth = width;
	dc.GetTextExtent(_("Command:"), &width, &height);
	if (width > maxWidth)
		maxWidth = width;
	dc.GetTextExtent(_("Response:"), &width, &height);
	if (width > maxWidth)
		maxWidth = width;
	dc.GetTextExtent(_("Trace:"), &width, &height);
	if (width > maxWidth)
		maxWidth = width;
	dc.GetTextExtent(_("Listing:"), &width, &height);
	if (width > maxWidth)
		maxWidth = width;
	dc.GetTextExtent(_("Status:"), &width, &height);
	if (width > maxWidth)
		maxWidth = width;

	dc.SetMapMode(wxMM_LOMETRIC);

	maxWidth = dc.DeviceToLogicalX(maxWidth) + 20;
	wxArrayInt array;
	array.Add(maxWidth);
	wxTextAttr defAttr;
	defAttr.SetTabs(array);
	defAttr.SetLeftIndent(0, maxWidth);

	for (int i = 0; i < MessageTypeCount; i++)
	{
		m_attributeCache[i].attr = defAttr;
		switch (i)
		{
		case Error:
			m_attributeCache[i].prefix = _("Error:");
			m_attributeCache[i].attr.SetTextColour(wxColour(255, 0, 0));
			break;
		case Command:
			m_attributeCache[i].prefix = _("Command:");
			m_attributeCache[i].attr.SetTextColour(wxColour(0, 0, 128));
			break;
		case Response:
			m_attributeCache[i].prefix = _("Response:");
			m_attributeCache[i].attr.SetTextColour(wxColour(0, 128, 0));
			break;
		case Debug_Warning:
		case Debug_Info:
		case Debug_Verbose:
		case Debug_Debug:
			m_attributeCache[i].prefix = _("Trace:");
			m_attributeCache[i].attr.SetTextColour(wxColour(128, 0, 128));
			break;
		case RawList:
			m_attributeCache[i].prefix = _("Listing:");
			m_attributeCache[i].attr.SetTextColour(wxColour(0, 128, 128));
			break;
		default:
			m_attributeCache[i].prefix = _("Status:");
			m_attributeCache[i].attr.SetTextColour(wxColour(0, 0, 0));
			break;
		}
		m_attributeCache[i].prefix += _T("\t");
		m_attributeCache[i].len = m_attributeCache[i].prefix.Length();
	}

	m_rtl = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft;
}

void CStatusView::OnContextMenu(wxContextMenuEvent& event)
{
	wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_LOG"));
	if (!pMenu)
		return;

	PopupMenu(pMenu);
	delete pMenu;
}

void CStatusView::OnClear(wxCommandEvent& event)
{
	if (m_pTextCtrl)
		m_pTextCtrl->Clear();
	m_nLineCount = 0;
	m_lineLengths.clear();
}

void CStatusView::OnCopy(wxCommandEvent& event)
{
	if (!m_pTextCtrl)
		return;

	long from, to;
	m_pTextCtrl->GetSelection(&from, &to);
	if (from != to)
		m_pTextCtrl->Copy();
	else
	{
		m_pTextCtrl->Freeze();
		m_pTextCtrl->SetSelection(-1, -1);
		m_pTextCtrl->Copy();
		m_pTextCtrl->SetSelection(from, to);
		m_pTextCtrl->Thaw();
	}
}

void CStatusView::SetFocus()
{
	m_pTextCtrl->SetFocus();
}


syntax highlighted by Code2HTML, v. 0.9.1