/*
 * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#include <algorithm>
#include <filesystem>

#include <ox/std/std.hpp>

#include "project.hpp"

namespace nostalgia::studio {

Project::Project(ox::FileSystem *fs, ox::String path) noexcept: m_path(std::move(path)), m_typeStore(fs), m_fs(fs) {
	oxTracef("nostalgia::studio", "Project: {}", m_path);
	buildFileIndex();
}

ox::Error Project::create() noexcept {
	std::error_code ec;
	std::filesystem::create_directory(m_path.toStdString(), ec);
	return OxError(ec.value(), "PassThroughFS: mkdir failed");
}

ox::FileSystem *Project::romFs() noexcept {
	return m_fs;
}

ox::Error Project::mkdir(const ox::String &path) const noexcept {
	oxReturnError(m_fs->mkdir(path.c_str(), true));
	fileUpdated.emit(path);
	return OxError(0);
}

ox::Result<ox::FileStat> Project::stat(const ox::String &path) const noexcept {
	return m_fs->stat(path.c_str());
}

bool Project::exists(const ox::String &path) const noexcept {
	return m_fs->stat(path.c_str()).error == 0;
}

const ox::Vector<ox::String> &Project::fileList(const char *ext) noexcept {
	return m_fileExtFileMap[ext];
}

void Project::buildFileIndex() noexcept {
	auto [files, err] = listFiles();
	if (err) {
		oxLogError(err);
		return;
	}
	m_fileExtFileMap.clear();
	std::sort(files.begin(), files.end());
	for (const auto &file : files) {
		if (!file.beginsWith("/.nostalgia/")) {
			indexFile(file);
		}
	}
}

void Project::indexFile(const ox::String &path) noexcept {
	const auto [ext, err] = fileExt(path);
	if (err) {
		return;
	}
	m_fileExtFileMap[ext].emplace_back(path);
}

ox::Error Project::writeBuff(const ox::String &path, const ox::Buffer &buff) noexcept {
	const auto newFile = m_fs->stat(path.c_str()).error != 0;
	oxReturnError(m_fs->write(path.c_str(), buff.data(), buff.size()));
	if (newFile) {
		fileAdded.emit(path);
		indexFile(path);
	} else {
		fileUpdated.emit(path);
	}
	return OxError(0);
}

ox::Result<ox::Buffer> Project::loadBuff(const ox::String &path) const noexcept {
	return m_fs->read(path.c_str());
}

ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, const ox::String &path) const noexcept {
	oxRequire(files, m_fs->ls(path.c_str()));
	for (const auto &name : files) {
		const auto fullPath = path + "/" + name.c_str();
		oxRequire(stat, m_fs->stat(fullPath.c_str()));
		switch (stat.fileType) {
			case ox::FileType::NormalFile:
				paths->push_back(fullPath);
				break;
			case ox::FileType::Directory:
				oxReturnError(lsProcDir(paths, fullPath));
				break;
			case ox::FileType::None:
				break;
		}
	}
	return OxError(0);
}

ox::Result<ox::Vector<ox::String>> Project::listFiles(const ox::String &path) const noexcept {
	ox::Vector<ox::String> paths;
	oxReturnError(lsProcDir(&paths, path));
	return paths;
}

}
