#version 450

#include "types.glsl"

layout (binding = 0) readonly buffer A {A_TYPE data_a[];};   // columns: [K_OC, T_in]
layout (binding = 1) writeonly buffer D {D_TYPE data_d[];};  // output:  [T_out, OC]

layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;

layout (push_constant) uniform parameter {
    uint32_t T_out;
    uint32_t OC;
    uint32_t K_OC;
    uint32_t T_in;
    uint32_t K;
    int32_t  stride;
    int32_t  p0;
} p;

// Load A_TYPE to float
float load_col(uint32_t idx) {
#if defined(DATA_A_BF16)
    return bf16_to_fp32(uint32_t(data_a[idx]));
#else
    return float(data_a[idx]);
#endif
}

// Store float as D_TYPE
void store_dst(uint32_t idx, float v) {
#if defined(DATA_A_BF16)
    data_d[idx] = D_TYPE(fp32_to_bf16(v));
#else
    data_d[idx] = D_TYPE(v);
#endif
}

void main() {
    const uint32_t t_out = gl_GlobalInvocationID.x;
    const uint32_t oc    = gl_GlobalInvocationID.y;
    if (t_out >= p.T_out || oc >= p.OC) return;

    const int32_t t_abs = int32_t(t_out) + p.p0; // absolute position in uncropped signal

    // Gather: only the ceil(K/stride) columns that scatter into t_abs, no modulo
    int32_t t_in_min = (t_abs - int32_t(p.K) + p.stride) / p.stride;
    if (t_in_min < 0) t_in_min = 0;
    int32_t t_in_max = t_abs / p.stride;
    if (t_in_max >= int32_t(p.T_in)) t_in_max = int32_t(p.T_in) - 1;

    float val = 0.0;
    for (int32_t t_in = t_in_min; t_in <= t_in_max; t_in++) {
        int32_t k = t_abs - t_in * p.stride;
        // col layout: [K_OC, T_in], column index = oc * K + k
        uint32_t col_idx = (oc * p.K + uint32_t(k)) + uint32_t(t_in) * p.K_OC;
        val += load_col(col_idx);
    }

    // dst layout: [T_out, OC], element (t_out, oc) = t_out + oc * T_out
    store_dst(t_out + oc * p.T_out, val);
}
