/*
 * 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 https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <ox/mc/read.hpp>
#ifdef OX_USE_STDLIB
#include <ox/oc/read.hpp>
#endif
#include <ox/std/buffer.hpp>
#include <ox/std/string.hpp>

#include "format.hpp"

namespace ox {

constexpr auto Error_ClawTypeMismatch = 200;
constexpr auto Error_ClawTypeVersionMismatch = 201;

struct ClawHeader {
	String typeName;
	int typeVersion = -1;
	ClawFormat fmt = ClawFormat::None;
	const char *data = nullptr;
	std::size_t dataSize = 0;
};

Result<ClawHeader> readClawHeader(const char *buff, std::size_t buffLen) noexcept;

Result<ClawHeader> readClawHeader(const ox::Buffer &buff) noexcept;

Result<Buffer> stripClawHeader(const char *buff, std::size_t buffLen) noexcept;

Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept;

template<typename T>
Error readClaw(const char *buff, std::size_t buffLen, T *val) {
	oxRequire(header, readClawHeader(buff, buffLen));
	if (header.typeName != getModelTypeName<T>()) {
		return OxError(Error_ClawTypeMismatch, "Claw Read: Type mismatch");
	}
	if (header.typeVersion != getModelTypeVersion<T>()) {
		return OxError(Error_ClawTypeVersionMismatch, "Claw Read: Type Version mismatch");
	}
	switch (header.fmt) {
		case ClawFormat::Metal:
		{
			MetalClawReader reader(reinterpret_cast<const uint8_t*>(header.data), header.dataSize);
			ModelHandlerInterface handler(&reader);
			return model(&handler, val);
		}
		case ClawFormat::Organic:
		{
#ifdef OX_USE_STDLIB
			OrganicClawReader reader(header.data, header.dataSize);
			return model(&reader, val);
#else
			break;
#endif
		}
		case ClawFormat::None:
			return OxError(1);
	}
	return OxError(1);
}

template<typename T>
Result<T> readClaw(const char *buff, std::size_t buffLen) {
	T val;
	oxReturnError(readClaw(buff, buffLen, &val));
	return val;
}

template<typename T>
Error readClaw(const Buffer &buff, T *val) {
	return readClaw<T>(buff.data(), buff.size(), val);
}

template<typename T>
Result<T> readClaw(const Buffer &buff) {
	return readClaw<T>(buff.data(), buff.size());
}

Result<ModelObject> readClaw(TypeStore *ts, const Buffer &buff) noexcept;

}
