


/* On macOS, compile with...
    clang 700mainAmbient.c 040pixel.o -lglfw -framework OpenGL -framework Cocoa -framework IOKit
On Ubuntu, compile with...
    cc 700mainAmbient.c 040pixel.o -lglfw -lGL -lm -ldl
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GLFW/glfw3.h>
#include "040pixel.h"

#include "240vector.c"
#include "280matrix.c"
#include "300isometry.c"
#include "300camera.c"
#include "700ray.c"
#include "700body.c"
#include "660sphere.c"
#include "670half.c"
#include "680and.c"
#include "140texture.c"
#include "700vanilla.c"

#define SCREENWIDTH 512
#define SCREENHEIGHT 512



/*** ARTWORK ******************************************************************/

camCamera camera;
double cameraTarget[3] = {0.0, 0.0, 0.0};
double cameraRho = 10.0, cameraPhi = M_PI / 3.0, cameraTheta = M_PI / 3.0;
texTexture texForSphere, texForHalf;
double cAmbient[3] = {0.2, 0.2, 0.2};
/* Four spheres, one half space, and one AND. */
#define BODYNUM 6
bodyBody bodies[BODYNUM];

int initializeArtwork(void) {
	/* Camera. */
    camSetProjectionType(&camera, camPERSPECTIVE);
    camSetFrustum(
        &camera, M_PI / 6.0, cameraRho, 10.0, SCREENWIDTH, SCREENHEIGHT);
    camLookAt(&camera, cameraTarget, cameraRho, cameraPhi, cameraTheta);
    /* Textures. */
    if (texInitializeFile(&texForSphere, "earth.png") != 0)
    	return 8;
    if (texInitializeFile(&texForHalf, "webdubois.png") != 0) {
    	texFinalize(&texForSphere);
    	return 7;
    }
    /* 0th body: top lens sphere. */
    if (bodyInitialize(
    		&bodies[0], sphDATASIZE, sphGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 6;
    }
    /* 1th body bottom lens sphere. */
    if (bodyInitialize(
    		&bodies[1], sphDATASIZE, sphGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		for (int i = 0; i < 1; i += 1)
			bodyFinalize(&bodies[i]);
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 5;
    }
    /* 2th body: another sphere. */
    if (bodyInitialize(
    		&bodies[2], sphDATASIZE, sphGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		for (int i = 0; i < 2; i += 1)
			bodyFinalize(&bodies[i]);
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 4;
    }
    /* 3th body: another sphere. */
    if (bodyInitialize(
    		&bodies[3], sphDATASIZE, sphGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		for (int i = 0; i < 3; i += 1)
			bodyFinalize(&bodies[i]);
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 3;
    }
    /* 4th body: AND. */
    if (bodyInitialize(
    		&bodies[4], andDATASIZE, andGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		for (int i = 0; i < 4; i += 1)
			bodyFinalize(&bodies[i]);
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 2;
    }
    /* 5th body: half space. */
    if (bodyInitialize(
    		&bodies[5], halfDATASIZE, halfGetIntersection, vanDATASIZE, 
    		vanGetMaterial) != 0) {
		for (int i = 0; i < 5; i += 1)
			bodyFinalize(&bodies[i]);
		texFinalize(&texForHalf);
		texFinalize(&texForSphere);
    	return 1;
    }
    /* Shape data. */
    double *rPtr = (double *)(bodies[0].shapeData);
    *rPtr = 5.1;
    rPtr = (double *)(bodies[1].shapeData);
    *rPtr = 5.1;
    rPtr = (double *)(bodies[2].shapeData);
    *rPtr = 1.0;
    rPtr = (double *)(bodies[3].shapeData);
    *rPtr = 2.0;
    bodyBody **bodiesArray = (bodyBody **)(bodies[4].shapeData);
    bodiesArray[0] = &bodies[0];
    bodiesArray[1] = &bodies[1];
    /* Material data. */
    texTexture **texPtr = (texTexture **)(bodies[0].materialData);
    *texPtr = &texForSphere;
    texPtr = (texTexture **)(bodies[1].materialData);
    *texPtr = &texForSphere;
    texPtr = (texTexture **)(bodies[2].materialData);
    *texPtr = &texForSphere;
    texPtr = (texTexture **)(bodies[3].materialData);
    *texPtr = &texForSphere;
    texPtr = (texTexture **)(bodies[4].materialData);
    *texPtr = &texForSphere;
    texPtr = (texTexture **)(bodies[5].materialData);
    *texPtr = &texForHalf;
    /* Positions. */
    double transl[3] = {0.0, 0.0, 5.0};
    isoSetTranslation(&(bodies[0].isometry), transl);
    vec3Set(0.0, 0.0, -5.0, transl);
    isoSetTranslation(&(bodies[1].isometry), transl);
    vec3Set(2.0, 0.0, 3.0, transl);
    isoSetTranslation(&(bodies[2].isometry), transl);
    vec3Set(0.0, 3.0, 3.0, transl);
    isoSetTranslation(&(bodies[3].isometry), transl);
    vec3Set(0.0, 0.0, 7.0, transl);
    isoSetTranslation(&(bodies[4].isometry), transl);
    return 0;
}

void finalizeArtwork(void) {
    for (int i = 0; i < BODYNUM; i += 1)
    	bodyFinalize(&bodies[i]);
	texFinalize(&texForHalf);
	texFinalize(&texForSphere);
}



/*** RENDERING ****************************************************************/

/* Given a ray x(t) = p + t u. Finds the color where that ray hits the scene (or 
the background), and outputs that color via the rgb parameter. */
void getSceneColor(const double p[3], const double u[3], double rgb[3]) {
    
}

void render(void) {
    
}



/*** USER INTERFACE ***********************************************************/

void handleKey(
        int key, int shiftIsDown, int controlIsDown, int altOptionIsDown, 
        int superCommandIsDown) {
    if (key == GLFW_KEY_I)
        cameraPhi -= 0.1;
    else if (key == GLFW_KEY_K)
        cameraPhi += 0.1;
    else if (key == GLFW_KEY_J)
        cameraTheta -= 0.1;
    else if (key == GLFW_KEY_L)
        cameraTheta += 0.1;
    else if (key == GLFW_KEY_U)
        cameraRho *= 1.1;
    else if (key == GLFW_KEY_O)
        cameraRho *= 0.9;
    else if (key == GLFW_KEY_P) {
        if (camera.projectionType == camORTHOGRAPHIC)
            camSetProjectionType(&camera, camPERSPECTIVE);
        else
            camSetProjectionType(&camera, camORTHOGRAPHIC);
    }
    camSetFrustum(
        &camera, M_PI / 6.0, cameraRho, 10.0, SCREENWIDTH, SCREENHEIGHT);
    camLookAt(&camera, cameraTarget, cameraRho, cameraPhi, cameraTheta);
}

void handleTimeStep(double oldTime, double newTime) {
    if (floor(newTime) - floor(oldTime) >= 1.0)
        printf(
            "info: handleTimeStep: %f frames/s\n", 1.0 / (newTime - oldTime));
    /* Rotate the two independent spheres. */
    double rotAxis[3] = {1.0 / sqrt(3.0), 1.0 / sqrt(3.0), 1.0 / sqrt(3.0)};
    double rotMatrix[3][3];
    mat3AngleAxisRotation(newTime, rotAxis, rotMatrix);
    for (int k = 2; k < 4; k += 1)
    	isoSetRotation(&(bodies[k].isometry), rotMatrix);
    render();
}

int main(void) {
    if (pixInitialize(SCREENWIDTH, SCREENHEIGHT, "Ray Tracing") != 0)
        return 1;
    if (initializeArtwork() != 0) {
        pixFinalize();
        return 2;
    }
    pixSetKeyDownHandler(handleKey);
    pixSetKeyRepeatHandler(handleKey);
    pixSetTimeStepHandler(handleTimeStep);
    pixRun();
    finalizeArtwork();
    pixFinalize();
    return 0;
}


