package hcMaxiBits;

//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) {
		// This method should call initGenotype() to initialize the genotype
		this.m_GenotypeLength = genotypeLength;
		initGenotype();
	}

	/**
	 * cloning constructor
	 * 
	 * @param toClone
	 */
	private GAIndividual(GAIndividual toClone) {
		this.m_Genotype = (BitSet) toClone.m_Genotype.clone();// allowed, has
																// only native
																// typed
																// fields
		this.m_GenotypeLength = toClone.m_GenotypeLength;
	}

	/**
	 * 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;

		// return new GAIndividual(this);
	}

	/**
	 * 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() {
		// this should return exactly the representation shown in the comment
		// above:
		// only 0 (for false bits) and 1 (for true bits) with no extra white
		// space!
		String representation = "";
		for (int i = 0; i < m_GenotypeLength; i++) {
			representation = m_Genotype.get(i) ? representation + "1"
					: representation + "0";

		}
		return representation;
	}

	/**
	 * 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) {

		this.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() {
		m_Genotype.flip(RandomNumberGenerator
				.randomInt(0, m_GenotypeLength - 1));
	}

	/**
	 * 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) {
		assert ind1.m_GenotypeLength == ind2.m_GenotypeLength;

		int splitPoint = RandomNumberGenerator.randomInt(0,
				ind1.m_GenotypeLength - 1);
		GAIndividual ind1Clone = (GAIndividual) ind1.clone();
		GAIndividual ind2Clone = (GAIndividual) ind2.clone();
		for (int i = splitPoint; i < ind1Clone.m_GenotypeLength; i++) {
			ind1Clone.m_Genotype.set(i, ind2.m_Genotype.get(i));
			ind2Clone.m_Genotype.set(i, ind1.m_Genotype.get(i));

		}
		// System.out.println("______");
		// System.out.println(ind1.getStringRepresentation());
		// System.out.println(ind2.getStringRepresentation());
		// System.out.println(splitPoint);
		// System.out.println(ind1Clone.getStringRepresentation());
		// System.out.println(ind2Clone.getStringRepresentation());

		return new GAIndividual[] { ind2Clone, ind1Clone };
	}

	/**
	 * 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++) {
			m_Genotype.set(i, RandomNumberGenerator.randomBoolean());
		}
	}

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