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

#include "parc.h"

using namespace parc;

struct data { // used in communication to monitor
  int x,y,
      state;
};

module cell {
public:
  pipe<int>   in[8],                    // channels for input
             *out[8];                   // connection to neighbors inputs
  int         ns,                       // next state
              s[8] = {0,0,0,0,0,0,0,0}; // default neighbor state
  data        d;
  pipe<data> *report;

  process p1 {
  start:
    for (;;) {
      fork [8] if (out[$1]) { out[$1]->write(&d.state,1); } // write to all
      wait(1);
      fork [8] if (out[$1]) { in[$1].read(&s[$1],1); }      // read from all
      // evaluate next state
      ns = 0;
      for (int i = 8; i-- ; ns += (s[i] > 0)); 
      if (d.state > 0) {
        if (ns < 2 || ns > 3) ns = -ns;
      } else {
        if (3 != ns)          ns = -ns;
      }
      d.state = ns;        // update local state
      report->write(&d,1); // tell monitor
    }
  } a;
};

template <int N,int M>
module top {
  int        i,j;
  pipe<data> report;
  data       d;

  cell.arr[N][M](report=&report,d.x=$1,d.y=$2); // create cell array

  void draw(data &d) { // VT100 draw a cell
    fprintf(stdout,"\033[%d;%dH%c\033[H",
                         d.x+1,d.y+1,d.state > 0 ? '0' + d.state 
                                                 : ' ');
  }

  void post_bind() {             // create cell connections
    fprintf(stdout,"\033[2J");
    for(i = 0; i < N; i++) {
      for(j = 0; j < M; j++) {
        cell &c(*arr[i][j]);
        memset(c.out,0,sizeof(c.out));

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

        c.d.state = 1 & random() ? 8 : 0; 
        draw(c.d); // draw initial state
      }
    }
  }

  process monitor { // reports state changes from cells
  start:
    for (;;) {
      report.read(&d,1);
      draw(d);
      if (!(d.x || d.y)) sleep(1);
    }
  } c;
};

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

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