/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "export/output/export_output_file.h"

#include "export/output/export_output_result.h"
#include "export/output/export_output_stats.h"
#include "base/qt/qt_string_view.h"

#include <QtCore/QFileInfo>
#include <QtCore/QDir>

#include <gsl/gsl_util>

namespace Export {
namespace Output {

File::File(const QString &path, Stats *stats) : _path(path), _stats(stats) {
}

int64 File::size() const {
	return _offset;
}

bool File::empty() const {
	return !_offset;
}

Result File::writeBlock(const QByteArray &block) {
	const auto result = writeBlockAttempt(block);
	if (!result) {
		_file.reset();
	}
	return result;
}

Result File::writeBlockAttempt(const QByteArray &block) {
	if (_stats && !_inStats) {
		_inStats = true;
		_stats->incrementFiles();
	}
	if (const auto result = reopen(); !result) {
		return result;
	}
	const auto size = block.size();
	if (!size) {
		return Result::Success();
	}
	if (_file->write(block) == size && _file->flush()) {
		_offset += size;
		if (_stats) {
			_stats->incrementBytes(size);
		}
		return Result::Success();
	}
	return error();
}

Result File::reopen() {
	if (_file && _file->isOpen()) {
		return Result::Success();
	}
	_file.emplace(_path);
	if (_file->exists()) {
		if (_file->size() < _offset) {
			return fatalError();
		} else if (!_file->resize(_offset)) {
			return error();
		}
	} else if (_offset > 0) {
		return fatalError();
	}
	if (_file->open(QIODevice::Append)) {
		return Result::Success();
	}
	const auto info = QFileInfo(_path);
	const auto dir = info.absoluteDir();
	return (!dir.exists()
		&& dir.mkpath(dir.absolutePath())
		&& _file->open(QIODevice::Append))
		? Result::Success()
		: error();
}

Result File::error() const {
	return Result(Result::Type::Error, _path);
}

Result File::fatalError() const {
	return Result(Result::Type::FatalError, _path);
}

QString File::PrepareRelativePath(
		const QString &folder,
		const QString &suggested) {
	if (!QFile::exists(folder + suggested)) {
		return suggested;
	}

	// Not lastIndexOf('.') so that "file.tar.xz" won't be messed up.
	const auto position = suggested.indexOf('.');
	const auto base = suggested.mid(0, position);
	const auto extension = (position >= 0)
		? base::StringViewMid(suggested, position)
		: QStringView();
	const auto relativePart = [&](int attempt) {
		auto result = base + QString(" (%1)").arg(attempt);
		result.append(extension);
		return result;
	};
	auto attempt = 0;
	while (true) {
		const auto relativePath = relativePart(++attempt);
		if (!QFile::exists(folder + relativePath)) {
			return relativePath;
		}
	}
}

Result File::Copy(
		const QString &source,
		const QString &path,
		Stats *stats) {
	QFile f(source);
	if (!f.exists() || !f.open(QIODevice::ReadOnly)) {
		return Result(Result::Type::FatalError, source);
	}
	const auto bytes = f.readAll();
	if (bytes.size() != f.size()) {
		return Result(Result::Type::FatalError, source);
	}
	return File(path, stats).writeBlock(bytes);
}

} // namespace Output
} // namespace File
