/*****************************************************************
  gravorbits.cpp
  by Dave Pape
  3 March 2003

 
*****************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <dms/dms.h>

void drawEverything(void);
void key(unsigned char k, int x, int y);
void idle(void);
void initializePlanets(void);
dms::Vector3 computeGravity(int i);


#define GRAV_CONSTANT 1

typedef struct
    {
    dms::Vector3 position;
    dms::Vector3 velocity;
    float mass;
    dms::Vector3 gravityForce;
    dms::Object * object;
    dms::SimpleTransform xform;
    } movingObject_t;

#define NUM_PLANETS 4

movingObject_t planets[NUM_PLANETS];


struct { float pos[3], vel[3], mass, radius; dms::Vector4 color; }
    initData[NUM_PLANETS] = 
        {
         { {0,0,0}, {0,0,0}, 1000, 2.0, dms::Color::Yellow },
         { {10, 0, 0}, {0, 10, 0}, 10, 0.5, dms::Color::Green },
         { {-14, 0, 0}, {0, -9, 0}, 5, 0.35, dms::Color::Cyan },
         { {-16, 0, 0}, {0, -9.2, 0}, 0.2, 0.1, dms::Color::White },
        };


dms::OrthoCamera camera(-25, 25, -25, 25, -10, 10);
dms::Light light;

bool paused = true;


int main(int argc, char *argv[])
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(512,512);
    glutCreateWindow("example");
    glutDisplayFunc(drawEverything);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);
    initializePlanets();
    glutMainLoop();
    return 0;
    }


void initializePlanets(void)
    {
    for (int i=0; i < NUM_PLANETS; i++)
        {
        planets[i].position = initData[i].pos;
        planets[i].velocity = initData[i].vel;
        planets[i].mass = initData[i].mass;
        planets[i].xform.setTranslation(planets[i].position);
        dms::QuadricObject * sphere = new dms::QuadricObject;
        sphere->makeSphere(initData[i].radius);
        sphere->setMaterial(*(new dms::Material(initData[i].color)));
        sphere->setTransform(planets[i].xform);
        planets[i].object = sphere;
        }
    }


void drawEverything(void)
    {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    camera.apply();
    light.apply();
    
    for (int i = 0; i < NUM_PLANETS; i++)
        {
        planets[i].object->drawAll();
        }
    glutSwapBuffers();
    }


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        exit(0);
    else if (k == ' ')
        paused = !paused;
    }



void idle(void)
    {
    dms::beginFrame();
    if (!paused)
        {
        for (int i=0; i < NUM_PLANETS; i++)
            planets[i].gravityForce = computeGravity(i);
        for (int i=0; i < NUM_PLANETS; i++)
            {
            dms::Vector3 acceleration = planets[i].gravityForce / planets[i].mass;
            planets[i].velocity += acceleration * dms::deltaTime();
            planets[i].position += planets[i].velocity * dms::deltaTime();
            planets[i].xform.setTranslation(planets[i].position);
            }
        }
    glutPostRedisplay();
    }


dms::Vector3 computeGravity(int i)
    {
    dms::Vector3 force(0,0,0);
    for (int j=0; j < NUM_PLANETS; j++)
        if (j != i)
            {
            float f = GRAV_CONSTANT * planets[i].mass * planets[j].mass /
                        planets[i].position.distanceSquared(planets[j].position);
            dms::Vector3 direction = planets[j].position - planets[i].position;
            direction.normalize();
            force += f * direction;
            }
    return force;
    }
