var MMLMaker = {

  map: {
    "A":  "a",
    "B♭": "a+",
    "B":  "b",
    "C":  "c",
    "C#": "c+",
    "D":  "d",
    "E♭": "d+",
    "E":  "e",
    "F":  "f",
    "F#": "f+",
    "G":  "g",
    "G#": "g+"
  },

  title: "",

  trackNames: [
    "A","B","C","D","E","F","G","H","I","J","K","L","M",
    "N","O","P","Q","R","S","T","U","V","W","X","Y","Z"
  ],

  tones: {
    "Acoustic Grand Piano": "0",
    "Electoric Piano" : "5",
    "Acoustic Giutar (Nylon)": "24",
    "Acoustic Giutar (Steel)": "25",
    "Electoric Giutar (Jazz)": "26",
    "Electoric Giutar (Clean)": "27",
    "Overdriven Guitar": "29",
    "Distortion Guitar": "30",
    "String Ensemble": "48",
    "Synth Strings": "50"
  },

  tempos: {
    "80": "80",
    "100": "100",
    "120": "120",
    "140": "140",
    "160": "160"
  },

  repeats: {
    "1": "1",
    "2": "2",
    "3": "3",
    "4": "4"
  },

  scaleInfo: {
    "tempo"     : "90",
    "channel"   : "1",
    "tone"      : "27",
    "noteLength": "8",
    "velocity"  : "90",
    "reverb"    : "60",
    "chorus"    : "0"
  },
  codeInfo: {
    "tempo"     : "120",
    "channel"   : "1",
    "tone"      : "27",
    "noteLength": "1",
    "velocity"  : "80",
    "reverb"    : "60",
    "chorus"    : "0"
  },
  macroInfo: {
    "tempo"     : "120",
    "channel"   : "1",
    "tone"      : "50",
    "noteLength": "1",
    "velocity"  : "80",
    "reverb"    : "80",
    "chorus"    : "0",
    "repeat"    : "1"
  },

  initMML: function(info, track) {
    var tempo = info["tempo"];
    var channel = info["channel"];
    var tone = info["tone"];
    var noteLength = info["noteLength"];
    var velocity = info["velocity"];
    var reverb = info["reverb"];
    var chorus = info["chorus"];
    return track + " t" + tempo + " C" + channel + " @" + tone
      + " l" + noteLength + " v" + velocity + " y91," + reverb
      + " y93," + chorus + " E127 r8 ";
  },

  makeScale: function(ranges) {
    var mml = this.initMML(this.scaleInfo, this.trackNames[0]);
    var octaveUpRoot = this.getOctaveUpRoot(ranges);
    var octaveRange = 3;
    var isFirst = true;
    var rootMml = "";
    for (var k in ranges) {
      var note = Scale.getNote(ranges[k]);
      var mmlNote = this.getMMLNote(note);
      if (note == octaveUpRoot) octaveRange++;
      mml += "o" + octaveRange + " " + mmlNote + " ";
      if (isFirst) {
        rootMml = "l4 o" + (octaveRange + 1) + " " + mmlNote + " ";
        isFirst = false;
      }
    }
    return mml + rootMml;
  },

  makeCode: function(ranges) {
    var octaveRange = 3
    var timingLength = 64;
    var tracks = [];
    var tracksIdx = 0;
    for (var k in ranges) {
      var note = Scale.getNote(ranges[k]);
      var mmlNote = this.getMMLNote(note);
      var timing = "r" + timingLength + " ";
      for (var i = 0; i < tracksIdx; i++) {
        timing += "r" + timingLength + " ";
      }
      if (note == this.getOctaveUpRoot(ranges)) octaveRange++;
      tracks[tracksIdx] = this.initMML(this.codeInfo, this.trackNames[tracksIdx]);
      if (ranges[k] > 23) {
        tracks[tracksIdx] += "o" + (octaveRange + 2) + " " + timing + " " + mmlNote;
      } else if (ranges[k] > 11) {
        tracks[tracksIdx] += "o" + (octaveRange + 1) + " " + timing + " " + mmlNote;
      } else {
        tracks[tracksIdx] += "o" + octaveRange + " " + timing + " " + mmlNote;
      }
      tracksIdx++;
    }
    var mml = "";
    for (var i = 0, n = tracks.length; i < n; i++) {
      mml += tracks[i] + "\n";
    }
    return mml;
  },

  makeMacro: function(macros) {
    var octaveRange = 3
    var rangesArray = [];
    var maxNoteCount = 0;
    var passageCount = macros.length;
    for (var i = 0; i < passageCount; i++) {
      var rootNote = macros[i]["rootNote"];
      var codeName = macros[i]["codeName"];
      var createdScale = Scale.createScale("code", codeName, rootNote);
      rangesArray[rangesArray.length] = createdScale["scale_r"];
      var count = createdScale["scale"].length;
      if (count > maxNoteCount) maxNoteCount = count;
    }
    var tracks = [];
    for (var i = 0; i < maxNoteCount; i++) {
      tracks[i] = this.initMML(this.macroInfo, this.trackNames[i]) + " [ ";
    }
    for (var i = 0; i < passageCount; i++) {
      var ranges = rangesArray[i];
      var tracksIdx = 0;
      var octaveUpRange = 0;
      for (var k in ranges) {
        var note = Scale.getNote(ranges[k]);
        var mmlNote = this.getMMLNote(note);
        if (note == this.getOctaveUpRoot(ranges)) {
          octaveUpRange++;
        }
        if (ranges[k] > 23) {
          tracks[tracksIdx] += "o" + (octaveRange + 2 + octaveUpRange) + " " + mmlNote + " ";
        } else if (ranges[k] > 11) {
          tracks[tracksIdx] += "o" + (octaveRange + 1 + octaveUpRange) + " " +  mmlNote + " ";
        } else {
          tracks[tracksIdx] += "o" + (octaveRange + octaveUpRange) + " " + mmlNote + " ";
        }
        tracksIdx++;
      }
      for (; tracksIdx < maxNoteCount; tracksIdx++) {
        tracks[tracksIdx] += "r ";
      }
    }
    var mml = "";
    for (var i = 0, n = tracks.length; i < n; i++) {
      mml += tracks[i] + " ]" + this.macroInfo["repeat"] +"\n";
    }
    return mml;
  },

  getMMLNote: function(note) {
    return this.map[note];
  },

  getOctaveUpRoot: function(ranges) {
    var notes_r = Scale.notes_r;
    for (var i = 3, n = notes_r.length; i < n; i++) {
      var range = (i > 11) ? i - 12 : i;
      if (ranges[notes_r[range]] != null) {
        return notes_r[range];
      }
    }
  }

}
