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

#if defined(OX_USE_STDLIB)

#include <iomanip>
#include <iostream>

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

#include "strops.hpp"

#include "math.hpp"
#include "stringview.hpp"
#include "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::CRStringView) {}
inline void (*infoLog)(ox::CRStringView)  = nullLog;
inline void (*debugLog)(ox::CRStringView) = nullLog;
inline void (*errorLog)(ox::CRStringView) = nullLog;

namespace mgba {

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

template<LogChan chan>
static void log(ox::CRStringView 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)
	if (OxPrintTrace) {
		std::cout << std::setw(53) << std::left << ch << "| ";
		std::cout << std::setw(65) << std::left << msg << '|';
		std::cout << " " << file << ':' << line << "\n";
	} else if (ox_strcmp(ch, "debug") == 0 || ox_strcmp(ch, "info") == 0) {
		printf("%s\n", msg);
		fflush(stdout);
	} else if (ox_strcmp(ch, "stdout") == 0) {
		printf("%s", msg);
		fflush(stdout);
	} else if (ox_strcmp(ch, "stderr") == 0) {
		printf("%s", msg);
		fflush(stdout);
	} else if (ox_strcmp(ch, "error") == 0) {
		//std::cerr << "\033[31;1;1mERROR:\033[0m (" << file << ':' << line << "): " << msg << '\n';
		fprintf(stderr, "\033[31;1;1mERROR:\033[0m (%s:%d): %s\n", file, line, msg);
		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
}

}

