#include <math.h>

#include "glut.h"
#include "arith.h"

using namespace std;


void init(void)
{
    glClearColor(0.0,0.0,0.0,0.0);
}

int windowWidth = 500, windowHeight = 500;

const int cntSpheres = 1;
const sphere Spheres[cntSpheres] =
{
    {
        {233.0, 290.0, 0.0},
        100.0,
        0
    }
};
const int cntMaterials = 1;
const material Materials[cntMaterials] =
{
    {
        0.5,
        1.0, 1.0, 1.0
    }
};

const int cntLights = 2;
const light Lights[cntLights] =
{
    {
        {0.0, 240.0, -100.0},
        1.0, 1.0, 1.0
    },
    {
        {640.0, 240.0, -10000.0},
        0.6, 0.7, 1.0
    }
};

bool hitSphere(const ray &r, const sphere &s, float &t)
{
    vecteur dist = s.pos - r.start;
    float B = r.dir * dist;
    float D = B*B - dist * dist + s.size * s.size;
    if (D < 0.0f)
        return false;
    float t0 = B - sqrtf(D);
    float t1 = B + sqrtf(D);
    bool retvalue = false;
    if ((t0 > 0.1f) && (t0 < t))
    {
        t = t0;
        retvalue = true;
    }
    if ((t1 > 0.1f) && (t1 < t))
    {
        t = t1;
        retvalue = true;
    }
    return retvalue;
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);


    for (int y = 0; y < windowHeight; ++y)
    {
        for (int x = 0; x < windowWidth; ++x)
        {
            float red = 0, green = 0, blue = 0;
            float coef = 1.0f;
            int level = 0;

            ray viewRay = { {float(x), float(y), -1000.0f}, { 0.0f, 0.0f, 1.0f}};
            do
            {
                float t = 2000.0f;
                int currentSphere= -1;
                for (int i = 0; i < cntSpheres; ++i)
                {
                    if (hitSphere(viewRay, Spheres[i], t))
                    {
                        currentSphere = i;
                    }
                }

                if (currentSphere == -1) break;

                point newStart = viewRay.start + t * viewRay.dir;
                vecteur n = newStart - Spheres[currentSphere].pos;
                float temp = n * n;
                if (temp == 0.0f) break;

                temp = 1.0f / sqrtf(temp);
                n = temp * n;

                material currentMat = Materials[Spheres[currentSphere].materialId];
                for (int j = 0; j < cntLights; ++j)
                {
                    light current = Lights[j];
                    vecteur dist = current.pos - newStart;
                    if (n * dist <= 0.0f) continue;

                    float t = sqrtf(dist * dist);
                    if ( t <= 0.0f ) continue;

                    ray lightRay;
                    lightRay.start = newStart;
                    lightRay.dir = (1/t) * dist;

                    bool inShadow = false;
                    for (int i = 0; i < cntSpheres; ++i)
                    {
                        if (hitSphere(lightRay, Spheres[i], t))
                        {
                            inShadow = true; break;
                        }
                    }

                    if (!inShadow)
                    {
                        // lambert
                        float lambert = (lightRay.dir * n) * coef;
                        red += lambert * current.red * currentMat.red;
                        green += lambert * current.green * currentMat.green;
                        blue += lambert * current.blue * currentMat.blue;
                    }
                }

                coef *= currentMat.reflection;
                float reflet = 2.0f * (viewRay.dir * n);
                viewRay.start = newStart;
                viewRay.dir = viewRay.dir - reflet * n;

                level++;
            }
            while ((coef > 0.0f) && (level < 10));

            glPointSize(1.0);
            glBegin(GL_POINTS);
                glColor3f(red, green, blue);
                glVertex2i(x, y);
            glEnd();
        }
    }
    glFlush();
}

void reshape(int w, int h)
{
    windowWidth = w; windowHeight = h;
    glViewport(0,0,(GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-windowWidth, windowWidth, -windowHeight, windowHeight);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char **argv)
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH);
    glutInitWindowSize(windowWidth, windowHeight);
    glutInitWindowPosition(100,100);
    glutCreateWindow("Rendering a Lit Sphere");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}