/* The Null Neuron Interpreter - A brainfuck interpreter
 * By Daniel D'Agostino
 * Monday 20th October 2008
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stack>

using std::stack;

int getFileSize(const char * filename)
{
  struct stat fs;

  FILE * file = fopen(filename, "r");
  if (file == NULL)
  {
    fprintf(stderr, "Error: Bad filename");
    exit(-1);
  }
  if (fstat(fileno(file), &fs) == -1)
  {
    fprintf(stderr, "Error: Could not check filesize");
    exit(-1);
  }
  else
    return fs.st_size;
  fclose(file);
}
/*
int countPtrIncrements(const char * buffer, int filesize)
{
  int i = 0;
  int count = 0;
  for (i = 0; i < filesize; i++)
    if (buffer[i] == '>')
      count++;
  return count;
}
*/
int main(int argc, char ** argv)
{
  /* variable declarations */

  stack<int> loopstack = stack<int>();

  char * filename = NULL;
  FILE * file = NULL;
  int filesize = 0;

  char * ptr = NULL;
  char * firstbyte = NULL;
  const unsigned int memlen = 9999;

  int i = 0;
  char * buffer;

  /* input parameter: one expected for file */

  if (argc != 2)
  {
    fprintf(stderr, "Error: No input file\n");
    exit(1);
  }
  else
    filename = argv[1];

  /* load stuff from file */

  filesize = getFileSize(filename);
  printf("Reading %d bytes... ", filesize);

  file = fopen(filename, "r"); /* already passed validation */
  buffer = (char *) malloc(filesize);
  fread(buffer, 1, filesize, file);
  //ptrIncrements = countPtrIncrements(buffer, filesize);
  //firstbyte = (char *) malloc(ptrIncrements);
  firstbyte = (char *) malloc(memlen);
  ptr = firstbyte + 1; // leave first byte zero to terminate reverse loops
  //memset(ptr, 0, ptrIncrements);
  memset(firstbyte, 0, memlen);
  fclose(file);

  printf("done.\n");

  /* parse */

  printf("Parsing...\n");
  //printf("i,  c,   ptr, *ptr \n");

  for (i = 0; i < filesize; i++)
  {
    //printf("%d   %c    %d    %d \n", i, buffer[i], (ptr-firstbyte), *ptr);
    switch(buffer[i])
    {
      case '>': ++ptr; break;
      case '<':
        if (ptr == firstbyte)
        {
          fprintf(stderr, "Error: Negative pointer at %u", i);
          exit(-1);
        }
        else
          --ptr;
        break;
      case '+': ++(*ptr); break;
      case '-': --(*ptr); break;
      case '.': putchar(*ptr); break;
      case ',':
        *ptr = getchar();
        if (*ptr == 10) // LF = ENTER KEY
          *ptr = 0;
        break;
      case '[':
        loopstack.push(i);
        break;
      case ']':
        if (loopstack.empty())
        {
          fprintf(stderr, "Error: Unterminated loop\n");
          exit(-2);
        }
        else if (*ptr != 0)
        {
          i = loopstack.top() - 1;
          loopstack.pop();
        }
        else
          loopstack.pop();
        break;
      default: break;
    }
  }

  /* clean up and quit */

  free(buffer);
  free(firstbyte);
  exit(0);
}
