#include <iostream>

#include "../rand/randomc.h"
#include "../rand/mersenne.cpp"
//#include "../rand/ranrotw.cpp"
//#include "../rand/mother.cpp"

#include "generic_individual.h"
#include "generic_fitness.h"
#include "individual.h"

using namespace std;

generic_fitness *individual::evaluator = 0;
int individual::vector_count = 0;
bool individual::minimize = false;
int individual::block_variants = 0;
int individual::block_size = 0;

/** 
 * an initializing constructor
 */
individual::individual(char p_type, int p_cells,
    RANDOM_GENERATOR *p_rg, float p_ones_ratio) {
  type = p_type;
  cells = p_cells;

  if(type == TYPE_MATRIX_SEED)
    size = cells * (cells + 1);
  else if(type == TYPE_RULES_SEED)
    size = cells * (individual::block_size + 1);

  fitness = DEFAULT_FITNESS;
  chromosome = new GENE [size];

  if(GENE_COUNT == 2) {
    for(int i = 0; i < size; i++)
      chromosome[i] = (GENE) ((p_rg->Random() < p_ones_ratio) ? 1
          : 0);
  }
  else {
    for(int i = 0; i < size; i++)
      chromosome[i] = (GENE) p_rg->IRandom(0, GENE_COUNT - 1);
  }
}

/** 
 * an initializing constructor (mainly for testing purposes)
 */
individual::individual(char p_type, int p_cells,
    float p_fitness, GENE *p_chr) {
  type = p_type;
  cells = p_cells;

  if(type == TYPE_MATRIX_SEED)
    size = cells * (cells + 1);
  else if(type == TYPE_RULES_SEED)
    size = cells * (individual::block_size + 1);

  fitness = p_fitness;
  chromosome = new GENE [size];

  if(p_chr != 0)
    for(int i = 0; i < size; i++) chromosome[i] = p_chr[i];
  else for(int i = 0; i < size; i++) chromosome[i] = 0;
}

/** 
 * a copy constructor
 */
individual::individual(individual *p_parent) {
  type = p_parent->get_type();
  cells = p_parent->get_cells();
  size = p_parent->get_size();
  fitness = p_parent->get_fitness();
  chromosome = new GENE [size];
  p_parent->get_chromosome(chromosome);
}

/**
 * sets the default evaluator for this class
 * NEEDS TO BE RAN BEFORE ANY CONSTRUCTOR IS CALLED FOR THE 1. TIME
 */
bool individual::set_evaluator(generic_fitness *p_eval) {
  if(p_eval == 0) return false;

  individual::evaluator = p_eval;
  individual::block_variants = evaluator->get_block_variants();
  individual::block_size = evaluator->get_block_bitsize();

  return true;
}

/**
 * sets the number of vectors used for evaluation
 */
int individual::set_vector_count(int p_vector_cnt) {
  return individual::vector_count = p_vector_cnt;
}

/**
 * sets the minimization flag
 */
bool individual::set_minimize(bool p_val) {
  return individual::minimize = p_val;
}

/**
 * computes an individual's fitness
 */
float individual::get_fitness() {
  float res;

  // did we compute the fitness before?
  if(fitness != DEFAULT_FITNESS) {
    res = fitness;
  }
  else {
    res = individual::evaluator->get_fitness(this,
        individual::vector_count);

    if(individual::minimize) res *= -1;
    fitness = res;
  }

  // up, up and away!
  return res;
}

/** 
 * writes a copy of an individual's chromosome into a buffer
 * which should have the appropriate amount of space allocated
 * by the caller
 */
GENE *individual::get_chromosome(GENE *p_buffer) {
  if(p_buffer == 0) return 0;

  for(int i = 0; i < size; i++)
    p_buffer[i] = chromosome[i];

  return p_buffer;
}

/**
 * returns a characteristic matrix of a CA corresponding
 * to an individual (the matrix is stored in linear form)
 */
GENE *individual::get_matrix(GENE *p_buffer) {
  if(p_buffer == 0) return 0;

  switch(type) {
    case TYPE_MATRIX_SEED:
      {
        int upper_margin = size - cells;

        for(int i = 0; i < upper_margin; i++)
          p_buffer[i] = chromosome[i];
      }

      break;

    default:
      break;
  }

  return p_buffer;
}

/**
 * returns a table of rules describing the behaviour of each cell
 */
int *individual::get_rules(int *p_buffer) {
  if(p_buffer == 0) return 0;

  switch(type) {
    case TYPE_RULES_SEED:
      {
        int index = 0;

        if(GENE_COUNT == 2) {
          // read rules from the chromosome into the buffer
          for(int i = 0; i < cells; i++) {
            int tmp_rule = 0;

            // parse a single rule
            for(int j = 0; j < individual::block_size; j++) {
              tmp_rule <<= 1;
              tmp_rule |= chromosome[index + j];
            }

            // store the rule into the buffer if valid
            if(tmp_rule < individual::block_variants)
              p_buffer[i] = tmp_rule;
            else return 0; // an invalid rule

            index += individual::block_size;
          }
        }
        else {
          // read rules from the chromosome into the buffer
          for(int i = 0; i < cells; i++) {
            int tmp_rule = 0;

            // parse a single rule
            for(int j = 0; j < individual::block_size; j++) {
              tmp_rule *= GENE_COUNT;
              tmp_rule += chromosome[index + j];
            }

            // store the rule into the buffer if valid
            if(tmp_rule < individual::block_variants)
              p_buffer[i] = tmp_rule;
            else return 0; // an invalid rule

            index += individual::block_size;
          }
        }
      }

      break;

    default:
      break;
  }

  return p_buffer;
}

/**
 * returns a seed of a CA corresponding to an individual
 */
GENE *individual::get_seed(GENE *p_buffer) {
  if(p_buffer == 0) return 0;

  switch(type) {
    case TYPE_MATRIX_SEED:
    case TYPE_RULES_SEED:
      {
        int lower_margin = size - cells;
        int j = 0;
  
        for(int i = lower_margin; i < size; i++)
          p_buffer[j++] = chromosome[i];
      }

      break;

    default:
      break;
  }

  return p_buffer;
}

/**
 * compares an individual with it's given peer
 */
bool individual::compare(individual *p_peer) {
  if((p_peer == 0) || (p_peer->get_size() != size)) return false;

  bool res = true;
  GENE *peer_chr = new GENE [size];
  p_peer->get_chromosome(peer_chr);

  for(int i = 0; i < size; i++)
    if(peer_chr[i] != chromosome[i]) {
      res = false;
      break;
    }

  delete [] peer_chr;

  return res;
}

/** 
 * prints an individual to the standard output
 */
void individual::print() {
  for(int i = 0; i < size; i++)
    cout << (int) chromosome[i];

  cout << " (T " << (int) type << ", S "
    << size << ", F " << fitness << ")" << endl;
}

