1use crate::vdp::{Cram, DarkenColors, VdpConfig};
2use bincode::{Decode, Encode};
3use jgenesis_common::frontend::Color;
4use std::ops::{Add, AddAssign};
5
6#[derive(Debug, Clone, Encode, Decode)]
7pub struct ColorTables {
8    pub normal: [u8; 8],
9    pub shadow: [u8; 8],
10    pub highlight: [u8; 8],
11}
12
13impl ColorTables {
14    // Values from http://gendev.spritesmind.net/forum/viewtopic.php?f=22&t=2188
15    pub const NON_LINEAR: Self = Self {
16        normal: [0, 52, 87, 116, 144, 172, 206, 255],
17        shadow: [0, 29, 52, 70, 87, 101, 116, 130],
18        highlight: [130, 144, 158, 172, 187, 206, 228, 255],
19    };
20
21    pub const LINEAR: Self = Self {
22        normal: [0, 36, 73, 109, 146, 182, 219, 255], // round(i * 255 / 7)
23        shadow: [0, 18, 36, 55, 73, 91, 109, 128],    // round(i * 255 / 7 / 2)
24        highlight: [128, 146, 164, 182, 200, 219, 237, 255], // round(255 / 2 + i * 255 / 7 / 2)
25    };
26
27    // All values -8 relative to NON_LINEAR
28    pub const NON_LINEAR_DARK: Self = Self {
29        normal: [0, 44, 79, 108, 136, 164, 198, 247],
30        shadow: [0, 21, 44, 62, 79, 93, 108, 122],
31        highlight: [122, 136, 150, 164, 179, 198, 220, 247],
32    };
33
34    // All values -8 relative to LINEAR
35    pub const LINEAR_DARK: Self = Self {
36        normal: [0, 28, 65, 101, 138, 174, 211, 247],
37        shadow: [0, 10, 28, 47, 65, 83, 101, 120],
38        highlight: [120, 138, 156, 174, 192, 211, 229, 247],
39    };
40
41    pub fn from_config(config: &VdpConfig) -> Self {
42        match (config.non_linear_color_scale, config.color_adjustment) {
43            (true, DarkenColors::No) => Self::NON_LINEAR,
44            (true, DarkenColors::Yes) => Self::NON_LINEAR_DARK,
45            (false, DarkenColors::No) => Self::LINEAR,
46            (false, DarkenColors::Yes) => Self::LINEAR_DARK,
47        }
48    }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum ColorModifier {
53    None,
54    Shadow,
55    Highlight,
56}
57
58impl Add for ColorModifier {
59    type Output = Self;
60
61    #[allow(clippy::unnested_or_patterns)]
62    fn add(self, rhs: Self) -> Self::Output {
63        match (self, rhs) {
64            (Self::None, Self::None)
65            | (Self::Shadow, Self::Highlight)
66            | (Self::Highlight, Self::Shadow) => Self::None,
67            (Self::None, Self::Shadow)
68            | (Self::Shadow, Self::None)
69            | (Self::Shadow, Self::Shadow) => Self::Shadow,
70            (Self::None, Self::Highlight)
71            | (Self::Highlight, Self::None)
72            | (Self::Highlight, Self::Highlight) => Self::Highlight,
73        }
74    }
75}
76
77impl AddAssign for ColorModifier {
78    fn add_assign(&mut self, rhs: Self) {
79        *self = *self + rhs;
80    }
81}
82
83#[inline]
84pub fn gen_to_rgba(
85    r: u8,
86    g: u8,
87    b: u8,
88    a: u8,
89    modifier: ColorModifier,
90    color_tables: &ColorTables,
91) -> Color {
92    let colors = match modifier {
93        ColorModifier::None => color_tables.normal,
94        ColorModifier::Shadow => color_tables.shadow,
95        ColorModifier::Highlight => color_tables.highlight,
96    };
97    Color::rgba(colors[r as usize], colors[g as usize], colors[b as usize], a)
98}
99
100pub fn resolve(cram: &Cram, palette: u8, color_id: u8) -> u16 {
101    lookup(cram, (palette << 4) | color_id)
102}
103
104pub fn lookup(cram: &Cram, cram_idx: u8) -> u16 {
105    // Use alpha bit to indicate whether pixel is transparent (needed by 32X)
106    // 32X transparency is based on color index being 0 (verified on hardware)
107    let alpha = cram_idx & 0xF != 0;
108    (cram[cram_idx as usize] & 0x7FFF) | (u16::from(alpha) << 15)
109}