/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "gui/downloaddialog.h"
#include "backends/cloud/cloudmanager.h"
#include "backends/networking/connection/islimited.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "engines/metaengine.h"
#include "gui/browser.h"
#include "gui/chooser.h"
#include "gui/editgamedialog.h"
#include "gui/launcher.h"
#include "gui/message.h"
#include "gui/remotebrowser.h"
#include "gui/widgets/edittext.h"
#include "gui/widgets/list.h"

namespace GUI {

enum {
	kDownloadDialogButtonCmd = 'Dldb'
};

DownloadDialog::DownloadDialog(uint32 storageId, LauncherDialog *launcher) :
	Dialog("GlobalOptions_Cloud_DownloadDialog"), _launcher(launcher), _close(false) {
	_backgroundType = GUI::ThemeEngine::kDialogBackgroundPlain;

	_browser = new BrowserDialog(_("Select directory where to download game data"), true);
	_remoteBrowser = new RemoteBrowserDialog(_("Select directory with game data"));

	_remoteDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.RemoteDirectory", _("From: "));
	_localDirectoryLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.LocalDirectory", _("To: "));
	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
	_progressBar = new SliderWidget(this, "GlobalOptions_Cloud_DownloadDialog.ProgressBar");
	_progressBar->setMinValue(0);
	_progressBar->setMaxValue(100);
	_progressBar->setValue(progress);
	_progressBar->setEnabled(false);
	_percentLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.PercentText", Common::String::format("%u %%", progress));
	_downloadSizeLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DownloadSize", "");
	_downloadSpeedLabel = new StaticTextWidget(this, "GlobalOptions_Cloud_DownloadDialog.DownloadSpeed", "");
	if (g_system->getOverlayWidth() > 320)
		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _("Cancel download"), 0, kDownloadDialogButtonCmd);
	else
		_cancelButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.MainButton", _c("Cancel download", "lowres"), 0, kDownloadDialogButtonCmd);

	_closeButton = new ButtonWidget(this, "GlobalOptions_Cloud_DownloadDialog.CloseButton", _("Hide"), 0, kCloseCmd);
	refreshWidgets();

	CloudMan.setDownloadTarget(this);
}

DownloadDialog::~DownloadDialog() {
	CloudMan.setDownloadTarget(nullptr);
}

void DownloadDialog::open() {
	Dialog::open();
	CloudMan.setDownloadTarget(this);
	if (!CloudMan.isDownloading())
		if (!selectDirectories())
			close();
	reflowLayout();
	draw();
}

void DownloadDialog::close() {
	CloudMan.setDownloadTarget(nullptr);
	Dialog::close();
}

void DownloadDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
	switch (cmd) {
	case kDownloadDialogButtonCmd:
		{
			CloudMan.setDownloadTarget(nullptr);
			CloudMan.cancelDownload();
			close();
			break;
		}
	case kDownloadProgressCmd:
		if (!_close) {
			refreshWidgets();
			draw();
		}
		break;
	case kDownloadEndedCmd:
		_close = true;
		break;
	default:
		Dialog::handleCommand(sender, cmd, data);
	}
}

