package jp.crestmuse.cmx.inference;

import jp.crestmuse.cmx.inference.MusicRepresentation.MusicElement;
import jp.crestmuse.cmx.inference.MusicRepresentation.Type;

public class NaiveVoicingCalculator implements Calculator {

  private MusicRepresentation musicRepresentation;
  private static final int BASE_NN = 36;
  private static final int[][] basslines = new int[][] { { 0, 4, 7, 0 },
      { 2, 0, -3, 2 }, { 4, -1, -5, 4 }, { -7, 0, 5, -7 }, { -5, 7, 2, -5 },
      { -3, -1, 0, -3 }, { -1, 0, 1, 2 } };

  public NaiveVoicingCalculator(MusicRepresentation mr) {
    musicRepresentation = mr;
  }

  public Type[] drivenBy() {
    return new Type[] { Type.Chord };
  }

  public void update(MusicElement me, int index) {
    MusicElement vh = musicRepresentation.addVoicingHighElement(index);
    MusicElement vmh = musicRepresentation.addVoicingMidHighElement(index);
    MusicElement vml = musicRepresentation.addVoicingMidLowElement(index);
    MusicElement vl = musicRepresentation.addVoicingLowElement(index);
    int highestIndex = me.getHighestProbIndex();
    // 連発防止
    // 前の小節と前の前の小節とコードが同じなら二位のコードを採用
    if (index > musicRepresentation.getDivision()
        && musicRepresentation.getChordElement(
            index - musicRepresentation.getDivision()).getHighestProbIndex() == highestIndex
        && musicRepresentation.getChordElement(
            index - musicRepresentation.getDivision() * 2)
            .getHighestProbIndex() == highestIndex) {
      int tmp = me.getRankedProbIndex(1);
      if (me.getProb(tmp) > 0.0)
	  highestIndex = tmp; 
      musicRepresentation.getChordElement(index).setEvidence(highestIndex);
    }
    int[] map = diatonicMapC(me.getLabel(highestIndex));
    vh.setProb(map[0], me.getProb(highestIndex));
    vmh.setProb(map[1], me.getProb(highestIndex));
    vml.setProb(map[2], me.getProb(highestIndex));
    vl.setProb(map[3], me.getProb(highestIndex));
    MusicElement bass = musicRepresentation.addBassElement(index);
    for (int i = 0; i < 4; i++) {
      bass = musicRepresentation.addBassElement(index + (i) * 2);
      bass.setEvidence(BASE_NN + basslines[highestIndex][i]);
    }
  }

  private int[] diatonicMapC(String chord) {
    int[] diatonic;
    if (chord.equals("C"))
      diatonic = new int[] { 55, 57, 60, 64 };
    else if (chord.equals("Dm"))
      diatonic = new int[] { 57, 60, 62, 65 };
    else if (chord.equals("Em"))
      diatonic = new int[] { 59, 62, 64, 67 };
    else if (chord.equals("F"))
      diatonic = new int[] { 57, 60, 62, 65 };
    else if (chord.equals("G"))
      diatonic = new int[] { 55, 59, 62, 65 };
    else if (chord.equals("Am"))
      diatonic = new int[] { 55, 57, 60, 64 };
    else
      diatonic = new int[] { 57, 59, 62, 65 }; // B(b5)
    return diatonic;
  }

}
