#include "ExecuteCellularAutomaton.h"

#include <cassert>
#include <cstdio>
#include <cstring>
#include <boost/lexical_cast.hpp>


tools::ExecuteCellularAutomaton::ExecuteCellularAutomaton(
  const char* chromostring, unsigned int n, unsigned int i )
    : iterations( i ) {
  ruleLUT = new AlleleType[4];
  ruleLUT[0] = static_cast<AlleleType>( 60 );
  ruleLUT[1] = static_cast<AlleleType>( 90 );
  ruleLUT[2] = static_cast<AlleleType>( 150 );
  ruleLUT[3] = static_cast<AlleleType>( 240 );

  chromosome = new AlleleType[n];
  for ( unsigned int locus = 0u; locus < n; locus++ )
    chromosome[locus] = boost::lexical_cast<AlleleType>( chromostring[locus] );

  ca = new CellularAutomaton( chromosome, n, ruleLUT );
}

tools::ExecuteCellularAutomaton::~ExecuteCellularAutomaton() {
  delete ca;
  delete chromosome;
  delete ruleLUT;
}

void tools::ExecuteCellularAutomaton::run() {
  while ( ca->getIteration() < iterations ) {
    ca->createVector();
    std::printf( "%s\n", ca->getStorage() );
  }
}

int main( int argc, char *argv[] ) {
  if ( argc < 3 ) {
    std::fprintf( stderr, "Usage: %s <iterations> <chromosome>\n", argv[0] );
    return 1;
  }

  unsigned int iterations = 0u;
  try {
    iterations = boost::lexical_cast<unsigned int>( argv[1] );
    if ( iterations == 0u )
      throw boost::bad_lexical_cast();
  } catch ( boost::bad_lexical_cast & ) {
    std::fprintf( stderr, "Could not convert '%s' to natural number!\n", argv[1] );
    std::fprintf( stderr, "Usage: %s <iterations> <chromosome>\n", argv[0] );
    return 2;
  }

  unsigned int n = 0u;
  n = std::strlen( argv[2] );
  if ( n < 2u ) {
    std::fprintf( stderr,
                  "Error! chromosome must be 2 or more digits long.\nUsage: %s <chromosome>\n",
                  argv[0] );
    return 3;
  }
  if ( n % 2u != 0 ) {
    std::fprintf( stderr,
                  "Error! chromosome length must be a multiple of 2.\nUsage: %s <chromosome>\n",
                  argv[0] );
    return 4;
  }
  char *chromostring = argv[2];
  for ( unsigned int i = 0; i < n; i += 2 ) {
    switch ( chromostring[i] ) {
      case '0':
      case '1':
        // OK
        break;
      default:
        std::fprintf( stderr,
                      "Error! chromosome 'state' cells can have only 0 or 1 value.\nUsage: %s <chromosome>\n",
                      argv[0] );
        return 5;
    }
    switch ( chromostring[i+1] ) {
      case '0':
      case '1':
      case '2':
      case '3':
        // OK
        break;
      default:
        std::fprintf( stderr,
                      "Error! chromosome 'rule' cells can only have values between 0 and 3.\nUsage: %s <chromosome>\n",
                      argv[0] );
        return 6;
    }
    // Chromosome is valid
  }

  tools::ExecuteCellularAutomaton eca( chromostring, n, iterations );
  eca.run();

  return 0;
}
