/*

Copyright 1998 Christopher R. Vincent (cvince@mit.edu)
All rights reserved.

8/8/98: This code was a quick hack and sucks in a few ways, but it
looks pretty cool on my 90MHz PC.  I'd like to eventually write a
version that generates a similar effect on the fly rather than using
static data.

*/

#include <fcntl.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include "vga.h"

#define TRUE 1
#define FALSE 0
#define CELL_WIDTH 4
#define CELL_HEIGHT 4
#define PALETTE_SIZE 100
#define DATA_LENGTH 400

int cell_cols, cell_rows;
int display_width, display_height, display_depth;
char **cell_states;
char **data;
char* scanline;
char* palette_shifts;

void usage() {
  printf("usage: fireplace data-file\n");
}

void init_palette() {
  int i,c;  
  float delta = 0.5 * display_depth / PALETTE_SIZE;
  float sum = 0;
  int mid_idx = PALETTE_SIZE / 2;
  for(i = 0; i < mid_idx; i++) {
    vga_setpalette(i,(char)floor(sum),0,0);
    sum += delta;
  } 
  sum = 0;
  for(i = mid_idx; i < display_depth; i++) {
    vga_setpalette(i,255,(char)floor(sum),0);
     sum += delta;
  } 
}

void init_palette_shifts() {
  int i;
  char shift;
  float delta = (float)PALETTE_SIZE/cell_rows;
  float sum = 0;
  palette_shifts = (char *)malloc(sizeof(char) * cell_rows);
  for(i = 0; i < cell_rows; i++) {
    sum += delta;
    shift = (char)floor(sum);
    if (shift > 0) {
      sum -= shift;
      palette_shifts[i] = shift;
    }
    else
      palette_shifts[i] = 0;
    }
}

void init_data(char* file) {
  int i,j;
  char b;
  int fd = open (file, O_RDONLY, 0);
  data = (char **)malloc(sizeof(char *) * DATA_LENGTH);
  for(i = 0; i < DATA_LENGTH; i++) {
    data[i] = (char *)malloc(sizeof(char) * cell_cols);
    for(j = 0; j < cell_cols; j++) {
      read (fd,&b,1);
      if (b < 0) {
	printf("error reading data file, row %d col %d\n",i,j);
	return;
      }
      data[i][j] = b;
    }
  }
  close(fd);
}

inline void draw_cell_row(int y, char *row) {
  int i,j;

  for(i = 0; i < cell_cols; i++) {
    for(j = 0; j < CELL_WIDTH; j++) {
      scanline[i*CELL_WIDTH+j] = row[i];
    }
  }
  for(i = 0; i < CELL_HEIGHT; i++) {
    vga_drawscanline(y+i,scanline);
  }
}

void main (int argc,char **argv) {
  int i,j,cell_y, display_y;
  char *row;
  int color, shift;
  int cell_idx = 0;
  int data_pointer = 0;
  int data_dir = TRUE;

  if (argc != 2) {
    usage();
    return;
  }
  
  vga_setmode(G320x240x256);
  display_width = vga_getxdim();
  display_height = vga_getydim();
  printf("display dimensions %dx%d\n",display_width,display_height);
  scanline = (char *)malloc(sizeof(char) * display_width);
  cell_cols = 1 + display_width / CELL_WIDTH;
  cell_rows = 1 + display_height / CELL_HEIGHT;
  printf("cell dimensions %dx%d\n",cell_cols,cell_rows);
  cell_states = (char **)malloc(sizeof(char *) * cell_rows);
  for(i = 0; i < cell_rows; i++) {
    cell_states[i] = (char *)malloc(sizeof(char) * cell_cols);  
  }

  display_depth = vga_getcolors();
  printf("display depth %d\n",display_depth);
  init_palette();
  init_palette_shifts();
  
  init_data(argv[1]);

  for(;;) {
    if (data_dir)
      data_pointer += 1;
    else
      data_pointer -= 1;
    if (data_pointer == 0)
      data_dir = TRUE;
    else if (data_pointer == DATA_LENGTH-1)
      data_dir = FALSE;
   
    for(i = 0; i < cell_cols; i++) {
      cell_states[(cell_idx+cell_rows-1) % cell_rows][i] = data[data_pointer][i];
    }
 
    for(cell_y = 0; cell_y < cell_rows; cell_y++) {
      row = cell_states[cell_y];
      display_y = (cell_y < cell_idx) 
	? cell_rows - cell_idx + cell_y : cell_y - cell_idx;
      shift = palette_shifts[display_y];
      for(j = 0; j < cell_cols; j++) {
	color = row[j];
	if (color > 0) {
	  color -= shift;
	  if (color < 0)
	    color = 0;
	  row[j] = (char)color;
	}
      }
      draw_cell_row(display_y*CELL_HEIGHT,row);
    }
    cell_idx = (cell_idx + 1) % cell_rows;
  }

  /* vga_setmode(TEXT); */

}
