/***************************************************************************
 *   Copyright (C) 2006 by Harish Kukreja                                  *
 *   harish.kukreja@gmail.com                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

//
// C++ Interface: Universe
//
// Description: Definitions for Physiks' 'Universe' class and its members
//
//
// Author: Harish Kukreja <harish.kukreja@gmail.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//


#include "universe.h"
#include "objects.h"
#include "computations.h"

#include <iostream>

using namespace Physiks;

// Universe constructors -----------------------------
Physiks::Universe::Universe(const u_int dim, PHYSCALAR tstep) : 
  dimension(dim), output(false), timestep(tstep) {}

//-----------------------------------------------------------------------------

Physiks::Universe::~Universe() {
  for (u_int i = 0; i < objects.size(); i++) {
    delete objects[i];
    delete forces[i];
  }
}

void Physiks::Universe::add_object(const PhysicalObject &obj) {
  objects.push_back(new PhysicalObject(obj));
  forces.push_back(new Vector(dimension, 0.0));
}

void Physiks::Universe::step(PHYSCALAR tstep) {
  /* Testing for equality isn't so bad in this case, since 0.0 has an exact representation on
     almost every architecture.  */
  tstep = (tstep == 0.0 ? timestep : tstep);
  for (u_int i = 0; i < forces.size(); i++) Vector::set_zero(*forces[i]);

  update_forces();
  euler_update_velocities(tstep);
  euler_update_positions(tstep);
}

void Physiks::Universe::integrate(const u_long numsteps) {
  for (u_long i = 0; i < numsteps; i++) step(timestep);
}

PhysicalObject *Physiks::Universe::object(const u_int index) const {
  return (index < objects.size()) ? objects[index] : NULL;
}

void Physiks::Universe::update_forces() {
  Vector temp(dimension, 0.0);
  for (u_int i = 0; i < objects.size(); i++) {
    /* Temporary. A way to disable computation of Coulomb and gravitational force must be
       offered. */
    for (u_int k = i + 1; k < objects.size(); k++) {
      temp.set(Computation::gravitational_force_vector(*objects[i], *objects[k]));
      *forces[i] += temp;
      *forces[k] -= temp; //  Every action has an equal and opposite reaction...
    }
  }
}

void Physiks::Universe::euler_update_velocities(const PHYSCALAR tstep) {
  for (u_int i = 0; i < objects.size(); i++)
    objects[i]->velocity += tstep * ((1 / objects[i]->mass) * (*forces[i]));
}

void Physiks::Universe::euler_update_positions(const PHYSCALAR tstep) {
  for (u_int i = 0; i < objects.size(); i++)
    objects[i]->position += tstep * objects[i]->velocity;
}
