#include <stdlib.h>
#include "structs.h"
#include "collision.h"
#include "vectorOp.h"
#define MAXOBJECTS 100
#define BADPOINT (vec3){-10000000,-10000000,-10000000}
#include "helper.h"
#include <math.h>
#define epsilon 1e-8
#define RAYSPERPIXEL 10
#define lightBounces 5
#define GLOBALILLUMINATION 0.4
enum objectType {
    SPHERETYPE,
    QUADTYPE
};

RGBA getRayColor(RGBA colorList[], bool isLightList[]) {
    int firstLightHitIndex = -1;
    RGBA rayColor = {1,1,1,1};
    for (int i = 0; i <= lightBounces; i++) {
        if (isLightList[i] == 1) {
            firstLightHitIndex = i;
            break;
        }
    }

    if (firstLightHitIndex == -1) {
        //rayColor = (RGBA){GLOBALILLUMINATION * colorList[0].r,GLOBALILLUMINATION * colorList[0].g,GLOBALILLUMINATION * colorList[0].b,1};
        //rayColor = (RGBA){0,0,0,1};
        //return rayColor;
        firstLightHitIndex = lightBounces - 1;
        isLightList[firstLightHitIndex] = 1;
        colorList[firstLightHitIndex] = (RGBA){GLOBALILLUMINATION,GLOBALILLUMINATION,GLOBALILLUMINATION,1};
        //colorList[firstLightHitIndex] = (RGBA){0,0,0,1};
    }
        //pixel.r += spheresHitList[firstLightHitIndex]->color.r;
        //pixel.g += spheresHitList[firstLightHitIndex]->color.g;
        //pixel.b += spheresHitList[firstLightHitIndex]->color.b;
        //pixel.a += spheresHitList[firstLightHitIndex]->color.a;
        for (int i = firstLightHitIndex; i >= 0; i--) {
            rayColor.r = rayColor.r * colorList[i].r;
            rayColor.g = rayColor.g * colorList[i].g;
            rayColor.b = rayColor.b * colorList[i].b;
            //rayColor.a = rayColor.a * colorList[i].a;
        }

        //pixel.r = -vec3Product(sphereNormal, lightray->direction);
        //pixel = (RGBA){1 - 0.5 *( sphereNormal.x + 1), 1 - 0.5 * (sphereNormal.y +1), 1 -0.5* (sphereNormal.z + 1),1};
        return rayColor;


}

RGBA raytrace(object objectList[], int objectCount, ray *lightray) {
    RGBA objectColorList[lightBounces + 1] = {0};
    object objectHitList[lightBounces + 1] = {0};
    bool isLightList[lightBounces + 1] = {false};

     for (int j = 0; j <= lightBounces; j++) {
         object currentObject;
         object *nearestObject = nullptr;
         double nearestObjectDistance = 10000000;
         vec3 nearestCollisionPoint = (vec3){10000,10000,10000};
         double nearestCollisionPointDistance = 10000000;




         for (int i = 0; i < objectCount; i++) {
             currentObject = objectList[i];
             vec3 collisionPoint;

             switch (currentObject.objectType) {
                 case SPHERETYPE:
                     collisionPoint = collisionPointSphere(lightray, (sphere*)(objectList[i].objectPointer));
                     break;
                 case QUADTYPE:
                     collisionPoint = collisionQuad(lightray, (quad*)(objectList[i].objectPointer));
                     break;
             }

             double collisionPointDistance = 1000000;
             if (collisionPoint.x != BADPOINT.x) {
                 collisionPointDistance = vec3Length(vec3Subtract(collisionPoint, lightray->origin));
                 if (collisionPointDistance < nearestCollisionPointDistance) {
                     nearestCollisionPoint = collisionPoint;
                     nearestCollisionPointDistance = collisionPointDistance;
                     nearestObject = &(objectList[i]);
                 }
             }
         }

         vec3 objectNormal = BADPOINT;
         if (nearestObject != nullptr) {
             switch (nearestObject->objectType) {
                 case SPHERETYPE:
                     sphere *nearestSphere = (sphere*)(nearestObject->objectPointer);
                     objectColorList[j] = nearestSphere->color;
                     objectNormal = vec3Scale(vec3Subtract(nearestCollisionPoint, nearestSphere->origin), 1/(nearestSphere->radius));////////////////////////////// optimize, schreibe divide vector funktion
                     break;
                 case QUADTYPE:
                     quad *nearestQuad = (quad*)(nearestObject->objectPointer);
                     objectColorList[j] = nearestQuad->color;
                     objectNormal = nearestQuad->normal;
                     break;
             }
             objectHitList[j] = *nearestObject;
             isLightList[j] = nearestObject->isLight;

             //diffus:
             lightray->direction = vec3Normalize(vec3Add(objectNormal, randUnitVector()));  //maybe kann man sich durch umstrukturierung das normalisieren einsparen
             if (fabs(lightray->direction.x ) <= epsilon || fabs(lightray->direction.y ) <= epsilon || fabs(lightray->direction.z) <= epsilon) {
                 lightray->direction = objectNormal;
             }
             //reflektion:
             //lightray->direction = vec3Add(lightray->direction, vec3Scale(sphereNormal, 2));
             lightray->origin = nearestCollisionPoint;
         }
     }
    return getRayColor(objectColorList, isLightList);

}


RGBA getPixelColor(object objectList[], int objectCount, ray *lightray) {
    RGBA pixel = (RGBA){0,0,0,0};
    ray originalRay = *lightray;

    for (int k = 0; k < RAYSPERPIXEL; k++) {
        *lightray = originalRay;

        RGBA tempPixel = raytrace(objectList, objectCount, lightray);

        pixel.r += tempPixel.r;
        pixel.g += tempPixel.g;
        pixel.b += tempPixel.b;
        //pixel.a += tempPixel.a;
    }

    //average pixelData
    pixel.r = pixel.r / RAYSPERPIXEL;
    pixel.g = pixel.g / RAYSPERPIXEL;
    pixel.b = pixel.b / RAYSPERPIXEL;
    //pixel.a = pixel.a / raysPerPixel;


    //free(spheresHitList);
    //free(spheresNormalList);
    return pixel;
}