/*
 * Copyright 2015 - 2025 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/.
 */

#if defined(OX_USE_STDLIB)

#include <iomanip>
#include <iostream>

static const auto OxPrintTrace = std::getenv("OXTRACE") != nullptr;
#endif

#include <ox/std/strops.hpp>

#include <ox/std/math.hpp>
#include <ox/std/stringview.hpp>
#include <ox/std/types.hpp>

#define REG_MGBA_DEBUG_ENABLE *reinterpret_cast<volatile uint16_t*>(0x4FFF780)
#define REG_MGBA_DEBUG_FLAGS  *reinterpret_cast<volatile uint16_t*>(0x4FFF700)
#define REG_MGBA_DEBUG_STRING (reinterpret_cast<char*>(0x4FFF600))

inline void nullLog(ox::StringViewCR) {}
inline void (*infoLog)(ox::StringViewCR)  = nullLog;
inline void (*debugLog)(ox::StringViewCR) = nullLog;
inline void (*errorLog)(ox::StringViewCR) = nullLog;

namespace mgba {

enum LogChan {
	Fatal = 0,
	Error = 1,
	Warn  = 2,
	Info  = 3,
	Debug = 4,
};

OX_ALLOW_UNSAFE_BUFFERS_BEGIN

template<LogChan chan>
static void log(ox::StringViewCR str) {
	const auto sz = ox::min<std::size_t>(0x100, str.bytes());
	ox::strncpy(REG_MGBA_DEBUG_STRING, str.data(), sz);
	REG_MGBA_DEBUG_FLAGS = chan | 0x100;
}

void initConsole() {
	REG_MGBA_DEBUG_ENABLE = 0xC0DE;
	if (REG_MGBA_DEBUG_ENABLE == 0x1DEA) {
		infoLog  = log<LogChan::Info>;
		debugLog = log<LogChan::Info>; // use INFO because mGBA disables DEBUG on start
		errorLog = log<LogChan::Info>;
	}
}

}

extern "C" {

void oxTraceInitHook() {
}

void oxTraceHook([[maybe_unused]] const char *file, [[maybe_unused]] int line,
                 [[maybe_unused]] const char *ch, [[maybe_unused]] const char *msg) {
#if defined(OX_USE_STDLIB)
	auto const chv = ox::StringView{ch};
	if (OxPrintTrace) {
		auto m = std::string_view{msg};
		if (m.ends_with('\n')) {
			m = std::string_view{msg, m.size() - 1};
		}
		std::cout << std::setw(53) << std::left << ch << "| ";
		std::cout << std::setw(65) << std::left << m << '|';
		std::cout << " " << file << ':' << line << "\n";
	} else if (chv == "debug" || chv == "info") {
		printf("%s\n", msg);
		std::ignore = fflush(stdout);
	} else if (chv == "stdout") {
		printf("%s", msg);
		std::ignore = fflush(stdout);
	} else if (chv == "stderr") {
		std::ignore = fprintf(stderr, "%s", msg);
		std::ignore = fflush(stderr);
	} else if (chv == "error") {
		//std::cerr << "\033[31;1;1mERROR:\033[0m (" << file << ':' << line << "): " << msg << '\n';
		std::ignore = fprintf(stderr, "\033[31;1;1mERROR:\033[0m (%s:%d): %s\n", file, line, msg);
		std::ignore = fflush(stderr);
	}
#else
	if (ox::strcmp(ch, "info") == 0) {
		infoLog(msg);
	} else if (ox::strcmp(ch, "debug") == 0) {
		debugLog(msg);
	} else if (ox::strcmp(ch, "stdout") == 0) {
		infoLog(msg);
	} else if (ox::strcmp(ch, "stderr") == 0) {
		errorLog(msg);
	} else if (ox::strcmp(ch, "error") == 0) {
		errorLog(msg);
	}
#endif
}

OX_ALLOW_UNSAFE_BUFFERS_END

}

