struct VertexInput {
    @location(0) position: vec3f,
    @location(1) texcoords: vec2f,
    @location(2) normal: vec3f,
}

struct VertexOutput {
    @builtin(position) clipPosition: vec4f,
    @location(0) position: vec3f,
    @location(1) texcoords: vec2f,
    @location(2) normal: vec3f,
}

struct FragmentInput {
    @location(0) position: vec3f,
    @location(1) texcoords: vec2f,
    @location(2) normal: vec3f,
}

struct FragmentOutput {
    @location(0) color: vec4f,
}

struct CameraUniforms {
    viewMatrix: mat4x4f,
    projectionMatrix: mat4x4f,
    cameraPosition: vec3f,
}

struct ModelUniforms {
    modelMatrix: mat4x4f,
    normalMatrix: mat3x3f,
}

struct MaterialUniforms {
    baseFactor: vec4f,
    specularFactor: f32,
    shininess: f32,
}

struct SpotLightUniforms {
    position: vec3f,
    direction: vec3f,
    cutoffAngle: f32,
    ambient: f32,
    intensity: f32,
}

@group(0) @binding(0) var<uniform> camera: CameraUniforms;
@group(1) @binding(0) var<uniform> model: ModelUniforms;
@group(2) @binding(0) var<uniform> material: MaterialUniforms;
@group(2) @binding(1) var baseTexture: texture_2d<f32>;
@group(2) @binding(2) var baseSampler: sampler;
@group(3) @binding(0) var<uniform> spotLight: SpotLightUniforms;

@vertex
fn vertex(input: VertexInput) -> VertexOutput {
    var output: VertexOutput;

    output.clipPosition = camera.projectionMatrix * camera.viewMatrix * model.modelMatrix * vec4(input.position, 1);

    output.position = (model.modelMatrix * vec4(input.position, 1)).xyz;
    output.texcoords = input.texcoords;
    output.normal = model.normalMatrix * input.normal;

    return output;
}

@fragment
fn fragment(input: FragmentInput) -> FragmentOutput {
    var output: FragmentOutput;

    let N = normalize(input.normal);
    let L = normalize(spotLight.position - input.position);
    let V = normalize(camera.cameraPosition - input.position);
    let H = normalize(L + V);

    // Spotlight factor based on cutoff angle
    let lightDir = normalize(spotLight.direction);
    let theta = dot(L, -lightDir);
    let spotlightFactor = select(0.0, 1.0, theta > spotLight.cutoffAngle);

    // Diffuse and Specular components
    let lambert = max(dot(N, L), 0) * spotlightFactor;
    let specular = pow(max(dot(N, H), 0), material.shininess) * spotlightFactor;

    let materialColor = textureSample(baseTexture, baseSampler, input.texcoords) * material.baseFactor;
    let lambertFactor = vec4(vec3(lambert), 1);
    let specularFactor = vec4(vec3(specular * material.specularFactor), 1);
    let ambientFactor = vec4(vec3(spotLight.ambient), 1);

    // Final color
//    output.color = materialColor * ((lambertFactor + specularFactor) + specularFactor * spotLight.intensity);
//    output.color = spotLight.intensity * (lambertFactor + specularFactor) + ambientFactor;
    output.color = spotlightFactor * materialColor;

    return output;
}