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

import data.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

/**
 *
 * @author smuRf
 */
public class PatternMatcher {
    private ArrayList<Double> postIntervals;
    private ArrayList<Double> preIntervals;
    private static PatternMatcher instance;
    private Double[] durations;
    
    public PatternMatcher(ArrayList<Double> postIntervals, ArrayList<Double> preIntervals, IPhrase selectedIPhrase)
    {
        this.postIntervals = postIntervals;
        this.preIntervals = preIntervals;
        
        durations = new Double[preIntervals.size()];
        for(int noteIndex = 0; noteIndex < durations.length; noteIndex++)
            durations[noteIndex] = selectedIPhrase.getINotes().get(noteIndex).getDuration();
        
        instance = this;
    }
    
    public static PatternMatcher getInstance()
    {
        return instance;
    }
    
    public double getDurations(int index)
    {
        if(index >= durations.length-1)
            return -3;
        return durations[index].doubleValue();
    }
    
    public double getExtendedPostInterval(int index)
    {
        if(index >= postIntervals.size())
            return -3;
        return postIntervals.get(index).doubleValue();
    }
    
    public double getExtendedPreInterval(int index)
    {
        if(index >= preIntervals.size())
            return -3;
        
        return preIntervals.get(index).doubleValue();
    }
    
    public static ArrayList<IMotif> match(ArrayList<Double> postIntervals, ArrayList<Double> preIntervals, HashMap<Double [], Integer>intervalPatterns, IPhrase selectedIPhrase, MotifDetector detector)
    {
        int motifCount = 0;
        int matchCount = 0;
        ArrayList<IMotif> motifs = new ArrayList();
        HashMap<Integer, HashSet<Integer>> tabuIndizes = new HashMap();
        
        for(Double[] postIntervalPattern:intervalPatterns.keySet())
        {
            int i = intervalPatterns.get(postIntervalPattern);
            int size = postIntervalPattern.length;
            
            if(!tabuIndizes.containsKey(size))
                tabuIndizes.put(size, new HashSet());
            
            int appearence = 0;
            ArrayList<Integer> indizes = new ArrayList();
            ArrayList<String> types = new ArrayList();
            HashMap<Integer, Integer> sizeOffsets = new HashMap();
            
            for(int j = 0; j < postIntervals.size()-size+1; j++)
            {
                Double[] durations = new Double[size];
                for(int noteIndex = 0; noteIndex < durations.length; noteIndex++)
                    durations[noteIndex] = selectedIPhrase.getINotes().get(j+noteIndex).getDuration();
                
                Double[] durationsPattern = new Double[postIntervalPattern.length];
                for(int noteIndex = 0; noteIndex < durationsPattern.length; noteIndex++)
                    durationsPattern[noteIndex] = selectedIPhrase.getINotes().get(i+noteIndex).getDuration();

                boolean equal = detector.detect(j, postIntervals.subList(j, j+size).toArray(new Double[0]), 
                                                preIntervals.subList(j, j+size).toArray(new Double[0]),
                                                postIntervalPattern, preIntervals.subList(i, i+size).toArray(new Double[0]), durations, durationsPattern);
                                
                if(detector instanceof RhythmicMotifDetectorHanson)
                {
                    int so = ((RhythmicMotifDetectorHanson)detector).getSizeOffset();
                    if(!tabuIndizes.containsKey(size+so))
                        tabuIndizes.put(size+so, new HashSet());
                    
                    sizeOffsets.put(j, so);
                }
                
                if(equal)
                {
                    Integer sizeOffset = sizeOffsets.get(j);
                    if(sizeOffset == null)
                        sizeOffset = 0;
                    if(j != i && !tabuIndizes.get(size+sizeOffset).contains(j))
                    {
                        appearence++;
                        indizes.add(j);
                        types.add(detector.getType());
                        
                        HashSet temp = tabuIndizes.get(size+sizeOffset);
                        temp.add(i);
                        tabuIndizes.put(size+sizeOffset, temp);
                    }
                }
            }

            if(appearence > 0)
            {
                INote[] noteArray = new INote[size];
                for(int j = 0; j < size; j++)
                    noteArray[j] = selectedIPhrase.getINotes().get(i+j);
                IMotif baseMotif = createMotif("motif"+motifCount++, noteArray, i, selectedIPhrase);

                for(int k = 0; k < indizes.size(); k++)
                {
                    int index = indizes.get(k);
                    Integer sizeOffset = sizeOffsets.get(index);
                    if(sizeOffset == null)
                        sizeOffset = 0;
                    String type = types.get(k);
                    noteArray = new INote[size+sizeOffset];
                    for(int j = 0; j < size+sizeOffset; j++)
                        noteArray[j] = selectedIPhrase.getINotes().get(index+j);
                    baseMotif.addEquiMotif(createMotif("match"+matchCount++, noteArray, index, selectedIPhrase, type));
                }
                matchCount = 0;

                motifs.add(baseMotif);
            }
        }
        
        return motifs;
    }
    
    private static IMotif createMotif(String name, INote[] motifArray, int noteIndex, IPhrase selectedIPhrase, String type)
    {
        IMotif motif = createMotif(name, motifArray, noteIndex, selectedIPhrase);
        motif.setMotifType(type);
        return motif;
    }
    
    private static IMotif createMotif(String name, INote[] motifArray, int noteIndex, IPhrase selectedIPhrase)
    {
        IScore iScore = new IScore(name, selectedIPhrase.getParent().getParent().getTempo());
        iScore.setPPQN(selectedIPhrase.getParent().getParent().getPPQN());
        IPart iPart = new IPart("part", selectedIPhrase.getParent().getDefaultInstrument(), 0);
        IPhrase iPhrase = new IPhrase("phrase", 0);

        for(INote iNote:motifArray)
            iPhrase.addINote(iNote);

        iScore.addIPart(iPart);
        iPart.addIPhrase(iPhrase);

        IMotif iMotif = new IMotif(iScore, Integer.parseInt(selectedIPhrase.getName().replace("phrase", "")),
                                            Integer.parseInt(selectedIPhrase.getParent().getName().replace("part", "")), noteIndex);

        return iMotif;
    }
}
