package src.individuals;

//import tools.RandomNumberGenerator;

import java.util.BitSet;

import tools.RandomNumberGenerator;

public class GAIndividual implements Comparable<GAIndividual> {
	protected BitSet m_Genotype;
	protected int m_GenotypeLength;

	/** Constructor **/
	public GAIndividual(int genotypeLength) {
		m_GenotypeLength = genotypeLength;
		initGenotype();
	}

	/**
	 * This method creates a deep copy of an individual. It should clone all
	 * objects contained by this object.
	 * 
	 * @return An deep copy of this {@link GAIndividual}
	 */
	@Override
	public Object clone() {
		GAIndividual c = new GAIndividual(m_GenotypeLength);
		c.setGenotype((BitSet) this.m_Genotype.clone());
		return c;
	}

	/**
	 * This method evaluates the GAIndividual as a simple
	 * "maximize number of bits" problem. The fitness is the number of true bits
	 * in m_Genotype. Best fitness is reached if there are no false bits.
	 * 
	 * @return The number of false bits (less is better!)
	 */
	public double evaluateAsMaxiBits() {
		int zeros = 0;
		for (int i = 0; i < m_GenotypeLength; i++) {
			if (!m_Genotype.get(i)) {
				zeros++;
			}
		}
		return zeros;
	}

	/**
	 * This method will return a string description of the GAIndividal notably
	 * the genotype: '0011000101'
	 * 
	 * @return A descriptive string
	 */
	public String getStringRepresentation() {
		String s = "";
		for (int i = 0; i < m_GenotypeLength; i++) {
			if (!m_Genotype.get(i)) {
				s += '0';
			} else {
				s += '1';
			}
		}
		return s;
	}

	/**
	 * This method will allow the user to read the GA genotype
	 * 
	 * @return BitSet
	 */
	public BitSet getGenotype() {
		return m_Genotype;
	}

	/**
	 * This method will allow the user to set the current GA genotype. Should
	 * check if the length of the BitSet and the genotype length match.
	 * 
	 * @param b
	 *            The new genotype of the Individual
	 */
	public void setGenotype(BitSet b) {
		m_Genotype = b;
	}

	/**
	 * This method allows the user to read the length of the genotype. This may
	 * be necessary since BitSet.length only returns the index of the last
	 * significant bit.
	 * 
	 * @return The length of the genotype.
	 */
	public int getGenotypeLength() {
		return m_GenotypeLength;
	}

	/**
	 * This method performs a simple one point mutation in the genotype (script
	 * 5.2.2). Please use the tools.RandomNumberGenerator
	 */
	public void mutate() {
		int rand = RandomNumberGenerator.randomInt(1, m_GenotypeLength) - 1;
		m_Genotype.flip(rand);
	}

	/**
	 * This method performs a simple one point crossover of two GAIndividuals
	 * (script 5.2.1). Please use the tools.RandomNumberGenerator
	 * 
	 * @return An array of length 2 with the two resulting GAIndivuals
	 */
	public static GAIndividual[] crossover(GAIndividual ind1, GAIndividual ind2) {
		if (ind1.getGenotypeLength() != ind2.getGenotypeLength()) {
			System.err
					.println("crossed individuals with different genotype length");
		}

		GAIndividual indA = (GAIndividual) ind1.clone();
		GAIndividual indB = (GAIndividual) ind2.clone();

		int rand = RandomNumberGenerator.randomInt(1, ind1.getGenotypeLength()) - 1;
		for (int i = rand; i < ind1.getGenotypeLength(); i++) {
			if (ind2.getGenotype().get(i)) {
				indA.getGenotype().set(i);
			} else {
				indA.getGenotype().clear(i);
			}
			if (ind1.getGenotype().get(i)) {
				indB.getGenotype().set(i);
			} else {
				indB.getGenotype().clear(i);
			}
		}
		// System.out.println("______");
		// System.out.println(ind1.getStringRepresentation());
		// System.out.println(ind2.getStringRepresentation());
		// System.out.println(rand);
		// System.out.println(indA.getStringRepresentation());
		// System.out.println(indB.getStringRepresentation());
		GAIndividual[] crossed = { indA, indB };
		return crossed;
	}

	/**
	 * This method initializes the GA genotype randomly. Please use the
	 * tools.RandomNumberGenerator
	 */
	public void initGenotype() {
		m_Genotype = new BitSet(m_GenotypeLength);
		for (int i = 0; i < m_GenotypeLength; i++) {
			if (RandomNumberGenerator.randomBoolean()) {
				m_Genotype.set(i);
			}
		}
	}

	@Override
	public int compareTo(GAIndividual o) {
		return ((Double) this.evaluateAsMaxiBits()).compareTo(o
				.evaluateAsMaxiBits());
	}
}
