#include <GL/glew.h>
#include <GLFW/glfw3.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.hpp"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.hpp"

#include <fstream>
#include <vector>
#include <iostream>

std::string loadFile(const char* path)
{
    std::ifstream f(path);
    return std::string((std::istreambuf_iterator<char>(f)),
                       std::istreambuf_iterator<char>());
}

GLuint compile(GLenum t, const std::string& src)
{
    GLuint s = glCreateShader(t);
    const char* c = src.c_str();
    glShaderSource(s, 1, &c, NULL);
    glCompileShader(s);

    GLint ok = 0;
    glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
    if (!ok) {
        char log[1024];
        glGetShaderInfoLog(s, sizeof(log), NULL, log);
        std::cerr << "shader compile failed:\n" << log;
    }
    return s;
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);

    GLFWwindow* win = glfwCreateWindow(1,1,"",NULL,NULL);
    glfwMakeContextCurrent(win);
    glewInit();

    // ---------------- LOAD IMAGES ----------------
    int w,h;
    unsigned char* img1 = stbi_load("frame1.jpg",&w,&h,NULL,4);
    unsigned char* img2 = stbi_load("frame2.jpg",&w,&h,NULL,4);

    if(!img1 || !img2)
    {
        std::cerr << "image load failed\n";
        return 1;
    }

    // ---------------- TEXTURES ----------------
    GLuint tex[2];
    glGenTextures(2, tex);
    GLuint texA = tex[0], texB = tex[1];

    auto upload = [&](GLuint t, unsigned char* d)
    {
        glBindTexture(GL_TEXTURE_2D,t);
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,d);

        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
    };

    upload(texA,img1);
    upload(texB,img2);

    // ---------------- SHADER ----------------
    std::string frag = loadFile("motion.frag");

    const char* vs =
        "#version 330 core\n"
        "layout(location=0) in vec2 pos;\n"
        "out vec2 uv;\n"
        "void main(){\n"
        "uv = pos*0.5+0.5;\n"
        "gl_Position = vec4(pos,0,1);\n"
        "}";

    GLuint v = compile(GL_VERTEX_SHADER,vs);
    GLuint f = compile(GL_FRAGMENT_SHADER,frag);

    GLuint prog = glCreateProgram();
    glAttachShader(prog,v);
    glAttachShader(prog,f);
    glLinkProgram(prog);

    GLint linked = 0;
    glGetProgramiv(prog, GL_LINK_STATUS, &linked);
    if (!linked) {
        char log[1024];
        glGetProgramInfoLog(prog, sizeof(log), NULL, log);
        std::cerr << "program link failed:\n" << log;
        return 1;
    }

    // ---------------- QUAD (FIXED) ----------------
    GLuint vao,vbo;

    float quad[] = {
        -1,-1,
         1,-1,
        -1, 1,
         1, 1
    };

    glGenVertexArrays(1,&vao);
    glBindVertexArray(vao);

    glGenBuffers(1,&vbo);
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
    glBufferData(GL_ARRAY_BUFFER,sizeof(quad),quad,GL_STATIC_DRAW);

    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,0);
    glEnableVertexAttribArray(0);

    // ---------------- FBO ----------------
    GLuint fbo,outTex;
    glGenFramebuffers(1,&fbo);
    glGenTextures(1,&outTex);

    glBindTexture(GL_TEXTURE_2D,outTex);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);

    glBindFramebuffer(GL_FRAMEBUFFER,fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,outTex,0);

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        std::cerr << "FBO broken\n";
        return 1;
    }

    // ---------------- RENDER ----------------
    glUseProgram(prog);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,texA);
    glUniform1i(glGetUniformLocation(prog,"texA"),0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,texB);
    glUniform1i(glGetUniformLocation(prog,"texB"),1);

    glViewport(0,0,w,h);
    glBindFramebuffer(GL_FRAMEBUFFER,fbo);

    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    // ---------------- READ BACK ----------------
    std::vector<unsigned char> out(w*h*4);
    glReadPixels(0,0,w,h,GL_RGBA,GL_UNSIGNED_BYTE,out.data());

    stbi_write_jpg("frameO.jpg",w,h,4,out.data(),90);
    stbi_image_free(img1);
    stbi_image_free(img2);

    std::cout << "done\n";
}
