/* life.c - demonstrator for later Mips+LCD game of life
 *
 * 23.03.06 - MACRO-based version
 * 22.02.06 - new file
 */

//#include "ks0108.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libsys.h"


#define NX  200 // 70 // 128  // 48 // 128
#define NY  150 // 31 // 64   // 27 // 64

#define	SCREEN_X	800
#define	SCREEN_Y	600

#define	SCALE_X		(4)
#define	SCALE_Y		(4)
#define	OFFSET_X	((SCREEN_X-(SCALE_X*NX))/2)
#define	OFFSET_Y	((SCREEN_Y-(SCALE_Y*NY))/2)

// OCCUPIED must be uneven, EMPTY even.
#define OCCUPIED 0x77771111
#define EMPTY    0xeeee0000

#define MATRIX( row, col )  (*(matrix + (NX*col) + row ))
#define FUTURE( row, col )  (*(future + (NX*col) + row ))

UINT32 *g_gx_buff;

int clock_lfsr32()
{
  return rand();
}


/* return a pseudo-random integer in the range 0..1023 */
int random1023() {
  int tmp;

  tmp = clock_lfsr32();
  return tmp & 0x000003ff;
}

typedef struct _scolor_t
{
	UINT8 r, g, b, a;
} __attribute__ ((__packed__)) color_t;


void DrawPixel(UINT32 x, UINT32 y, color_t *pColor)
{
	int i, j;
	for (i=0; i < SCALE_Y; i++)
		for (j=0; j < SCALE_X; j++)
			g_gx_buff[(OFFSET_X+SCALE_X*x+j) + SCREEN_X*(OFFSET_Y+SCALE_Y*y+i)] = *((UINT32*)pColor);
}


/* MSB/LSB in y-Richtung vertauscht */
void displayBoard1( int* matrix )
{
	int x, y;
	int mask, accu;
	UINT32 color;

	for( x=0; x < NX; x++ )
	{
		mask = 0x80;
		accu = 0x00;
		for( y=0; y < NY; y++ )
		{
			color = (UINT32)-(MATRIX(x,y) & 0x1);
			DrawPixel(x, y, (color_t*)&color);
//			if (MATRIX( x, y ) != OCCUPIED)
//			{
//				accu |= mask;
//			}
//			mask = mask >> 1;

//			color = 0;
//			DrawPixel(x, (y>>3), (color_t*)&color);
//			if (mask == 0)
//			{
//				color = accu;
//				DrawPixel(x, (y>>3), (color_t*)&color);
//				mask = 0x80;
//				accu = 0x00;
//			}
		}
//		color = accu;
//		DrawPixel(x, (y>>3), (color_t*)&color);

	}
//	*(matrix-15) = 0xdead0000;
//	*(matrix-16) = *(matrix-16) + 1;
}

void initializeBoard( int* matrix ) {
  int x, y;
  for( x=0; x < NX; x++ ) {
    for( y=0; y < NY; y++ ) {
        int d = random1023();
        if (d > 920)  MATRIX( x, y ) = OCCUPIED;
        else          MATRIX( x, y ) = EMPTY;
    }
  }
}


int countNeighbors( int* matrix, int i, int j ) {
    int im = i-1;
    int ip = i+1;

    int jm = j-1;
    int jp = j+1;

    int n = 0;
    // wrap-around
    if (ip >= NX) ip = 0;
    if (jp >= NY) jp = 0;

    if (im < 0) im = NX-1;
    if (jm < 0) jm = NY-1;

    // check eight neighbors
    n += MATRIX(im, jm ) & 0x1;  // nw
    n += MATRIX(im, j  ) & 0x1;  // west
    n += MATRIX(im, jp ) & 0x1;  // sw

    n += MATRIX(i,  jm ) & 0x1;  // north
 // n += MATRIX(i,   j ) & 0x1;  // center
    n += MATRIX(i,  jp ) & 0x1;  // south

    n += MATRIX(ip, jm ) & 0x1;  // ne
    n += MATRIX(ip, j  ) & 0x1;  // east
    n += MATRIX(ip, jp ) & 0x1;  // se

    return n;
}


/**
 * the actual game of life algorithm: cell is born when three neighbors,
 * survives when exactly two live neighbors, dies otherwise.
 */
void nextGeneration( int* matrix, int* future ) {
  int i, j;
  int lifes = 0;

  for( i=0; i < NX; i++ ) {
    for( j=0; j < NY; j++ ) {
      // count neighbors
      int n = countNeighbors( matrix, i, j );

      //if ((n != 0) && (random1023() > 990)) { // some slight random offset
      // if (random1023() > 500) FUTURE(i,j) = OCCUPIED;
      //  else                    FUTURE(i,j) = EMPTY;
      //}
      // else if (n == 3)                                FUTURE(i,j) = OCCUPIED;

      if (n == 3)                                     FUTURE(i,j) = OCCUPIED;
      else if ((n == 2) && (MATRIX(i,j) == OCCUPIED)) FUTURE(i,j) = OCCUPIED;
      else                                            FUTURE(i,j) = EMPTY;

      if (FUTURE(i,j) == OCCUPIED) lifes++;
    }
  }
  // generation ++;

  // avoid extinction :-)
  if (lifes < 10) {
    // printf( "CREATING A NEW POPULATION\n\n\n" );
    initializeBoard( matrix );
  }
  else { // swap buffers: C PROHIBITS ASSIGNING TO ARRAY TYPES
    for( i=0; i < NX; i++ ) {
      for( j=0; j < NY; j++ ) {
        MATRIX(i,j) = FUTURE(i,j);
      }
    }
  }
}

void gx_init(void)
{
	volatile UINT32 *pVGA_ctrl = (UINT32*)SYS_VGA_CTRL;
	volatile UINT32 *pVGA_front = (UINT32*)SYS_VGA_FB_FRONT;
	volatile UINT32 *pVGA_back = (UINT32*)SYS_VGA_FB_BACK;

	sleep(500);

	g_gx_buff = (UINT32*)malloc(SCREEN_X*SCREEN_Y*sizeof(UINT32));
	memset(g_gx_buff, 0, SCREEN_X*SCREEN_Y*sizeof(UINT32));
	printf("g_gx_buff  : %8.8X\n", (UINT32)g_gx_buff);

	*pVGA_ctrl |= SYS_VGA_BIT_MSTEN;
	*pVGA_front = (UINT32)g_gx_buff;
	*pVGA_back = (UINT32)g_gx_buff;

}

int main( int argc, char* argv[] ) {

	int* ptr, cnt = 0;

	int* matrix;
	int* future;
	volatile UINT32 *pBtn = (UINT32*)SYS_GPIO0;

	//  lcdEnableDisplay( 1 );
	//  lcdSetColor( 1 );
	//  lcdSetPixel( 0, 0 );
	//  lcdSetPixel( 3, 5 );

	// if we have stdlib:
	matrix = (void*) malloc( NX*NY*sizeof(int) );
	future = (void*) malloc( NX*NY*sizeof(int) );


	gx_init();

//  matrix = (void *) 0x00004000;
//  future = (void *) 0x00014000;

  // init_lfsr32( 0xcafebabe );
  // init_lfsr32( 0x13 );

	srand(clock());
	initializeBoard( matrix );

	while( 1 )
	{

		if (*pBtn)
		{
			srand(clock());
			initializeBoard( matrix );
			cnt = 0;
		}
		sputs("Generation #"); print_word(cnt++); sputs("\n");
		displayBoard1( matrix );
		nextGeneration( matrix, future );
	}

  // return 0
}
