/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package genetic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import jm.music.data.Phrase;

/**
 *
 * @author smuRf
 */
public class CrossOver {
    private Phrase[] chromosomes;
    private int newPopulationSize;
    private Phrase[] motifs;
    private double mutationPropability;
    private double motifMixPropability;
    private boolean mixInMotifs;
    
    public CrossOver(Phrase[] chromosomes, Phrase[] motifs, boolean mixInMotifs, double motifMixPropability, double mutationPropability)
    {
        this.chromosomes = chromosomes;
        this.mixInMotifs = mixInMotifs;
        newPopulationSize = this.chromosomes.length /2 * 3;
        this.mutationPropability = mutationPropability;
        this.motifMixPropability = motifMixPropability;
        this.motifs = new Phrase[motifs.length];
        System.arraycopy(motifs, 0, this.motifs, 0, motifs.length);
    }
    
    public Phrase[] doCrossOver()
    {
        ArrayList<Phrase> breed = new ArrayList();
        Phrase[] breedArray;
        Random rnd = new Random();
        
        HashMap<Integer, Integer> parentOneTwoIndizes = new HashMap();
        HashMap<Integer, Integer> parentTwoOneIndizes = new HashMap();
        
        for(int cntChildren = 0; cntChildren < newPopulationSize; cntChildren++)
        {
            boolean invalid = true;
            Phrase parentOne = null;
            Phrase parentTwo = null;

            int iterations = 0;
            while(invalid)
            {
                int indexOne = rnd.nextInt(chromosomes.length);
                int indexTwo = rnd.nextInt(chromosomes.length);
                
                if(indexOne != indexTwo)
                {
                    if(parentOneTwoIndizes.get(indexOne) == null || parentOneTwoIndizes.get(indexOne) != indexTwo)
                    {
                        if(parentTwoOneIndizes.get(indexTwo) == null || parentTwoOneIndizes.get(indexTwo) != indexOne)
                        {
                            invalid = false;
                            parentOneTwoIndizes.put(indexOne, indexTwo);
                            parentTwoOneIndizes.put(indexTwo, indexOne);
                            parentOne = chromosomes[indexOne];
                            parentTwo = chromosomes[indexTwo];
                        }
                    }
                }
                iterations++;
                if(iterations > 500 || breed.size() > newPopulationSize)
                    break;
            }
            
            if(parentOne != null && parentTwo != null)
            {
                Phrase child = onePointCrossOver(parentOne, parentTwo);
                
                double prop = rnd.nextDouble();
                if(mixInMotifs && prop < motifMixPropability)
                {
                    int motifIndex = rnd.nextInt(motifs.length);
                    child = Mutation.mixWithMotif(child, motifs[motifIndex]);
                }
                
                prop = rnd.nextDouble();
                if(prop < mutationPropability)
                {
                    Mutation mutation = new Mutation(child);
                    child = mutation.doMutate().copy();
                }
                
                breed.add(child);
            }
        }
        
        breedArray = new Phrase[breed.size()];
        for(int i = 0; i < breed.size(); i++)
            breedArray[i] = breed.get(i);
        
        return breedArray;
    }
    
    private Phrase onePointCrossOver(Phrase parentOne, Phrase parentTwo)
    {
        Random rnd = new Random();
        int maxSplitValue = (parentOne.size() < parentTwo.size() ? parentOne.size() : parentTwo.size());
        int split = rnd.nextInt(maxSplitValue);
        
        Phrase child = new Phrase("child");
        for(int i = 0; i < split; i++)
            child.add(parentOne.getNote(i).copy());
        for(int i = split; i < parentTwo.size(); i++)
            child.add(parentTwo.getNote(i).copy());
        
        return child;
    }
}