bool DownloadDialog::selectDirectories() {
	if (Networking::Connection::isLimited()) {
		MessageDialog alert(_("It looks like your connection is limited. "
			"Do you really want to download files with it?"), _("Yes"), _("No"));
		if (alert.runModal() != GUI::kMessageOK)
			return false;
	}

	//first user should select remote directory to download
	if (_remoteBrowser->runModal() <= 0)
		return false;

	Cloud::StorageFile remoteDirectory = _remoteBrowser->getResult();

	//now user should select local directory to download into
	if (_browser->runModal() <= 0)
		return false;

	Common::FSNode dir(_browser->getResult());
	Common::FSList files;
	if (!dir.getChildren(files, Common::FSNode::kListAll)) {
		MessageDialog alert(_("ScummVM couldn't open the specified directory!"));
		alert.runModal();
		return false;
	}

	//check that there is no file with the remote directory's name in the local one
	for (Common::FSList::iterator i = files.begin(); i != files.end(); ++i) {
		if (i->getName().equalsIgnoreCase(remoteDirectory.name())) {
			//if there is, ask user whether it's OK
			if (!i->isDirectory()) {
				GUI::MessageDialog alert(_("Cannot create a directory to download - the specified directory has a file with the same name."), _("OK"));
				alert.runModal();
				return false;
			}
			GUI::MessageDialog alert(
				Common::String::format(_("The \"%s\" already exists in the specified directory.\nDo you really want to download files into that directory?"), remoteDirectory.name().c_str()),
				_("Yes"),
				_("No")
				);
			if (alert.runModal() != GUI::kMessageOK)
				return false;
			break;
		}
	}

	//make a local path
	Common::String localPath = dir.getPath();

	//simple heuristic to determine which path separator to use
	if (localPath.size() && localPath.lastChar() != '/' && localPath.lastChar() != '\\') {
		int backslashes = 0;
		for (uint32 i = 0; i < localPath.size(); ++i)
			if (localPath[i] == '/')
				--backslashes;
			else if (localPath[i] == '\\')
				++backslashes;

		if (backslashes > 0)
			localPath += '\\' + remoteDirectory.name();
		else
			localPath += '/' + remoteDirectory.name();
	} else {
		localPath += remoteDirectory.name();
	}

	CloudMan.startDownload(remoteDirectory.path(), localPath);
	CloudMan.setDownloadTarget(this);
	_localDirectory = localPath;
	return true;
}

void DownloadDialog::handleTickle() {
	if (_close) {
		if (_launcher)
			_launcher->doGameDetection(_localDirectory);
		close();
		_close = false;
		return;
	}

	int32 progress = (int32)(100 * CloudMan.getDownloadingProgress());
	if (_progressBar->getValue() != progress) {
		refreshWidgets();
		draw();
	}

	Dialog::handleTickle();
}

void DownloadDialog::reflowLayout() {
	Dialog::reflowLayout();
	refreshWidgets();
}

namespace {
Common::String getHumanReadableBytes(uint64 bytes, Common::String &unitsOut) {
	Common::String result = Common::String::format("%lu", bytes);
	unitsOut = "B";

	if (bytes >= 1024) {
		bytes /= 1024;
		result = Common::String::format("%lu", bytes);
		unitsOut = "KB";
	}

	double floating = bytes;

	if (bytes >= 1024) {
		bytes /= 1024;
		floating /= 1024.0;
		unitsOut = "MB";
	}

	if (bytes >= 1024) {
		bytes /= 1024;
		floating /= 1024.0;
		unitsOut = "GB";
	}

	if (bytes >= 1024) { // woah
		bytes /= 1024;
		floating /= 1024.0;
		unitsOut = "TB";
	}

	// print one digit after floating point
	result = Common::String::format("%.1f", floating);
	return result;
}
}

Common::String DownloadDialog::getSizeLabelText() {
	Common::String downloaded, downloadedUnits, total, totalUnits;
	downloaded = getHumanReadableBytes(CloudMan.getDownloadBytesNumber(), downloadedUnits);
	total = getHumanReadableBytes(CloudMan.getDownloadTotalBytesNumber(), totalUnits);
	return Common::String::format(_("Downloaded %s %s / %s %s"), downloaded.c_str(), _(downloadedUnits.c_str()), total.c_str(), _(totalUnits.c_str()));
}

Common::String DownloadDialog::getSpeedLabelText() {
	Common::String speed, speedUnits;
	speed = getHumanReadableBytes(CloudMan.getDownloadSpeed(), speedUnits);
	speedUnits += "/s";
	return Common::String::format(_("Download speed: %s %s"), speed.c_str(), _(speedUnits.c_str()));
}

void DownloadDialog::refreshWidgets() {
	_localDirectory = CloudMan.getDownloadLocalDirectory();
	_remoteDirectoryLabel->setLabel(_("From: ") + CloudMan.getDownloadRemoteDirectory());
	_localDirectoryLabel->setLabel(_("To: ") + _localDirectory);
	uint32 progress = (uint32)(100 * CloudMan.getDownloadingProgress());
	_percentLabel->setLabel(Common::String::format("%u %%", progress));
	_downloadSizeLabel->setLabel(getSizeLabelText());
	_downloadSpeedLabel->setLabel(getSpeedLabelText());
	_progressBar->setValue(progress);
}

} // End of namespace GUI
