#include "Gra.h"
#include "tbb/task_scheduler_init.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include "tbb/parallel_reduce.h"
#include "tbb/partitioner.h"
#include "tbb/tick_count.h"


using namespace tbb;

class Ewolucja {
   char *const m_biezace;
   char *const m_nastepne;
   int const m_rozmiar;
public:
   void operator()(const blocked_range<size_t>& zakres) const {
      for (size_t i = zakres.begin(); i != zakres.end(); ++i)
         m_nastepne[i] = komorka(i, m_biezace, m_rozmiar);
   }
   Ewolucja(char *biezace, char *nastepne, int rozmiar) :
	     m_biezace(biezace), m_nastepne(nastepne), m_rozmiar(rozmiar)
   {}
};

class Sumowanie {
public:
   int suma;
	  void operator()(const blocked_range<char *>& zakres) {
      for (char *i = zakres.begin(); i != zakres.end(); ++i)
         if (*i == 1) ++suma;
      }
   Sumowanie() : suma(0) 
   {}
   Sumowanie(const Sumowanie& s, split) : suma(0)
   {}
   void join(const Sumowanie& s) {
      suma += s.suma;
   }
};

void kolejnePokolenie(char* biezace, char *nastepne, int rozmiar, double& czas, int& ilosc)
{
   task_scheduler_init init;
   int ileWatkow = task_scheduler_init::default_num_threads();
   tick_count t0, t1;

   t0 = tick_count::now();
	
   if (ileWatkow >= 1) {
      // wersja równoległa
      Ewolucja e(biezace, nastepne, rozmiar);
      parallel_for(blocked_range<size_t>(0, rozmiar * rozmiar), e, auto_partitioner());
	  Sumowanie s;
	  parallel_reduce(blocked_range<char *>(nastepne, nastepne + rozmiar * rozmiar - 1), 
		  s, auto_partitioner());
	  ilosc = s.suma;
   } else {
      // wersja szeregowa
	  ilosc = 0;
	  for (int i = 0; i < rozmiar * rozmiar; ++i) {
         nastepne[i] = komorka(i, biezace, rozmiar);
		 if (nastepne[i]) ++ilosc;
	  }
   }
   t1 = tick_count::now();
   czas = (t1 - t0).seconds();
}

char komorka(int k, char *const biezace, int const rozmiar)
{
   int i = k / rozmiar,
      j = k % rozmiar,
      suma = 0, 
      im1 = (i == 0) ? rozmiar - 1 : i - 1,
      ip1 = (i == rozmiar - 1) ? 0 : i + 1,
      jm1 = (j == 0) ? rozmiar - 1 : j - 1,
      jp1 = (j == rozmiar - 1) ? 0 : j + 1;

   if (biezace[im1 * rozmiar + jm1]) ++suma;	// i-1, j-1
   if (biezace[im1 * rozmiar + j]) ++suma;	// i-1, j
   if (biezace[im1 * rozmiar + jp1]) ++suma;	// i-1, j+1
   if (biezace[i * rozmiar + jm1]) ++suma;	// i, j-1
   if (biezace[i * rozmiar + jp1]) ++suma;	// i, j+1
   if (biezace[ip1 * rozmiar + jm1]) ++suma;	// i+1, j-1
   if (biezace[ip1 * rozmiar + j]) ++suma;	// i+1, j
   if (biezace[ip1 * rozmiar + jp1]) ++suma;	// i+1, j+1

   if (biezace[i * rozmiar + j] == 0 && suma == 3)
      return 1;
   else if (biezace[i * rozmiar + j] == 1 && (suma == 2 || suma == 3))
      return 1;
   return 0;
}
