//
// Created by ary on 29.07.2026.
//
#include <stdio.h>
#ifdef _WIN32
    #include <windows.h>
    #define SLEEP_SECONDS(s) Sleep((s) * 1000)
#else
    #include <unistd.h>
    #define SLEEP_SECONDS(s) sleep(s)
#endif

#include "interface.h"
#include "examples.h"
#include "structs.h"
#include "vectorOp.h"
#include "raytrace.h"
#include <stdlib.h>
#include "helper.h"
#include <pthread.h>
#define WORKTHREADSIZE 32

void* renderPixel(RenderArgs *arg, int pixelId) {
    RenderArgs *args = (RenderArgs*)arg;
    int step = pixelId * args->channels;
    int x = pixelId % args->width;
    int y = pixelId / args->width;
    RGBA pixelColor;

    vec3 *pixelOrigin = (vec3 *)malloc(sizeof(vec3));
    *pixelOrigin = vec3Add(*args->screenTopLeft,
        vec3Add(
            vec3Scale(*args->vecPixelStepX, x),
            vec3Scale(*args->vecPixelStepY, y)
            )
    );

    ray *lightRay = (ray *)malloc(sizeof(ray));;
    lightRay->origin = args->cam1->origin;

    lightRay->direction = vec3Normalize(vec3Subtract(*pixelOrigin, args->cam1->origin));

    pixelColor = getPixelColor(args->objectList,args->objectCount, lightRay);
    free(pixelOrigin);
    free(lightRay);

    (*args->image)[step] = (int)(pixelColor.r * 255.0);
    (*args->image)[step+1] = (int)(pixelColor.g * 255.0);
    (*args->image)[step+2] = (int)(pixelColor.b * 255.0);
    if (args->channels == 4) {
        (*args->image)[step+3] = 255;
    }
    return NULL;
}

void *workerLoop(void *arg) {
    RenderArgs *args = (RenderArgs*)arg;

    while (1) {
        pthread_mutex_lock(&args->mutexKey);
        int pixelId = args->nextPixelId;
        args->nextPixelId++;
        pthread_mutex_unlock(&args->mutexKey);

        if (pixelId >= args->pixelCount) break;

        renderPixel(args, pixelId);
    }
    return NULL;
}

void* monitorProgress(void *arg) {
    MonitorArgs *args = (MonitorArgs*)arg;

    while (1){
        pthread_mutex_lock(args->mutexKey);
        int pixelId = *args->nextPixelId;
        pthread_mutex_unlock(args->mutexKey);
        if (pixelId >= args->pixelCount) break;
        double progress = 100.0 * pixelId / args->pixelCount;
        printf("progress: %lf%%\n", progress);
        SLEEP_SECONDS(1);
    }
    return NULL;
}

void displaySzene(object objectList[], int objectCount, camera cam1, int width, int height, int channels, unsigned char **image) {
    const vec3 worldUp = (vec3){0, 1, 0};
    vec3 screenX = vec3Cross(worldUp, cam1.direction);
    vec3 screenY = vec3Cross(screenX, cam1.direction);
    vec3 screenMiddlePoint = vec3Add(cam1.origin, vec3Scale(cam1.direction, cam1.screenDistance));
    vec3 screenTopLeft = vec3Add(
        vec3Add(screenMiddlePoint,
            vec3Scale(screenY, -cam1.height/2.0)),///////////////////////////////////////////////////////////////////////////////////////////////////////////////aendern minus weg
        vec3Scale(screenX, -cam1.width/2.0)
        );
    double pixelStepX = cam1.width / cam1.resolutionX;
    double pixelStepY = cam1.height / cam1.resolutionY;
    vec3 vecPixelStepX = vec3Scale(screenX, pixelStepX);
    vec3 vecPixelStepY = vec3Scale(screenY, pixelStepY);

    pthread_t *threadIdList = (pthread_t *)malloc(sizeof(pthread_t) * WORKTHREADSIZE);



        RenderArgs *args = (RenderArgs*)malloc(sizeof(RenderArgs));
        *args = (RenderArgs){objectList, objectCount, width, channels, &screenTopLeft, &vecPixelStepX, &vecPixelStepY, &cam1, image, 0, 0,width * height};
        pthread_mutex_init(&args->mutexKey, NULL);
        //pthread_t threadId;
        for (int i = 0; i < WORKTHREADSIZE; i++) {
            pthread_create(&(threadIdList[i]), NULL, workerLoop, args);
        }
    pthread_t monitorThread;
    pthread_create(&monitorThread, NULL, monitorProgress, (void*)(&(MonitorArgs){&args->nextPixelId, args->pixelCount, &args->mutexKey}));


        //renderPixel(i, objectList, objectCount, width, channels, &screenTopLeft, &vecPixelStepX, &vecPixelStepY, &cam1, image);

    for (int i = 0; i < WORKTHREADSIZE; i++) {
        pthread_join(threadIdList[i], NULL);
    }
    pthread_join(monitorThread, NULL);
    pthread_mutex_destroy(&args->mutexKey);
    free(threadIdList);
    free(args);
}