




#define EGL_NO_X11

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <assert.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/eglext.h>

#include <stdio.h>
#include <stdlib.h>

#define WINDOW_WIDTH 768
#define WINDOW_HEIGHT 512

EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;
GLuint program_object;
GLuint texture;
struct wl_compositor *compositor = NULL;

static void initEGL(EGLNativeWindowType native_window) {
    EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
    EGLConfig config;
    EGLint num_configs;
    EGLint major, minor;

    egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(egl_display, &major, &minor);
    eglBindAPI(EGL_OPENGL_ES_API);

    EGLint attrib_list[] = {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE};

    eglChooseConfig(egl_display, attrib_list, &config, 1, &num_configs);
    egl_surface = eglCreateWindowSurface(egl_display, config, native_window, NULL);
    egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, context_attribs);

    eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
}
static void registry_global(void* data, struct wl_registry* registry, uint32_t id, const char* interface, uint32_t version)
{
    if (strcmp(interface, "wl_compositor") == 0) {
        compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
    }
}
void load_texture(const char *filename) {
    // Load the texture here
     FILE *file = fopen(filename, "rb");
    if (!file) {
        fprintf(stderr, "Failed to open file: %s\n", filename);
        exit(1);
    }

    unsigned char header[54];
    fread(header, sizeof(unsigned char), 54, file);

    int width = *(int*)&header[18];
    int height = *(int*)&header[22];
    int size = 3 * width * height;
    unsigned char *data = (unsigned char *)malloc(size);
    fread(data, sizeof(unsigned char), size, file);
    fclose(file);

    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    //fclose(file);
    free(data);
}
GLuint LoadShader(GLenum type, const char* shaderSrc)
{
    GLuint shader = glCreateShader(type);
    assert(shader);

    glShaderSource(shader, 1, &shaderSrc, NULL);
    glCompileShader(shader);

    GLint compiled;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    assert(compiled);

    return shader;
}
GLuint initProgramObject()
{
    char vShaderStr[] = "#version 300 es                          \n"
                        "layout(location = 0) in vec4 vPosition;  \n"
                        "void main()                              \n"
                        "{                                        \n"
                        "   gl_Position = vPosition;              \n"
                        "}                                        \n";

    char fShaderStr[] = "#version 300 es                              \n"
                        "precision mediump float;                     \n"
                        "out vec4 fragColor;                          \n"
                        "void main()                                  \n"
                        "{                                            \n"
                        "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
                        "}                                            \n";

    GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);

    GLuint programObject = glCreateProgram();
    assert(programObject);

    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);

    glLinkProgram(programObject);

    GLint linked;
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);
    assert(linked);

    return programObject;
}
static void draw() {
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // Draw your OpenGL content here
    glViewport(0, 0, 768, 512);

    // Bind the texture
    glBindTexture(GL_TEXTURE_2D, texture);

    // Specify the texture coordinates and vertices for a quad
    GLfloat texCoords[] = {0.0f, 0.0f,
                           1.0f, 0.0f,
                           1.0f, 1.0f,
                           0.0f, 1.0f};

    GLfloat vertices[] = {-1.0f, -1.0f,
                           1.0f, -1.0f,
                           1.0f, 1.0f,
                           -1.0f, 1.0f};

    // Enable vertex and texture coordinate arrays
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    // Specify the texture coordinates
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
    // Specify the vertices
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertices);

    // Draw the quad
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    // Disable vertex and texture coordinate arrays
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    // Swap the buffers
    eglSwapBuffers(egl_display, egl_surface);
}
static const struct wl_registry_listener registry_listener = { registry_global, NULL };
int main(int argc, char **argv) {
    struct wl_display *display = wl_display_connect(NULL);
    assert(display);

    struct wl_registry *registry = wl_display_get_registry(display);
    wl_registry_add_listener(registry, &registry_listener, NULL);

    wl_display_dispatch(display);
    wl_display_roundtrip(display);

    assert(compositor);
    //assert(shell);

    struct wl_surface *surface = wl_compositor_create_surface(compositor);
    assert(surface);

    struct wl_egl_window *egl_window = wl_egl_window_create(surface, WINDOW_WIDTH, WINDOW_HEIGHT);
    assert(egl_window);

    initEGL((EGLNativeWindowType)egl_window);

    program_object = initProgramObject();

    // Load texture and other initialization tasks can be added here
    printf("Loading texture...\n");
    load_texture(argv[1]);
    while (1) {
        draw();
        printf("after draw\n");
        /*int ret = wl_display_dispatch(display);
        printf("ret value %d\n",wl_display_dispatch(display));
        if (ret == -1)
            break; // Error occurred
        */
        
    }

    eglDestroySurface(egl_display, egl_surface);
    eglDestroyContext(egl_display, egl_context);
    eglTerminate(egl_display);

    wl_egl_window_destroy(egl_window);
    wl_surface_destroy(surface);
    wl_registry_destroy(registry);
    wl_display_disconnect(display);

    return 0;
}

