#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "configuration.h"
#include "mpicomm.h"
#include "simulation.h"
#include "random.h"

// ---------------------------------------------------- Function delcarations
#ifdef DEBUG_ATTACH
#include <unistd.h>
void wait_for_debugger();
#endif

void run_simulation(const conf_t *configuration);

void print_simulation_info(const sim_t *simulation, const char* header, FILE *f);

// ==========================================================================

int main(int argc, char *argv[])
{
   int mpi_rank, nprocs;
   const char* error;

   MPI_Init(&argc, &argv);

   MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
   MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);

   conf_t configuration;
   conf_init(&configuration);

   if(mpi_rank == 0) {
      // parse comand line args on root process only
      error = conf_set_from_args(&configuration, argc, argv, nprocs);
      if(error) {
         fprintf(stderr, "%s\n", error);
         MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
      }
   }
   mpi_broadcast_configuration(&configuration);

   if(info_enabled(&configuration)) {
      conf_print(&configuration, stdout);
   }

#ifdef DEBUG_ATTACH
   if(mpi_rank == configuration.debug_rank) {
      wait_for_debugger();
   }
#endif

   run_simulation(&configuration);

   MPI_Finalize();

   return EXIT_SUCCESS;
}

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

void run_simulation(const conf_t *configuration)
{
   sim_t s;
   int i, n, rank;

   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   set_random_seed((rank + 17) * (int)time(NULL));

   sim_init(&s, configuration, MPI_COMM_WORLD);

   print_simulation_info(&s, "Simulation status (initial):", stdout);

   n = configuration->n_iterations;
   for(i = 0; i < n; i++) {
      if(trace_enabled(configuration)) {
         printf("Iteration %d\n", i);
      }
      sim_calc_forces(&s);
      sim_move_particles(&s);
      sim_communicate_particles(&s);
   }

   print_simulation_info(&s, "Simulation satus (final):", stdout);
}

void print_simulation_info(const sim_t *s, const char* header, FILE *f)
{
   if(s->configuration->verbosity_level >= INFO) {
      int rank;
      sim_info_t info;
      MPI_Comm_rank(MPI_COMM_WORLD, &rank);

      sim_info(s, &info);
      if(rank == 0) {
         fprintf(f, "\n%s\n", header);
         fprintf(f, " * Number of particles (total):  %ld\n", info.n_particles_total);
         fprintf(f, " * Particles per cell (min-max): %d - %d\n", info.min_particles_per_cell, info.max_particles_per_cell);
         fprintf(f, " * Max. velocity:                %f\n", info.max_velocity); 
      }
   }
}

#ifdef DEBUG_ATTACH
void wait_for_debugger()
{
   int rank;
   volatile int resume = 0;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);

   printf("Rank %d (pid %d) waiting for debugger to attach...\n", rank, getpid());
   while(!resume) {
      sleep(1);
   }
}
#endif

