/* 
 * This application is an emulation of the game of life automaton
 * It must be deployed from processor 0 and use contiguous processor
 * (example 0,1,2,3) 
 */


#include "stdio.h"
#include "limits.h"
#include "barrier.h"
#include "hard_config.h"
#include "mapping_info.h"

#define WIDTH           128
#define HEIGHT          128
#define NB_CLUSTER_MAX  256
#define NB_ITERATION    1000000000

#define PRINTF(...) ({ if ( proc_id==0) { giet_tty_printf(__VA_ARGS__); } })

giet_barrier_t barriers[2];

unsigned int init_ok = 1;

#define NEW 0
#define OLD 1

typedef unsigned char uint8_t;
typedef unsigned int size_t;

uint8_t world[2][HEIGHT][WIDTH];
uint8_t world_yuv[HEIGHT][WIDTH];

/* Generate binary values for world between base_line and base_line + nb_line */
void init_world(size_t base_line, size_t nb_line)
{
   size_t x,y;
   for (y = base_line ; y < base_line + nb_line; y++){
      for(x = 0; x < WIDTH ; x++) {
         // TODO OPTIMIZE RANDOM INIT
         world[OLD][y][x] = giet_rand() % 2;  
      }
   }
}

uint8_t number_of_alive_neigh(size_t x, size_t y)
{
   uint8_t nb = 0;

   nb += world[OLD][(y - 1) % HEIGHT][(x - 1) % WIDTH];
   nb += world[OLD][ y              ][(x - 1) % WIDTH];
   nb += world[OLD][(y + 1) % HEIGHT][(x - 1) % WIDTH];
   nb += world[OLD][(y - 1) % HEIGHT][ x             ];
   nb += world[OLD][(y + 1) % HEIGHT][ x             ];
   nb += world[OLD][(y - 1) % HEIGHT][(x + 1) % WIDTH];
   nb += world[OLD][ y              ][(x + 1) % WIDTH];
   nb += world[OLD][(y + 1) % HEIGHT][(x + 1) % WIDTH];

   return nb;
}

/* Compute cell x,y */
uint8_t compute_cell(size_t x, size_t y)
{
   uint8_t nb_neighbours_alive = number_of_alive_neigh(x,y);
   if (world[OLD][y][x] == 1) {
      if (nb_neighbours_alive == 2 ||
          nb_neighbours_alive == 3) 
      {
         return 1;   
      }
   }
   else {
      if (nb_neighbours_alive == 3) {
         return 1;
      }
      else {
         return world[OLD][y][x];
      }
   }
   return 0;
}

//////////////////////////////////////////////////////
void compute_new_gen(size_t base_line, size_t nb_line)
{
   size_t x,y;
   for (y = base_line; y < base_line + nb_line; y++)
   {
      for(x = 0; x < WIDTH ; x++) 
      {
         world[NEW][y][x] = compute_cell(x,y);  
      }
   }
}

////////////////////////////////////////////////////
void display_world(size_t base_line, size_t nb_line)
{
   size_t x,y;
   for (y = base_line; y < base_line + nb_line; y++){
      for(x = 0; x < WIDTH ; x++) {
         //world_yuv[y][x] = world[NEW][y][x]*100;  
         world[NEW][y][x] = world[NEW][y][x]*255;  
      }
   }

   giet_fb_sync_write( base_line * WIDTH , 
                       &world[NEW][base_line][0], 
                       nb_line * WIDTH);
}

/////////////////////////////////////////////////////
void grow_old_world(size_t base_line, size_t nb_line)
{
   size_t x,y;
   for (y = base_line; y < base_line + nb_line; y++)
   {
      for(x = 0; x < WIDTH ; x++) 
      {
         world[OLD][y][x] = world[NEW][y][x];  
      }
   }
}

////////////////////////////////////////
__attribute__((constructor)) void main()
{
   // get processor identifier
   unsigned int x;
   unsigned int y;
   unsigned int p;
   giet_proc_xyp( &x, &y, &p );

   // compute continuous processor index
   unsigned int proc_id = (((x * Y_SIZE) + y) * NB_PROCS_MAX) + p;  

   unsigned int nlocal_procs  = NB_PROCS_MAX;               // processors per cluster
   unsigned int nclusters     = X_SIZE*Y_SIZE;              // number of clusters
   unsigned int nglobal_procs = nclusters * nlocal_procs;   // number of processors
   size_t i;

   size_t       nb_line       = HEIGHT / nglobal_procs;
   size_t       base_line     = nb_line * proc_id; 
   
   PRINTF("*** Starting init at cycle %d ***\n", giet_proctime());

   //  barriers initialization
   if ( proc_id == 0 )
   {
      barrier_init(&barriers[0], nglobal_procs);
      barrier_init(&barriers[1], nglobal_procs);

      init_ok = 0;
   }
   else
   {
      while ( init_ok == 1 );
   }

   init_world(base_line, nb_line);

   PRINTF("*** Completing init at cycle %d ***\n", giet_proctime());
   barrier_wait(&barriers[0]);
   
   for (i = 0; i < NB_ITERATION; i++)
   {
      compute_new_gen(base_line, nb_line);
      grow_old_world(base_line, nb_line);
      display_world(base_line, nb_line);
      barrier_wait(&barriers[1]);
      barrier_init(&barriers[1], nglobal_procs);
   }

   PRINTF("*** End of main at cycle %d ***\n", giet_proctime());

   giet_exit("Completed");
} // end main()

// Local Variables:
// tab-width: 3
// c-basic-offset: 3
// c-file-offsets:((innamespace . 0)(inline-open . 0))
// indent-tabs-mode: nil
// End:

// vim: filetype=cpp:expandtab:shiftwidth=3:tabstop=3:softtabstop=3



