package twin;

//import tools.RandomNumberGenerator;

import java.util.BitSet;
import java.util.Random;

/**
 * @author Maximus
 *
 */
public class PBIndividualTwin implements Comparable<PBIndividualTwin> {
	protected BitSet m_Genotype;
	protected int m_GenotypeLength;
	protected Random r = new Random();

	/** Constructor **/
	public PBIndividualTwin(double[] propVector) {
		// This method should call initGenotype() to initialize the genotype
		this.m_GenotypeLength = propVector.length;
		initGenotype(propVector);

	}

	/**
	 * cloning constructor
	 * 
	 * @param toClone
	 */
	private PBIndividualTwin(PBIndividualTwin 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 PBIndividualTwin}
	 */
	@Override
	public Object clone() {

		PBIndividualTwin c = new PBIndividualTwin(new double[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;
	}

	/**
	 * 
	 * @return best fitness ==0
	 */
	public double evaluateAsTwin() {
		int f1 = 0;
		int f2 = 0;
		for (int i = 0; i < (m_GenotypeLength / 2); i++) {
			if (m_Genotype.get(i)) {
				f1++;
			}
		}
		for (int i = (m_GenotypeLength / 2); i < m_GenotypeLength; i++) {
			if (m_Genotype.get(i)) {
				f2++;
			}
		}
		return (m_GenotypeLength / 2) - Math.abs(f1 - f2);
	}

	/**
	 * 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 initializes the GA genotype randomly. Please use the
	 * tools.RandomNumberGenerator
	 * 
	 * @param propVector
	 */
	public void initGenotype(double[] propVector) {
		m_Genotype = new BitSet(m_GenotypeLength);
		for (int i = 0; i < m_GenotypeLength; i++) {
			if (r.nextDouble() < propVector[i]) {
				m_Genotype.set(i);
			}
		}
	}

	@Override
	public int compareTo(PBIndividualTwin o) {
		// return ((Double) this.evaluateAsMaxiBits()).compareTo(o
		// .evaluateAsMaxiBits());
		return ((Double) this.evaluateAsTwin()).compareTo(o.evaluateAsTwin());
	}
}
