use std::{
    fs::{read_to_string, File},
    io::Write,
};

use image::{GenericImageView, ImageReader};

const FULL_CHARSET: &[u8] = b" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n\t";

fn main() {
    golfed();
    // source code conversion
    let source_text = read_to_string("../Assets/test/quine.shader").unwrap();
    let mut encoded_text: Vec<u32> = Vec::new();
    let mut temp = 0;
    let mut i = 0;
    let mut blob_start = 0;
    for (index, char) in source_text.chars().enumerate() {
        if char == '?' {
            blob_start = index;
        }

        temp <<= 8;
        let char_index = FULL_CHARSET
            .iter()
            .position(|&c| c == char as u8)
            .unwrap_or_else(|| {
                println!("char {char:?} not in charset");
                0
            });
        temp |= (char_index as u32) & 0xff;
        i += 1;
        if i == 4 {
            i = 0;
            encoded_text.push(temp);
            temp = 0;
        }
    }
    if i > 0 {
        temp <<= (4 - i) * 8;
        encoded_text.push(temp);
    }
    let mut out_string = String::new();
    out_string += "\t\tconst uint text[] = {";
    for t in &encoded_text {
        out_string += &format!("{t:#010x},");
    }
    out_string += "};\n";
    out_string += &format!("\t\tconst uint text_len = {};\n", encoded_text.len());
    out_string += &format!("\t\tconst uint blob_start = {blob_start};\n");

    // font
    let img = ImageReader::open("vgafont.png").unwrap().decode().unwrap();
    let mut encoded_font = Vec::new();
    for &c in FULL_CHARSET {
        let col = c as u32 & 0b1_1111;
        let row = (c as u32 >> 5) & 0b111;
        let mut encoded: [u32; 4] = [0; 4];
        for section in 0..4 {
            for y in 0..4 {
                for x in 0..8 {
                    let px = col * 9 + x;
                    let py = row * 16 + (16 - 1 - section * 4) - y;
                    let pixel = (img.get_pixel(px, py).0[0] != 0) as u32;
                    let offset = x + ((y & 3) << 3);
                    encoded[section as usize] |= pixel << offset;
                }
            }
        }
        encoded_font.push(encoded);
    }

    out_string += &format!("\n\t\tconst uint font[{}][4] = {{", encoded_font.len());
    for c in encoded_font {
        out_string += &format!("{{{},{},{},{}}},", c[0], c[1], c[2], c[3]);
    }
    out_string.pop();
    out_string += "};\n";
    let mut file = File::create("out.h").unwrap();
    file.write_all(out_string.as_bytes()).unwrap();
}

fn golfed() {
    // source code conversion
    // let source_text = read_to_string("../Assets/test/quine_golf.shader").unwrap();
    let source_text = read_to_string("../Assets/test/QuineSurface.shader").unwrap();
    let mut encoded_text: Vec<u32> = Vec::new();
    let mut temp = 0;
    let mut i = 0;
    let mut blob_start = 0;
    let mut used_charset: Vec<_> = " 0123456789abcdefx,".chars().collect();
    for (index, char) in source_text.chars().enumerate() {
        if char == '?' && blob_start == 0 {
            blob_start = index;
            continue;
        }
        temp *= 80;
        let char_index = used_charset
            .iter()
            .position(|&c| c == char)
            .unwrap_or_else(|| {
                used_charset.push(char);
                used_charset.len() - 1
            });
        temp += char_index as u32;
        i += 1;
        if i == 5 {
            i = 0;
            encoded_text.push(temp);
            temp = 0;
        }
    }
    if i > 0 {
        for _ in 0..(5 - i) {
            temp *= 80;
        }
        encoded_text.push(temp);
    }
    let mut out_string = String::new();
    out_string += "const uint t[]={";
    for t in &encoded_text {
        out_string += &format!("{t:#010x},");
    }
    out_string.pop();
    out_string += "}\n";
    out_string += &format!("b={blob_start};\n");
    out_string += &format!(
        "uint magic_number_L_plus_b= {};\n",
        encoded_text.len() * 11 + blob_start - 1
    );
    out_string += &format!("uint magic_number_L= {};\n", encoded_text.len() * 11 - 1);

    // font
    let img = ImageReader::open("6x6.png").unwrap().decode().unwrap();
    let mut encoded_font = Vec::new();
    for c in used_charset {
        let col = c as u32 & 0b1_1111;
        let row = (c as u32 >> 5) & 0b111;
        let mut encoded = 0u32;
        for y in 0..6 {
            for x in 0..5 {
                let px = col * 6 + x;
                let py = row * 6 + 5 - y;
                let pixel = (img.get_pixel(px, py).0[0] > 40) as u32;
                let offset = x + y * 5;
                encoded |= pixel << offset;
            }
        }
        encoded_font.push(encoded);
    }

    out_string += "const uint R[]={";
    for c in encoded_font {
        out_string += &format!("{c},");
    }
    out_string.pop();
    out_string += "};\n";
    let mut file = File::create("out_golfed.h").unwrap();
    file.write_all(out_string.as_bytes()).unwrap();
}
