/*
 * OpenClonk, http://www.openclonk.org
 *
 * Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/
 * Copyright (c) 2013-2016, The OpenClonk Team and contributors
 *
 * Distributed under the terms of the ISC license; see accompanying file
 * "COPYING" for details.
 *
 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
 * See accompanying file "TRADEMARK" for details.
 *
 * To redistribute this file separately, substitute the full license texts
 * for the above references.
 */
// HTTP download dialog; downloads a file

#include "C4Include.h"
#include "gui/C4DownloadDlg.h"

#include "graphics/C4GraphicsResource.h"

C4Network2HTTPClient HTTPClient;

C4DownloadDlg::C4DownloadDlg(const char *szDLType) : C4GUI::Dialog(C4GUI_ProgressDlgWdt, 100, FormatString(LoadResStr("IDS_CTL_DL_TITLE"), szDLType).getData(), false), szError(nullptr)
{
#ifdef HAVE_WINSOCK
	fWinSock = AcquireWinSock();
#endif
	// add all elements - will be reposisioned when text is displayed
	AddElement(pIcon = new C4GUI::Icon(C4Rect(), C4GUI::Ico_NetWait));
	AddElement(pStatusLabel = new C4GUI::Label("", C4Rect(), ACenter, C4GUI_MessageFontClr, &::GraphicsResource.TextFont, false));
	pProgressBar = nullptr; // created when necessary
	AddElement(pCancelBtn = new C4GUI::CancelButton(C4Rect()));
}

C4DownloadDlg::~C4DownloadDlg()
{
#ifdef HAVE_WINSOCK
	if (fWinSock) ReleaseWinSock();
#endif
}

void C4DownloadDlg::SetStatus(const char *szNewText, int32_t iProgressPercent)
{
	// get positions
	C4GUI::ComponentAligner caMain(GetClientRect(), C4GUI_DefDlgIndent, C4GUI_DefDlgIndent, true);
	// place icon
	pIcon->SetBounds(caMain.GetFromLeft(C4GUI_IconWdt, C4GUI_IconWdt));
	// place message label
	// use text with line breaks
	StdStrBuf sMsgBroken;
	int iMsgHeight = ::GraphicsResource.TextFont.BreakMessage(szNewText, caMain.GetInnerWidth(), &sMsgBroken, true);
	pStatusLabel->SetBounds(caMain.GetFromTop(iMsgHeight));
	pStatusLabel->SetText(sMsgBroken.getData());
	// place progress bar
	if (iProgressPercent >= 0)
	{
		if (!pProgressBar)
		{
			AddElement(pProgressBar = new C4GUI::ProgressBar(caMain.GetFromTop(C4GUI_ProgressDlgPBHgt)));
		}
		else
		{
			pProgressBar->SetBounds(caMain.GetFromTop(C4GUI_ProgressDlgPBHgt));
		}
		pProgressBar->SetProgress(iProgressPercent);
	}
	else
	{
		// no progress desired
		if (pProgressBar) { delete pProgressBar; pProgressBar = nullptr; }
	}
	// place button
	caMain.ExpandLeft(C4GUI_DefDlgIndent*2 + C4GUI_IconWdt);
	C4GUI::ComponentAligner caButtonArea(caMain.GetFromTop(C4GUI_ButtonAreaHgt), 0,0);
	pCancelBtn->SetBounds(caButtonArea.GetCentered(C4GUI_DefButtonWdt, C4GUI_ButtonHgt));
	pCancelBtn->SetToolTip(LoadResStr("IDS_DL_CANCEL"));
	// resize to actually needed size
	SetClientSize(GetClientRect().Wdt, GetClientRect().Hgt - caMain.GetHeight());
}

