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

import java.util.ArrayList;
import jm.music.data.Note;
import jm.music.data.Part;
import jm.music.data.Phrase;
import jm.music.data.Score;


/**
 *
 * @author smuRf
 */
public class IPhrase implements BasicDataElement{
    private String name;
    private double startTime;
    private ArrayList<INote> notes;
    private IPart parent;
    private IHanson postHanson;
    private IHanson preHanson;
    private boolean mute = false;
    
    public IPhrase(String _name, double _startTime)
    {
        if(_name.equals("Untitled Phrase"))
            name = "phrase";
        else
            name = _name;
        startTime = _startTime;
        notes = new ArrayList<>();
    }
    
    public IPhrase(String _name, double _startTime, Phrase phrase)
    {
        if(_name.equals("Untitled Phrase"))
            name = "phrase";
        else
            name = _name;
        startTime = _startTime;
        notes = new ArrayList<>();
        
        Phrase p = new Phrase();
        p.setStartTime(0);

        for(Note note:phrase.getNoteArray())
        {
            INote iNote = new INote(note.getNote(), (note.getPitch()/12)-1, note.getRhythmValue(), note.getPitch(), note.getRhythmValue(), note.getDynamic());
            addINote(iNote);
        }
    }
    
    public void setMute(boolean mute)
    {
        this.mute = mute;
    }
    
    public IPart getParent()
    {
        return parent;
    }
    
    public void setParent(IPart iPart)
    {
        parent = iPart;
    }
    
    public void updateName(int id)
    {
        name+=id;
    }
    
    public String getName()
    {
        return name;
    }
    
    public double getStartTime()
    {
        return startTime;
    }
    
    public void addINote(INote note)
    {
        note.setParent(this);
        notes.add(note);
    }
    
    public void appendPhrase(IPhrase iPhrase)
    {
        ArrayList<INote> appendINotes = iPhrase.getINotes();
        
        for(INote iNote:appendINotes)
            addINote(iNote);
    }
    
    public INote getINote(int i)
    {
        return notes.get(i);
    }
    
    public ArrayList<INote> getINotes()
    {
        return notes;
    }
    
    public void removeNote(int index)
    {
        notes.remove(index);
    }
    
    public Phrase getPhrase()
    {   
        Phrase phrase = new Phrase(name, startTime);
        phrase.setMute(mute);
        return phrase;
    }
    
    public Phrase getPhraseWithNotes()
    {
        Phrase phrase = new Phrase();
        
        for(INote note:getINotes())
        {
            phrase.add(note.getOriginalNote());
        }
        
        return phrase;
    }
    
    public void addHansonRepresentation(IHanson []iHanson)
    {
        postHanson = iHanson[0];
        preHanson = iHanson[1];
    }
    
    public double getPostHansonValue(int i)
    {
        return postHanson.getInterval(i);
    }       
    
    public IHanson getPostHansonValues()
    {
        return postHanson;
    }
    
    public double getPreHansonValue(int i)
    {
        return preHanson.getInterval(i);
    }       
    
    public IHanson getPreHansonValues()
    {
        return preHanson;
    }
    
