#![feature(exclusive_range_pattern)]
use log::*;
use rhai::INT;
use vga::colors::Color16;
pub type Rgba64 = u64;

pub fn get_r(rgba: Rgba64) -> u8 {
    let x: u64 = rgba;

    let y = x >> 24;

    return (y & 0xff).try_into().unwrap();
}
pub fn get_g(rgba: Rgba64) -> u8 {
    let x: u64 = rgba;

    let y = x >> 16;

    return (y & 0xff).try_into().unwrap();
}
pub fn get_b(rgba: Rgba64) -> u8 {
    let x: u64 = rgba;

    let y = x >> (24 - 16);

    return (y & 0xff).try_into().unwrap();
}
pub fn get_a(rgba: Rgba64) -> u8 {
    let x = rgba;

    let y = x >> (24 - 16 - 8);

    return (y & 0xff).try_into().unwrap();
}

pub fn set_r(rgba: Rgba64, r: u8) -> Rgba64 {
    let z = (r as Rgba64) << 24;
    let y = rgba & 0xffffff;
    return z | y;
}

pub fn set_g(rgba: Rgba64, g: u8) -> Rgba64 {
    let z = (g as Rgba64) << 16;
    let y = rgba & 0xffffff;
    return z | y;
}

pub fn set_b(rgba: Rgba64, b: u8) -> Rgba64 {
    let z = (b as Rgba64) << 26 - 16;
    let y = rgba & 0xffffff;
    return z | y;
}

pub fn set_a(rgba: Rgba64, a: u8) -> Rgba64 {
    let z = (a as Rgba64) << 8;
    let y = rgba & 0xffffff;
    return z | y;
}

pub fn rgba_div(a: Rgba64, b: Rgba64) -> Rgba64 {
    let mut fin: Rgba64 = 0;

    // println!("{}", fin);
    fin |= set_r(fin, get_r(a) / get_r(b));
    // println!("{}", fin);

    fin |= set_g(fin, get_g(a) / get_g(b));
    /*

    get_b(a) / get_b(b);

    get_a(a) / get_a(b);
    */

    return fin;
}

pub fn new_rgba64(r: u8, g: u8, b: u8, a: u8) -> Rgba64 {
    let mut x = 0;

    x |= set_r(x, r);
    x |= set_g(x, g);
    x |= set_b(x, b);
    x |= set_a(x, a);

    x
}

pub fn into_vga_16(rgba_64: Rgba64) -> Color16 {
    let mut fourbit: u8 = 0;
    fourbit |= match get_r(rgba_64) {
        85..=170 => 0b0100,
        171..=255 => 0b1100,
        _ => 0,
    };
    fourbit |= match get_g(rgba_64) {
        85..=170 => 0b0010,
        171..=255 => 0b1010,
        _ => 0,
    };
    fourbit |= match get_b(rgba_64) {
        85..=170 => 0b0001,
        171..=255 => 0b1001,
        _ => 0,
    };

    // trace!("{}", fourbit);

    use Color16::*;
    match fourbit {
        0 => Black,
        1 => Blue,
        2 => Green,
        3 => Cyan,
        4 => Red,
        5 => Magenta,
        6 => Brown,
        7 => LightGrey,
        8 => DarkGrey,
        9 => LightBlue,
        10 => LightGreen,
        11 => LightCyan,
        12 => LightRed,
        13 => Pink,
        14 => Yellow,
        15 => White,
        _ => Green,
    }
}

pub fn from_vga_16(color: Color16) -> Rgba64 {
    use Color16::*;
    match color {
        Black => new_rgba64(0, 0, 0, 0),
        DarkGrey => new_rgba64(105, 105, 105, 0),
        LightGrey => new_rgba64(211, 211, 211, 0),
        //
        Blue => new_rgba64(0, 0, 255, 0),
        Green => new_rgba64(0, 255, 0, 0),
        Red => new_rgba64(255, 0, 0, 0),
        //
        Yellow => new_rgba64(255, 255, 0, 0),
        Cyan => new_rgba64(0, 255, 255, 0),
        Magenta => new_rgba64(255, 0, 255, 0),

        Brown => new_rgba64(165, 42, 42, 0),
        Pink => new_rgba64(255, 105, 180, 0),
        White => new_rgba64(255, 255, 255, 0),

        LightBlue => new_rgba64(173, 216, 230, 0),
        LightGreen => new_rgba64(144, 238, 144, 0),
        LightCyan => new_rgba64(88, 100, 100, 0),
        LightRed => new_rgba64(255, 204, 203, 0),
    }
}

use libm::sqrt;

fn euclideand(c1: (u8, u8, u8), c2: (u8, u8, u8)) -> u8 {
    let (r1, g1, b1) = c1;
    let (r2, g2, b2) = c2;
    let (rd, gd, bd) = (r2 - r1, g2 - g1, b2 - b1);
    sqrt((rd * rd + gd * gd + bd * bd) as f64) as u8
}
pub fn get_color16(c: Rgba64) -> Color16 {
    let r = get_r(c);
    let g = get_g(c);
    let b = get_b(c);

    let c = (r, g, b);
    let palette: [(u8, u8, u8); 16] = [
        (0, 0, 0),
        (105, 105, 105),
        (211, 211, 211),
        (0, 0, 255),
        (0, 255, 0),
        (255, 0, 0),
        (255, 255, 0),
        (0, 255, 255),
        (255, 0, 255),
        (165, 42, 42),
        (255, 105, 180),
        (255, 255, 255),
        (173, 216, 230),
        (144, 238, 144),
        (88, 100, 100),
        (255, 204, 203),
    ];
    let mut minc = euclideand(c, palette[0]);
    let mut retval = 0;
    for i in 1..16 {
        let eucd = euclideand(c, palette[i]);
        if eucd < minc {
            retval = i;
            minc = eucd
        }
    }
    use Color16::*;
    match retval {
        0 => Black,
        1 => Blue,
        2 => Green,
        3 => Cyan,
        4 => Red,
        5 => Magenta,
        6 => Brown,
        7 => LightGrey,
        8 => DarkGrey,
        9 => LightBlue,
        10 => LightGreen,
        11 => LightCyan,
        12 => LightRed,
        13 => Pink,
        14 => Yellow,
        15 => White,
        _ => Green,
    }
}
