/*
 *    Copyright 2025 Jared Davis
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */


use crate::lab::Lab;
use crate::xyz::{Illumination, XYZ};

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RGB {
    pub r: f32,
    pub g: f32,
    pub b: f32,
}

impl RGB {
    pub fn from_hex(hex: &str) -> Result<RGB, std::num::ParseIntError> {
        let hex = hex.trim_start_matches("#");
        let v = u32::from_str_radix(hex, 16)?;

        Ok(RGB{
            r: ((v >> 16) & 0xFF) as f32,
            g: ((v >> 8) & 0xFF) as f32,
            b: (v & 0xFF) as f32,
        })
    }

    pub fn xyz(self, illumination: Illumination) -> XYZ {
        let r = inverse_gamma(linearize(self.r));
        let g = inverse_gamma(linearize(self.g));
        let b = inverse_gamma(linearize(self.b));

        let x = illumination.x[0] * r + illumination.x[1] * g + illumination.x[2] * b;
        let y = illumination.y[0] * r + illumination.y[1] * g + illumination.y[2] * b;
        let z = illumination.z[0] * r + illumination.z[1] * g + illumination.z[2] * b;

        XYZ { x, y, z }
    }

    pub fn lab(self, illumination: Illumination) -> Lab {
        self.xyz(illumination).lab(illumination)
    }
}

fn linearize(v: f32) -> f32 {
    f32::min(v, 255.0) / 255.0
}

fn inverse_gamma(v: f32) -> f32 {
    if v < 0.04045 {
        v * 0.0773993808
    } else {
        f32::powf((v + 0.055) * 0.9478672986, 2.4)
    }
}