#include "FileZilla.h"
#include "queue.h"
#include "statuslinectrl.h"
#include <wx/dcbuffer.h>

#define TRANSFERSTATUS_TIMER_ID (wxID_HIGHEST + 1)

BEGIN_EVENT_TABLE(CStatusLineCtrl, wxWindow)
EVT_PAINT(CStatusLineCtrl::OnPaint)
EVT_TIMER(TRANSFERSTATUS_TIMER_ID, CStatusLineCtrl::OnTimer)
EVT_ERASE_BACKGROUND(CStatusLineCtrl::OnEraseBackground)
END_EVENT_TABLE()

int CStatusLineCtrl::m_fieldOffsets[4];
wxCoord CStatusLineCtrl::m_textHeight;
bool CStatusLineCtrl::m_initialized = false;

#define PROGRESSBAR_WIDTH 100

CStatusLineCtrl::CStatusLineCtrl(CQueueView* pParent, const t_EngineData* const pEngineData, const wxRect& initialPosition)
	: m_pEngineData(pEngineData)
{
	wxASSERT(pEngineData);

	Create(pParent, wxID_ANY, initialPosition.GetPosition(), initialPosition.GetSize());
	SetOwnFont(pParent->GetFont());
	SetBackgroundStyle(wxBG_STYLE_CUSTOM);
	SetBackgroundColour(pParent->GetBackgroundColour());
	
	m_transferStatusTimer.SetOwner(this, TRANSFERSTATUS_TIMER_ID);

	m_madeProgress = false;
	m_pParent = pParent;
	m_pStatus = 0;
	m_lastOffset = -1;

	SetTransferStatus(0);


	// Calculate field widths so that the contents fit under every language.
	if (!m_initialized)
	{
		m_initialized = true;
		wxClientDC dc(this);
		dc.SetFont(GetFont());

		wxCoord w, h;
		wxTimeSpan elapsed(100, 0, 0);
		dc.GetTextExtent(elapsed.Format(_("%H:%M:%S elapsed")), &w, &h);
		m_textHeight = h;
		m_fieldOffsets[0] = 50 + w;

		dc.GetTextExtent(elapsed.Format(_("%H:%M:%S left")), &w, &h);
		m_fieldOffsets[1] = m_fieldOffsets[0] + 20 + w;

		m_fieldOffsets[2] = m_fieldOffsets[1] + 20;
		m_fieldOffsets[3] = m_fieldOffsets[2] + PROGRESSBAR_WIDTH + 20;
	}
}

CStatusLineCtrl::~CStatusLineCtrl()
{
	if (m_pStatus && m_pStatus->totalSize >= 0)
		m_pEngineData->pItem->SetSize(m_pStatus->totalSize);

	if (m_transferStatusTimer.IsRunning())
		m_transferStatusTimer.Stop();
	delete m_pStatus;
}

void CStatusLineCtrl::OnPaint(wxPaintEvent& event)
{
	wxAutoBufferedPaintDC dc(this);

	wxRect rect = GetRect();

	dc.SetFont(GetFont());
	dc.SetPen(GetBackgroundColour());
	dc.SetBrush(GetBackgroundColour());

	// Get character height so that we can center the text vertically.
	wxCoord h = (rect.GetHeight() - m_textHeight) / 2;

	// Clear background
	dc.DrawRectangle(0, 0, rect.GetWidth(), rect.GetHeight());

	if (!m_pStatus)
	{
		dc.DrawText(m_statusText, 50, h);
		return;
	}

	int elapsedSeconds;
	if (m_pStatus->started.IsValid())
	{
		wxTimeSpan elapsed = wxDateTime::Now().Subtract(m_pStatus->started);

		DrawRightAlignedText(dc, elapsed.Format(_("%H:%M:%S elapsed")), m_fieldOffsets[0], h);

		elapsedSeconds = elapsed.GetSeconds().GetLo(); // Assume GetHi is always 0
	}
	else
		elapsedSeconds = 0;

	if (elapsedSeconds)
	{
		wxFileOffset rate = (m_pStatus->currentOffset - m_pStatus->startOffset) / elapsedSeconds;

        if (rate > (1000*1000))
			dc.DrawText(wxString::Format(_("%s bytes (%d.%d MB/s)"), wxLongLong(m_pStatus->currentOffset).ToString().c_str(), (int)(rate / 1000 / 1000), (int)((rate / 1000 / 100) % 10)), m_fieldOffsets[3], h);
		else if (rate > 1000)
			dc.DrawText(wxString::Format(_("%s bytes (%d.%d KB/s)"), wxLongLong(m_pStatus->currentOffset).ToString().c_str(), (int)(rate / 1000), (int)((rate / 100) % 10)), m_fieldOffsets[3], h);
		else
			dc.DrawText(wxString::Format(_("%s bytes (%d B/s)"), wxLongLong(m_pStatus->currentOffset).ToString().c_str(), (int)rate), m_fieldOffsets[3], h);

		if (m_pStatus->totalSize > 0 && rate > 0)
		{
			int left = ((m_pStatus->totalSize - m_pStatus->startOffset) / rate) - elapsedSeconds;
			if (left < 0)
				left = 0;
			wxTimeSpan timeLeft(0, 0, left);
			DrawRightAlignedText(dc, timeLeft.Format(_("%H:%M:%S left")), m_fieldOffsets[1], h);
		}
		else
		{
			DrawRightAlignedText(dc, _("--:--:-- left"), m_fieldOffsets[1], h);
		}
	}
	else
	{
		DrawRightAlignedText(dc, _("--:--:-- left"), m_fieldOffsets[1], h);
		dc.DrawText(wxString::Format(_("%s bytes (? B/s)"), wxLongLong(m_pStatus->currentOffset).ToString().c_str()), m_fieldOffsets[3], h);
	}

	DrawProgressBar(dc, m_fieldOffsets[2], 1, rect.GetHeight() - 2);
}

