#![no_std]
#![allow(unexpected_cfgs)]
#![cfg_attr(target_arch = "spirv", no_std)]

use gpu_shared::RayMarchingPushConstants;
use heapless::Vec;
use spirv_std::glam::{self, Vec4, vec3};
use spirv_std::glam::{IVec3, Vec3};
use spirv_std::num_traits::{clamp, signum};
use spirv_std::spirv;

#[spirv(vertex)]
pub fn main_vs(
    #[spirv(position)] out_pos: &mut glam::Vec4,
    #[spirv(vertex_index)] idx: i32,
    #[spirv(push_constant)] constants: &RayMarchingPushConstants,
)
{
    let cube_vertices = [
        // Face 1
        Vec3::new(0., 0., 0.),
        Vec3::new(0., 0., 1.),
        Vec3::new(1., 0., 1.),
        Vec3::new(1., 0., 0.),
        // Face 2
        Vec3::new(0., 1., 0.),
        Vec3::new(0., 1., 1.),
        Vec3::new(1., 1., 1.),
        Vec3::new(1., 1., 0.),
    ];

    #[rustfmt::skip]
    let cube_faces = [
        1, 0, 2, 3,

        4, 5, 7, 6,

        0, 1, 4, 5,
        1, 2, 5, 6,
        2, 3, 6, 7,
        3, 0, 7, 4
    ];

    let quad_index = (idx as usize) / (3 * 2);
    let trig_index = (idx as usize) % (3 * 2);
    let triangle_map = [0, 1, 2, 1, 3, 2];

    let vertex = cube_vertices[cube_faces[quad_index * 4 + triangle_map[trig_index]]];
    *out_pos = (constants.view_projection * constants.transform)
        * Vec4::new(vertex.x, vertex.y, vertex.z, 1.);
}

#[spirv(fragment)]
pub fn main_fs(output: &mut glam::Vec4)
{
    *output = v[0];
}

pub fn traverse(
    max_depth: u32,
    root_color: Vec4,
    root_subdivided: bool,
    ray_origin: Vec3,
    ray_dir: Vec3,
) -> Vec4
{
    const N: u32 = 4;
    let normal = max_mask3(eye_pos) * signum(ray_dir);
    if max_depth == 0 || !root_subdivided
    {
        return root_color;
    }

    let node_tile_index = 0;
    let mut stack = [0, 0, 0, 0];
    let voxel_scale = N * N * N;
    let scale_exp = 3;

    const VOXEL_SCALE_LUT: [u32; 4] = [1, N, N * N, N * N * N];

    let dist = 1. / ray_dir;
    let origin = ray_origin * (N * N * N * N) as f32;
    let mut pos = origin;
    let offset = -origin * dist;
    let wall_offset = Vec3::select(ray_dir > Vec3::splat(0.), Vec3::splat(1.), Vec3::splat(0.));
    let step = IVec3::select(ray_dir > Vec3::splat(0.), IVec3::splat(1), IVec3::splat(-1));
    let mut voxel_pos = clamp(
        IVec3::from(pos.floor()),
        IVec3::splat(0),
        IVec3::splat((N * N * N * N) as i32 - 1),
    );

    for _ in 0..256
    {
        let mut child_pos = voxel_pos >> IVec3::splat(scale_exp * 2);
        let mut local_child_pos = child_pos & IVec3::splat(3);
        let mut child_index = local_child_pos.x + local_child_pos.y * N + local_child_pos.z * N * N;
    }

    return Vec4::new(1., 0., 1., 1.);
}

pub fn max_mask3(x: glam::Vec3)
{
    let max = x.max_element();
    glam::Vec3::new(
        if x.x == max { 1. } else { 0. },
        if x.y == max { 1. } else { 0. },
        if x.z == max { 1. } else { 0. },
    );
}
