#include "TestVectors.h"
#include "Logger.h"

#include <cassert>
#include <boost/dynamic_bitset.hpp>
#include <fstream>

//#define VERBOSE
#ifdef VERBOSE
#include <cstdio>
#endif

TestVectors::TestVectors( std::string fn ) :
    filename( fn ), data( NULL ), good( false ), rows( 0u ), cols( 0u ) {
  try {

    std::ifstream inputFile( filename.c_str(),
                             std::ifstream::in | std::ifstream::binary | std::ifstream::ate );
    if ( !inputFile )
      throw 1;
    // Allocate memory
    unsigned int fileSize = static_cast<unsigned int>( inputFile.tellg() );
    if ( fileSize == 0u )
      throw 2;
    data = new char[fileSize];
    // Read whole file into memory in one go
    inputFile.seekg( 0, std::ifstream::beg );
    inputFile.read( data, fileSize );
    if ( inputFile.fail() || inputFile.bad() )
      throw 3;
    inputFile.close();
    // Figure out cols and verify
    while (( cols < fileSize ) &&
           ( data[cols] == '0' || data[cols] == '1' || data[cols] == 'x' ) )
      cols++;
    if ( cols == 0u )
      throw 4;
    // Knowing filesize and cols, calculate rows and verify
    rows = fileSize / ( cols + 1u );
#ifdef VERBOSE
    std::printf( "fileSize::%u\trows::%u\tcols::%u\n", fileSize, rows, cols );
#endif
    if ( rows*( cols + 1u ) != fileSize )
      throw 5;
    // Data is valid
    good = true;

  } catch ( int exception ) {
    LOG( Logger::LOGLEVEL_LOW,
         "Could not open data file <%s>! Error code :: %d",
         filename.c_str(), exception );
    if ( data ) {
      delete[] data;
      data = NULL;
    }
    rows = 0u;
    cols = 0u;
    good = false;
    return;
  }
}

TestVectors::~TestVectors() {
  if ( data )
    delete[] data;
}

bool TestVectors::isGood() const {
  return good;
}

unsigned int TestVectors::getRows() const {
  return rows;
}
unsigned int TestVectors::getCols() const {
  return cols;
}
std::string TestVectors::getFilename() const {
  return filename;
}

char TestVectors::getBit( unsigned int row, unsigned int col ) const {
  assert( row < rows );
  assert( col < cols );
  unsigned int index = row * ( cols + 1u ) + col;
  char bit = data[index];
  assert( bit == '0' || bit == '1' || bit == 'x' );
  return bit;
}

unsigned int TestVectors::countMatches( CellularAutomaton *ca, unsigned int iterations ) const {
  unsigned int row, col;
  boost::dynamic_bitset<> rowMatch( rows );
  unsigned int matches = 0u;
  const char *bits = ca->getStorage();

  while ( ca->getIteration() < iterations ) {
    ca->createVector();
    for ( row = 0u; row < rows; row++ ) {
      if ( !rowMatch.test( row ) ) {
        bool match = true;
        for ( col = 0u; match && ( col < cols ); col++ ) {
          match = match &&
                  ( getBit( row, col ) == 'x' ||
                    getBit( row, col ) == bits[col] );
        }
#ifdef VERBOSE
        std::printf( "Matching |%s| to row %d...%s\n", bits, row, match ? "MATCH" : "FAIL" );
#endif
        if ( match ) {
          rowMatch.set( row );
          if ( ++matches == rows )
            return rows;
        }
      }
    }
  }
  return matches;
}
