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

#pragma once

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

namespace nostalgia::gfx {

using Color16 = uint16_t;

/**
 * Nostalgia logically uses 16 bit colors, but must translate that to 32
 * bit colors in some implementations.
 */
using Color32 = uint32_t;

[[nodiscard]]
constexpr Color32 toColor32(Color16 nc) noexcept {
	auto const r = static_cast<Color32>(((nc & 0b0000000000011111) >> 0) * 8);
	auto const g = static_cast<Color32>(((nc & 0b0000001111100000) >> 5) * 8);
	auto const b = static_cast<Color32>(((nc & 0b0111110000000000) >> 10) * 8);
	auto const a = static_cast<Color32>(255);
	return r | (g << 8) | (b << 16) | (a << 24);
}


[[nodiscard]]
constexpr uint8_t red16(Color16 c) noexcept {
	return c & 0b0000000000011111;
}

[[nodiscard]]
constexpr uint8_t green16(Color16 c) noexcept {
	return (c & 0b0000001111100000) >> 5;
}

[[nodiscard]]
constexpr uint8_t blue16(Color16 c) noexcept {
	return (c & 0b0111110000000000) >> 10;
}

[[nodiscard]]
constexpr uint8_t alpha16(Color16 c) noexcept {
	return static_cast<uint8_t>(c >> 15);
}

[[nodiscard]]
constexpr uint8_t red32(Color16 c) noexcept {
	return red16(c) * 8;
}

[[nodiscard]]
constexpr uint8_t green32(Color16 c) noexcept {
	return green16(c) * 8;
}

[[nodiscard]]
constexpr uint8_t blue32(Color16 c) noexcept {
	return blue16(c) * 8;
}

[[nodiscard]]
constexpr uint8_t alpha32(Color16 c) noexcept {
	return static_cast<uint8_t>((c >> 15) * 255);
}


[[nodiscard]]
constexpr uint8_t red32(Color32 c) noexcept {
	return (c & 0x000000ff) >> 0;
}

[[nodiscard]]
constexpr uint8_t green32(Color32 c) noexcept {
	return (c & 0x0000ff00) >> 8;
}

[[nodiscard]]
constexpr uint8_t blue32(Color32 c) noexcept {
	return (c & 0x00ff0000) >> 16;
}

[[nodiscard]]
constexpr Color32 color32(uint8_t r, uint8_t g, uint8_t b) noexcept {
	return static_cast<Color32>(r | (g << 8) | (b << 16));
}

[[nodiscard]]
constexpr Color32 color32(Color16 c) noexcept {
	return color32(red32(c), green32(c), blue32(c));
}

[[nodiscard]]
constexpr Color32 color32(float r, float g, float b) noexcept {
	return static_cast<Color32>(static_cast<uint8_t>(r * 255) | (static_cast<uint8_t>(g * 255) << 8) | (static_cast<uint8_t>(b * 255) << 16));
}


[[nodiscard]]
constexpr float redf(Color16 c) noexcept {
	return static_cast<float>(red16(c)) / 31.f;
}

[[nodiscard]]
constexpr float greenf(Color16 c) noexcept {
	return static_cast<float>(green16(c)) / 31.f;
}

[[nodiscard]]
constexpr float bluef(Color16 c) noexcept {
	return static_cast<float>(blue16(c)) / 31.f;
}


[[nodiscard]]
constexpr float redf(Color32 c) noexcept {
	return static_cast<float>(red32(c)) / 255.f;
}

[[nodiscard]]
constexpr float greenf(Color32 c) noexcept {
	return static_cast<float>(green32(c)) / 255.f;
}

[[nodiscard]]
constexpr float bluef(Color32 c) noexcept {
	return static_cast<float>(blue32(c)) / 255.f;
}


[[nodiscard]]
constexpr Color16 color16(int r, int g, int b, int a = 0) noexcept {
	return static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(r), 31))
	     | static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(g), 31) << 5)
	     | static_cast<Color16>(ox::min<uint8_t>(static_cast<uint8_t>(b), 31) << 10)
         | static_cast<Color16>(a << 15);
}

[[nodiscard]]
constexpr Color16 color16(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0) noexcept {
	return static_cast<Color16>(ox::min<uint8_t>(r, 31))
		| static_cast<Color16>(ox::min<uint8_t>(g, 31) << 5)
		| static_cast<Color16>(ox::min<uint8_t>(b, 31) << 10)
		| static_cast<Color16>(a << 15);
}

static_assert(color16(0, 31, 0) == 992);
static_assert(color16(16, 31, 0) == 1008);
static_assert(color16(16, 31, 8) == 9200);
static_assert(color16(16, 32, 8) == 9200);

[[nodiscard]]
constexpr Color16 applySelectionColor(Color16 const color) noexcept {
	namespace core = nostalgia::gfx;
	auto const r = core::red16(color) / 2;
	auto const g = (core::green16(color) + 20) / 2;
	auto const b = (core::blue16(color) + 31) / 2;
	return core::color16(r, g, b);
}

}
