/*
 * Copyright 2015 - 2022 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <cstdio>
#include <fstream>

#include <ox/fs/fs.hpp>

struct Buff {
	char *data = nullptr;
	std::size_t size = 0;
};

static ox::Result<Buff> loadFsBuff(const char *path) noexcept {
	std::ifstream file(path, std::ios::binary | std::ios::ate);
	if (!file.good()) {
		oxErrorf("Could not find OxFS file: {}", path);
		return OxError(1, "Could not find OxFS file");
	}
	try {
		const auto size = static_cast<std::size_t>(file.tellg());
		file.seekg(0, std::ios::beg);
		const auto buff = new char[size];
		file.read(buff, size);
		return Buff{buff, size};
	} catch (const std::ios_base::failure &e) {
		oxErrorf("Could not read OxFS file: {}", e.what());
		return OxError(2, "Could not read OxFS file");
	}
}

static ox::Result<ox::UniquePtr<ox::FileSystem>> loadFs(const char *path) noexcept {
	oxRequire(buff, loadFsBuff(path));
	return {ox::make_unique<ox::FileSystem32>(buff.data, buff.size)};
}

static ox::Error runLs(ox::FileSystem *fs, int argc, const char **argv) noexcept {
	if (argc < 2) {
		oxErr("Must provide a directory to ls\n");
		return OxError(1);
	}
	oxRequire(files, fs->ls(argv[1]));
	for (const auto &file : files) {
		oxOutf("{}\n", file);
	}
	return OxError(0);
}

static ox::Error runRead(ox::FileSystem *fs, int argc, const char **argv) noexcept {
	if (argc < 2) {
		oxErr("Must provide a path to a file to read\n");
		return OxError(1);
	}
	oxRequire(buff, fs->read(argv[1]));
	fwrite(buff.data(), sizeof(decltype(buff)::value_type), buff.size(), stdout);
	return OxError(0);
}

static ox::Error run(int argc, const char **argv) noexcept {
	if (argc < 2) {
		oxErr("Subcommand and OxFS file arguments are required\n");
		return OxError(1);
	}
	const auto fsPath = argv[1];
	ox::String subCmd = argv[2];
	oxRequire(fs, loadFs(fsPath));
	if (subCmd == "ls") {
		return runLs(fs.get(), argc - 2, argv + 2);
	} else if (subCmd == "read") {
		return runRead(fs.get(), argc - 2, argv + 2);
	}
	return OxError(1);
}

int main(int argc, const char **argv) {
	auto err = run(argc, argv);
	oxAssert(err, "unhandled error");
	return static_cast<int>(err);
}