    public IPhrase normalize()
    {
        double[] durationPossibilities = new double[]{0.0, 0.125, 0.25, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0};
        INote restINote = null;
        
        IPhrase iPhrase = new IPhrase(getName(), getStartTime());
        iPhrase.setParent(parent);
        
        //remove short rests
        Note prevNote = null;
        for(INote iNote:getINotes())
        {
            restINote = null;
            Note note = iNote.getNote();
            if(iNote.getName().equals("REST") && iNote.getRhythmValue() < 3.0 && prevNote != null)
                prevNote.setRhythmValue(prevNote.getRhythmValue() + note.getRhythmValue());
            if(iNote.getName().equals("REST") && iNote.getRhythmValue() >= 3.0 && prevNote != null)
            {
                double rhythmValue = (int)iNote.getRhythmValue();
                double offset = iNote.getRhythmValue() - rhythmValue;
                restINote = new INote("REST", rhythmValue, rhythmValue*parent.getParent().getPPQN(), rhythmValue, note.getDynamic());
                
                prevNote.setRhythmValue(prevNote.getRhythmValue() + (int)(offset*parent.getParent().getPPQN()));
            }
            
            if(prevNote != null)
            {
                INote newINote = new INote(prevNote.getNote(), prevNote.getPitch()/12-1, prevNote.getRhythmValue()/parent.getParent().getPPQN(), prevNote.getRhythmValue(), prevNote.getPitch(), prevNote.getRhythmValue()/parent.getParent().getPPQN(), prevNote.getDynamic());
                iPhrase.addINote(newINote);
                if(restINote != null)
                    iPhrase.addINote(restINote);
            }
            
            if(!iNote.getName().equals("REST"))
                prevNote = note.copy();
            else
                prevNote = null;
        }
        if(prevNote != null)
        {
            INote newINote = new INote(prevNote.getNote(), prevNote.getPitch()/12-1, prevNote.getRhythmValue()/parent.getParent().getPPQN(), prevNote.getRhythmValue(), prevNote.getPitch(), prevNote.getRhythmValue()/parent.getParent().getPPQN(), prevNote.getDynamic());
            iPhrase.addINote(newINote);
        }
        
        //map duration to next possibility
        for(INote iNote:iPhrase.getINotes())
        {
            double minDiff = Double.MAX_VALUE;
            int minIndex = -1;
            int durationOverflow = -1;
            
            if(iNote.getRhythmValue() > durationPossibilities[durationPossibilities.length - 1])
            {
                durationOverflow = (int)iNote.getRhythmValue();
                double divider = iNote.getRhythmValue() - durationOverflow;
                
                if(divider > 0.5)
                {
                    iNote.setDuration(durationOverflow + 1);
                    iNote.setRhythmValue(durationOverflow + 1);
                    iNote.setRealDuration((int)((durationOverflow + 1) * parent.getParent().getPPQN()));
                }
                if(divider >= 0.25 && divider <= 0.5)
                {
                    iNote.setDuration(durationOverflow + 0.5);
                    iNote.setRhythmValue(durationOverflow + 0.5);
                    iNote.setRealDuration((int)((durationOverflow + 0.5) * parent.getParent().getPPQN()));
                }
                if(divider >= 0 && divider < 0.25)
                {
                    iNote.setDuration(durationOverflow);
                    iNote.setRhythmValue(durationOverflow);
                    iNote.setRealDuration((int)((durationOverflow) * parent.getParent().getPPQN()));
                }
                
                continue;
            }

            for(int i = 0; i < durationPossibilities.length; i++)
            {
                double durationPossibility = durationPossibilities[i];
                double rhythmValue = iNote.getRhythmValue();

                double diff = Math.abs(rhythmValue - durationPossibility);
                if(diff < minDiff)
                {
                    minDiff = diff;
                    minIndex = i;
                }
            }
            
            double duration = durationPossibilities[minIndex];
            int realDuration = (int)(durationPossibilities[minIndex] * parent.getParent().getPPQN());

            iNote.setDuration(duration);
            iNote.setRhythmValue(duration);
            iNote.setRealDuration(realDuration);
        }
        
        int size = iPhrase.getINotes().size();
        for(int i = 0; i < size; i++)
        {
            INote iNote = iPhrase.getINote(i);
            if(iNote.getName().equals("REST"))
            {
                iPhrase.removeNote(i);
                size = iPhrase.getINotes().size();
                i --;
            }   
        }
        
        return iPhrase;
    }
    
    @Override
    public Score getMusicStructure() {
        Score score = parent.getParent().getScore();
        Part part = parent.getPart();
        Phrase phrase = getPhrase();
        phrase.setMute(mute);
        
        phrase.setStartTime(0);

        for(INote iNote:getINotes())
        {
            Note note = iNote.getNote();
            phrase.add(note);
        }

        part.add(phrase);
        score.add(part);

        return score;
    }
}
