package Organisms2.g2;

public class Genes {
	private static final double BASE_FOOD_CULTIVATION_PROB = 0.0;
	private static final double BASE_MOVE_PROBABILITY = 0.3;
	private static final double BASE_REPRODUCTION_THRESHOLD = 1.0;
	private static final double MUTATION_PROBABILITY = 1.0;
	private static final int MAX_MUTATION_AMOUNT = 20; // (0,128)

	private double _moveProbability;
	private double _reproductionThreshold;
	private double _foodCultivationProbability;
	private int _mutationAmount;
	private byte _generation;

	public Genes( int value ) {
		_foodCultivationProbability = BASE_FOOD_CULTIVATION_PROB; 
		_moveProbability = BASE_MOVE_PROBABILITY;
		_reproductionThreshold = BASE_REPRODUCTION_THRESHOLD;
		_mutationAmount = MAX_MUTATION_AMOUNT;
		_generation = 0;
		decode( value );
	}

	public Genes( double base_food_cult, double base_move, double base_reprod ) {
		_foodCultivationProbability = BASE_FOOD_CULTIVATION_PROB; 
		_moveProbability = BASE_MOVE_PROBABILITY;
		_reproductionThreshold = BASE_REPRODUCTION_THRESHOLD;
		_mutationAmount = MAX_MUTATION_AMOUNT;

		if( base_food_cult > 0 ) {
			_foodCultivationProbability = base_food_cult;
		}

		if( base_move > 0 ) {
			_moveProbability = base_move;
		}

		if( base_reprod > 0 ) {
			_reproductionThreshold = base_reprod;
		}

		_generation = 0;
	}

	public void doubleMutationAmount() {
		_mutationAmount *= 2.0;
	}

	public void halveMutationAmount() {
		_mutationAmount /= 2.0;
	}

	public double moveProbability() {
		return( _moveProbability );
	}

	public double reproductionThreshold() {
		return( _reproductionThreshold );
	}

	public double foodCultivationProbability() {
		return( _foodCultivationProbability );
	}

	public int generation() {
		return( _generation );
	}

    protected void decode( int value ) {
		byte temp;

		if( value != -1 ) {
			// 1st 8 bits represent _generation
			_generation = ( byte )( ( value & 0xFF000000 ) >> 24 );

			// 2nd 8 bits represent _foodCultivationProbability change
			temp = ( byte )( ( value & 0x00FF0000 ) >> 16 );
			_foodCultivationProbability += 1.0 * temp / 128;
			_foodCultivationProbability = Math.max( _foodCultivationProbability, 0 );
			_foodCultivationProbability = Math.min( _foodCultivationProbability, 1 );

			// 3rd 8 bits represent _moveProbability change
			temp = ( byte )( ( value & 0x0000FF00 ) >> 8 );
			_moveProbability += 1.0 * temp / 128;
			_moveProbability = Math.max( _moveProbability, 0 );
			_moveProbability = Math.min( _moveProbability, 1 );

			// 4th 8 bits represent _reproductionThreshold change
			temp = ( byte )( value & 0x000000FF );
			_reproductionThreshold += 1.0 * temp / 128;
			_reproductionThreshold = Math.max( _reproductionThreshold, 0 );
			_reproductionThreshold = Math.min( _reproductionThreshold, 1 );
		}
	}

	public int mutate() {
		int value, next_gen;
		byte temp;

		// 1st 8 bits represent _generation
		next_gen = ( ( 0x00000FF & _generation ) + 1 );

		if( Group2PlayerQ.DEBUG ) {
			if( next_gen >= 128 ) {
				//System.out.println( "OVERFLOW" );
			}
		}

		value = 0;
		value = value | ( ( 0x000000FF & next_gen ) << 24 );

		// 2nd 8 bits represent _foodCultivationProbability change
		temp = ( byte )Math.round( ( _foodCultivationProbability - BASE_FOOD_CULTIVATION_PROB ) * 128 );
		
		if( Math.random() < MUTATION_PROBABILITY ) {
			temp += ( byte )Math.round( ( 2 * Math.random() * _mutationAmount - _mutationAmount ) );
		}

		//value = value | ( ( 0x000000FF & temp ) << 16 );
		value = value | ( ( 0x000000FF & 0 ) << 16 );
		
		// 3rd 8 bits represent _moveProbability change
		temp = ( byte )Math.round( ( _moveProbability - BASE_MOVE_PROBABILITY ) * 128 );

		if( Math.random() < MUTATION_PROBABILITY ) {
			temp += ( byte )Math.round( ( 2 * Math.random() * _mutationAmount - _mutationAmount ) );
		}

		value = value | ( ( 0x000000FF & temp ) << 8 );
		
		// 4th 8 bits represent _reproductionThreshold change
		temp = ( byte )Math.round( ( _reproductionThreshold - BASE_REPRODUCTION_THRESHOLD ) * 128 );

		if( Math.random() < MUTATION_PROBABILITY ) {
			temp += ( byte )Math.round( ( 2 * Math.random() * _mutationAmount - _mutationAmount ) );
		}

		value = value | ( 0x000000FF & temp );
		return( value );
	}
}