void C4DownloadDlg::OnIdle()
{
	// continue query process
	if (!HTTPClient.Execute())
	{
		// query aborted
		Close(false);
		return;
	}
	if (!HTTPClient.isBusy())
	{
		// download done or aborted
		Close(HTTPClient.isSuccess());
		return;
	}
	StdStrBuf sStatus; int32_t iProgress = -1;
	StdStrBuf sSize("");
	// download in progress: Update status
	if (!HTTPClient.isConnected())
	{
		// still connecting
		sStatus.Ref(LoadResStr("IDS_DL_STATUSCONNECTING"));
	}
	else
	{
		// header received?
		size_t iSize = HTTPClient.getTotalSize();
		if (!iSize)
		{
			// file size unknown: No header received.
			sStatus.Ref(LoadResStr("IDS_PRC_CONNECTED"));
		}
		else
		{
			// file size known: Download in progress
			sStatus.Ref(LoadResStr("IDS_CTL_DL_PROGRESS"));
			if (iSize <= 1024)
				sSize.Format(" (%ld Bytes)", (long)iSize);
			else if (iSize <= 1024*1024)
				sSize.Format(" (%ld KB)", (long)(iSize/1024));
			else
				sSize.Format(" (%ld MB)", (long)(iSize/1024/1024));
			iProgress = int64_t(100) * HTTPClient.getDownloadedSize() / iSize;
		}
	}
	const char *szStatusString = LoadResStr("IDS_PRC_DOWNLOADINGFILE");
	SetStatus(FormatString(szStatusString, GetFilename(HTTPClient.getRequest())).getData(), iProgress );
}

void C4DownloadDlg::UserClose(bool fOK)
{
	// user cancelled
	HTTPClient.Cancel(LoadResStr("IDS_ERR_USERCANCEL"));
}

const char *C4DownloadDlg::GetError()
{
	// own error?
	if (szError) return szError;
	// fallback to HTTP error
	return HTTPClient.GetError();
}

bool C4DownloadDlg::ShowModal(C4GUI::Screen *pScreen, const char *szURL, const char *szSaveAsFilename)
{
	// reset error
	szError = nullptr;
	// initial text
	if (!HTTPClient.Init()) return false;
	HTTPClient.SetServer(szURL);
	// show dlg
	if (!Show(pScreen, true)) return false;
	// start query
	if (!HTTPClient.Query(nullptr, true)) return false;
	// first time status update
	OnIdle();
	// cycle until query is finished or aborted
	if (!DoModal()) return false;
	// download successful: Save file
	if (!HTTPClient.getResultBin().SaveToFile(szSaveAsFilename))
	{
		// file saving failed
		szError = LoadResStr("IDS_FAIL_SAVE");
		return false;
	}
	return true;
}

bool C4DownloadDlg::DownloadFile(const char *szDLType, C4GUI::Screen *pScreen, const char *szURL, const char *szSaveAsFilename, const char *szNotFoundMessage)
{
	// log it
	LogF(LoadResStr("IDS_PRC_DOWNLOADINGFILE"), szURL);
	// show download dialog
	C4DownloadDlg *pDlg = new C4DownloadDlg(szDLType);
	if (!pDlg->ShowModal(pScreen, szURL, szSaveAsFilename))
	{
		// show an appropriate error
		const char *szError = pDlg->GetError();
		if (!szError || !*szError) szError = LoadResStr("IDS_PRC_UNKOWNERROR");
		StdStrBuf sError;
		sError.Format(LoadResStr("IDS_PRC_DOWNLOADERROR"), GetFilename(szURL), szError);
		// it's a 404: display extended message
		if (SSearch(szError, "404") && szNotFoundMessage)
			{ sError.Append("|"); sError.Append(szNotFoundMessage); }
		// display message
		pScreen->ShowMessageModal(sError.getData(), FormatString(LoadResStr("IDS_CTL_DL_TITLE"), szDLType).getData(), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error, nullptr);
		delete pDlg;
		return false;
	}
	LogF(LoadResStr("IDS_PRC_DOWNLOADCOMPLETE"), szURL);
	delete pDlg;
	return true;
}