void CStatusLineCtrl::SetTransferStatus(const CTransferStatus* pStatus)
{
	if (!pStatus)
	{
		if (m_pStatus && m_pStatus->totalSize >= 0)
			m_pParent->UpdateItemSize(m_pEngineData->pItem, m_pStatus->totalSize);
		delete m_pStatus;
		m_pStatus = 0;

		switch (m_pEngineData->state)
		{
		case t_EngineData::disconnect:
			m_statusText = _("Disconnecting from previous server");
			break;
		case t_EngineData::cancel:
			m_statusText = _("Waiting for transfer to be canceled");
			break;
		case t_EngineData::connect:
			m_statusText = wxString::Format(_("Connecting to %s"), m_pEngineData->lastServer.FormatServer().c_str());
			break;
		default:
			m_statusText = _("Transferring");
			break;
		}

		if (m_transferStatusTimer.IsRunning())
			m_transferStatusTimer.Stop();
	}
	else
	{
		if (!m_pStatus)
			m_pStatus = new CTransferStatus(*pStatus);
		else
			*m_pStatus = *pStatus;

		m_lastOffset = pStatus->currentOffset;

		if (!m_madeProgress && !pStatus->list && pStatus->currentOffset - pStatus->startOffset > 16384)
			m_madeProgress = true;

		if (!m_transferStatusTimer.IsRunning())
			m_transferStatusTimer.Start(100);
	}
	Refresh(false);
}

void CStatusLineCtrl::OnTimer(wxTimerEvent& event)
{
	bool changed;
	CTransferStatus status;
	if (!m_pEngineData->pEngine->GetTransferStatus(status, changed))
		SetTransferStatus(0);
	else if (changed)
		SetTransferStatus(&status);
	else
		m_transferStatusTimer.Stop();
}

void CStatusLineCtrl::DrawRightAlignedText(wxDC& dc, wxString text, int x, int y)
{
	wxCoord w, h;
	dc.GetTextExtent(text, &w, &h);
	x -= w;

	dc.DrawText(text, x, y);
}

void CStatusLineCtrl::OnEraseBackground(wxEraseEvent& event)
{
	// Don't erase background, only causes status line to flicker.
}

void CStatusLineCtrl::DrawProgressBar(wxDC& dc, int x, int y, int height)
{
	wxASSERT(m_pStatus);

	if (m_pStatus->totalSize <= 0)
		return;

	int barSplit = wxLongLong(m_pStatus->currentOffset * (PROGRESSBAR_WIDTH - 2) / m_pStatus->totalSize).GetLo();
	if (barSplit > PROGRESSBAR_WIDTH)
		barSplit = PROGRESSBAR_WIDTH;

	// Draw right part
	dc.SetPen(*wxTRANSPARENT_PEN);
	dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
	dc.DrawRectangle(x + 1 + barSplit, y + 1, PROGRESSBAR_WIDTH - barSplit - 1, height - 2);

	if (barSplit && height > 2)
	{	
		// Draw pretty gradient

		int greenmin = 128;
		int greenmax = 255;
		int colourCount = ((height + 1) / 2);

		for (int i = 0; i < colourCount; i++)
		{
			int curGreen = greenmax - ((greenmax - greenmin) * i / (colourCount - 1));
			dc.SetPen(wxPen(wxColour(0, curGreen, 0)));
			dc.DrawLine(x + 1, y + colourCount - i, x + 1 + barSplit, y + colourCount - i);
			dc.DrawLine(x + 1, y + height - colourCount + i - 1, x + 1 + barSplit, y + height - colourCount + i - 1);
		}
	}

	dc.SetPen(*wxBLACK_PEN);
	dc.SetBrush(*wxTRANSPARENT_BRUSH);
	dc.DrawRectangle(x, y, PROGRESSBAR_WIDTH, height);

	// Draw percentage-done text
	wxString prefix;
	int perMill;
	if (m_pStatus->currentOffset > m_pStatus->totalSize)
	{
		perMill = 1000;
		prefix = _T("> ");
	}
	else
		perMill = wxLongLong(m_pStatus->currentOffset * 1000 / m_pStatus->totalSize).GetLo();

	wxString text = wxString::Format(_T("%s%d.%d%%"), prefix.c_str(), perMill / 10, perMill % 10);

	wxCoord w, h;
	dc.GetTextExtent(text, &w, &h);
	dc.DrawText(text, x + PROGRESSBAR_WIDTH / 2 - w / 2, y + height / 2 - h / 2);
}

wxLongLong CStatusLineCtrl::GetSpeed() const
{
	if (!m_pStatus)
		return -1;

	wxTimeSpan elapsed = wxDateTime::Now().Subtract(m_pStatus->started);
	int elapsedSeconds = elapsed.GetSeconds().GetLo(); // Assume GetHi is always 0
	if (elapsedSeconds > 0)
		return (m_pStatus->currentOffset - m_pStatus->startOffset) / elapsedSeconds;
	else
		return -1;
}


syntax highlighted by Code2HTML, v. 0.9.1