#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> 


#include "parc.h"

using namespace parc;

module cell {
public:
  signal<int> *neigbor[8],   // pointers to neighbor values
               state;        // local value, > 0 is alive
  int          ns;           // next state
 
  c_signal<bool> *clk;       // pointer to clock 

  process p1 {
  start:
    for (;;) {
      @(clk);                // wait for clock edge
      for (int i=ns=0; i < 8; i++) { // count live neighbors
        if (neigbor[i] && neigbor[i]->Value() > 0) ns++;
      }
      if (state.Value() > 0) { // evaluate next state
        if (ns < 2 || ns > 3) ns = -ns;
      } else {
        if (3 != ns)          ns = -ns;
      }
      state @= ns;  // non-blocking assignment to signal
    }
  } a;
};

template <int N,int M>
module top {            // cell array
  c_signal<bool> clk;   // global clock
  int            i,j;   

  cell.arr[N][M](clk=&clk);      // create cell array

  void post_bind() {             // post array creation mods
    for(i = 0; i < N; i++) {
      for(j = 0; j < M; j++) {
        cell &c(*arr[i][j]);
        memset(c.neigbor,0,sizeof(c.neigbor)); // clear pointers

        // connect neighbors
        if (i > 0)                  { c.neigbor[0] = &arr[i-1][  j]->state; }
        if (j > 0)                  { c.neigbor[1] = &arr[  i][j-1]->state; }
        if (i < (N-1))              { c.neigbor[2] = &arr[i+1][  j]->state; }
        if (j < (M-1))              { c.neigbor[3] = &arr[  i][j+1]->state; }
        if (i > 0 && j > 0)         { c.neigbor[4] = &arr[i-1][j-1]->state; }
        if (j > 0 && i < (N-1))     { c.neigbor[5] = &arr[i+1][j-1]->state; }
        if (i > 0 && j < (M-1))     { c.neigbor[6] = &arr[i-1][j+1]->state; }
        if (i < (N-1) && j < (M-1)) { c.neigbor[7] = &arr[i+1][j+1]->state; }

        *c.state.pValMod() = 1 & random() ? 8 : 0; // set initial value
      }
    }
    fprintf(stdout,"\033[2J");
  }

  void draw() { // VT100 drawing routine
    int i,j;
    fprintf(stdout,"\033[H");
    for(i = 0; i < N; i++) {
      for(j = 0; j < M; j++) {
        cell &c(*arr[i][j]);
        int   s = c.state.Value();
        fputc(s > 0 ? '0' + s : ' ',stdout);
      }
      fprintf(stdout,"\n");
    }
    sleep(1);
  }

  process sync { // draw array then clock the cells 
  start:
    for (;;) {
      draw();
      clk = clk ? false 
                : true;
      wait(1);
    }
  } c;
};

int main(int argc,char **argv)
{
  top<30,60> t; // builds and initializes

  root()->StartAll(); // run all the processes
}