/**
 * Description: Create and save midi file.
 * Documentation https://jazz-soft.net/doc/JZZ/reference.html
 *
 * @package     midi-Synth
 * @category    module js
 * @author      Oleg Klenitsky <https://adminkov.bcr.by/midi/>
 * @copyright   2024 Oleg Klenitsky
 * @license     GPL-2.0-or-later
 */

"use strict";

/**
 * [createFileMidi description]
 * @return {bin} [description]
 */
function createFileMidi(){
  var maxTick = synth.getSongDurationTicks();
  var type = 1;
  var notes = document.getElementById("svg_body_part2").childNodes;
  var tracksInfo = document.getElementById("win_part1").childNodes;
  var valMSB;
  var valLSB;

  // Song info
  let inputCopyright = document.getElementById("song_midi").value;
  if("" == inputCopyright) inputCopyright = "Copyright notice";
  let inputTempoBPM  = document.getElementById("tempoBPM").value;
  if("" == inputTempoBPM) inputTempoBPM = 120;
  let inputTracks = document.getElementById("tracks_count").value;
  if("" == inputTracks) inputTracks = 1;
  let inputPPQN = document.getElementById("ppqn").value;
  if("" == inputPPQN) inputPPQN = 480;

  let smf = new JZZ.MIDI.SMF(type, inputPPQN);

  //Create track0
  let trk0 = new JZZ.MIDI.SMF.MTrk();
  trk0.smfCopyright(inputCopyright).smfBPM(inputTempoBPM);

  arrTimeSignature.forEach(function(item, index){
    if(88 == item.subtype){
      // по тикам ставим Signature для trk0.
      let duration = item.timeSignature[0] +"/"+ Math.pow(2, item.timeSignature[1]);
      trk0.tick(item.tick).smfTimeSignature(duration);
    }
    if(81 == item.subtype){
      // по тикам ставим tempoBPM для trk0.
      trk0.tick(item.tick).smfBPM(item.tempoBPM);
    }
  });
  smf.push(trk0);

  
  //Sort SVG (rect, circle) by field: trk
  let sortedNotes1 = Array.from(notes).sort(function(a,b){
    return parseInt(a.getAttribute("trk"))-parseInt(b.getAttribute("trk"));
  });

  //Sort SVG (rect, circle) by fields: x & y or cx & cy
  let sortedNotes2 = sortedNotes1.sort(function(a,b){
    if(a["trk"] == b["trk"]){
      if("rect" == a.tagName && "rect" == b.tagName){
        let a_trk = parseInt(a.getAttribute("trk"));
        let b_trk = parseInt(b.getAttribute("trk"));
        let a_x = parseInt(a.getAttribute("x"));
        let b_x = parseInt(b.getAttribute("x"));
        let a_y = parseInt(a.getAttribute("y"));
        let b_y = parseInt(b.getAttribute("y"));
        return a_trk - b_trk || a_x - b_x || a_y - b_y;
      }
      if("circle" == a.tagName && "circle" == b.tagName){
        let a_trk = parseInt(a.getAttribute("trk"));
        let b_trk = parseInt(b.getAttribute("trk"));
        let a_cx = parseInt(a.getAttribute("cx"));
        let b_cx = parseInt(b.getAttribute("cx"));
        let a_cy = parseInt(a.getAttribute("cy"));
        let b_cy = parseInt(b.getAttribute("cy"));
        return a_trk - b_trk || a_cx - b_cx || a_cy - b_cy;
      }
    }
  });

  tracksInfo.forEach(function(trackInfo, index){
    let numTrk = parseInt(trackInfo.getAttribute("numtrk"));
    let trackName = document.getElementById("part1_"+numTrk+"_trackName").innerHTML;
    let ch      = "";
    let numProg = "";
    let numBank = 0;

    if("GM" == standMidi){
      numProg = document.getElementById("part1_"+numTrk+"_instr").innerHTML;
      numProg = numProg.split(":");
      numProg = (numProg[1]) ?parseInt(numProg[1]) :0;
    }else{
      numBank = document.getElementById("part1_" +numTrk+ "_elemBank").innerHTML;
      numBank = numBank.split(":");
      numBank = (numBank[1]) ?parseInt(numBank[1]) :0;

      numProg = document.getElementById("part1_" +numTrk+ "_elemPreset").innerHTML;
      numProg = numProg.split(":");
      numProg = (numProg[1]) ?parseInt(numProg[1]) :"";
    }

    ch = document.getElementById("part1_"+numTrk+"_ch").innerHTML;
    ch = ch.split(":");
    ch = ("" == ch[1]) ?"" :parseInt(ch[1]-1);

    if("" === ch)return;

    let trk = new JZZ.MIDI.SMF.MTrk();
    if("" !== ch && "" !== numProg) trk.smfSeqName(trackName).ch(ch).bank(numBank).program(numProg);

    sortedNotes2.forEach(function(note, idx){
      let noteName = parseInt(sortedNotes2[idx].getAttribute("note"));
      let noteCh = parseInt(sortedNotes2[idx].getAttribute("ch"));
      let noteVel = parseInt(sortedNotes2[idx].getAttribute("velocity"));
      if(0 == noteVel)noteVel=1;//костыль
      let notePosX;
      let noteDur;
      if(9 == noteCh){
        notePosX = parseInt(sortedNotes2[idx].getAttribute("cx"));
        noteDur = 4;
      }else{
        notePosX = parseInt(sortedNotes2[idx].getAttribute("x"));
        noteDur = parseInt(sortedNotes2[idx].getAttribute("width"))*12;
      }
      if("" !== ch && ch == noteCh){
        trk.ch(ch).tick(notePosX*12).note(noteName, noteVel, noteDur);
      }
    });

    if(arrVolume[numTrk]){
      arrVolume[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).volumeMSB(item.value);
      });
    }
    arrVolume[numTrk] = [];

    if(arrPitchBend[numTrk]){
      arrPitchBend[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).pitchBend(item.param2,item.param1);
      });
    }
    arrPitchBend[numTrk] = [];

    if(arrPanpos[numTrk]){
      arrPanpos[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).panMSB(item.value);
      });
    }
    arrPanpos[numTrk] = [];

    if(arrExpression[numTrk]){
      arrExpression[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).expressionMSB(item.value);
      });
    }
    arrExpression[numTrk] = [];

    if(arrModulation[numTrk]){
      arrModulation[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).modMSB(item.value);
      });
    }
    arrModulation[numTrk] = [];

    if(arrSoftPedal[numTrk]){
      arrSoftPedal[numTrk].forEach(function(item, idx){
        let softPedal = false;
        if(0 <= item.value && item.value <= 64) softPedal = false;
        if(64 < item.value && item.value <= 127) softPedal = true;
        if("" !== ch) trk.ch(ch).tick(item.posX).soft(softPedal);
      });
    }
    arrSoftPedal[numTrk] = [];

    if(arrSostenuto[numTrk]){
      arrSostenuto[numTrk].forEach(function(item, idx){
        let sostenuto = false;
        if(0 <= item.value && item.value <= 64) sostenuto = false;
        if(64 < item.value && item.value <= 127) sostenuto = true;
        if("" !== ch) trk.ch(ch).tick(item.posX).sostenuto(sostenuto);
      });
    }
    arrSostenuto[numTrk] = [];

    if(arrSustain[numTrk]){
      arrSustain[numTrk].forEach(function(item, idx){
        let sustain = false;
        if(0 <= item.value && item.value <= 64) sustain = false;
        if(64 < item.value && item.value <= 127) sustain = true;
        if("" !== ch) trk.ch(ch).tick(item.posX).damper(sustain);
      });
    }
    arrSustain[numTrk] = [];

    if(arrBalance[numTrk]){
      arrBalance[numTrk].forEach(function(item, idx){
        if("" !== ch) trk.ch(ch).tick(item.posX).balanceMSB(item.value);
      });
    }
    arrBalance[numTrk] = [];

    trk.tick(maxTick).smfEndOfTrack();
    smf.push(trk);
  });

  return smf;
}

/**
 * [saveFileMidi description]
 */
function saveFileMidi(){
  var fileName = document.getElementById("file_name_midi").value;
  var link = document.createElement("a");
  var smf;

  btnTool = "";
  document.getElementById("ptrTimeStave").style.display = "none";

  smf = createFileMidi();

  fileName = fileName.split(".");
  fileName[0] = fileName[0]+"_new";
  fileName = fileName.join(".");

  var str = smf.dump(); // MIDI file dumped as a string
  var b64 = JZZ.lib.toBase64(str); // convert to base-64 string
  var uri = 'data:audio/midi;base64,' + b64; // data URI

  link.text = fileName;
  link.download = link.text;
  link.href = uri;
  link.click();
}

/**
 * Creating a temporary file
 */
function saveTempMidi(){
  var fileName = document.getElementById("file_name_midi").value;
  var link = document.createElement("a");
  var smf;

  smf = createFileMidi();

  var str = smf.dump(); // MIDI file dumped as a string
  var b64 = JZZ.lib.toBase64(str); // convert to base-64 string
  var uri = 'data:audio/midi;base64,' + b64; // data URI

  return uri;
}

/**
 * Create sourceConvert for export file
 */
function fileMidiConvert(){
  var fileName = document.getElementById("file_name_midi").value;
  var smf = createFileMidi();

  var str = smf.dump(); // MIDI file dumped as a string
  var b64 = JZZ.lib.toBase64(str); // convert to base-64 string

  return b64;
}

/**
 * Get params of new midi file
 * @return {[type]} [description]
 */
function getNewFileParams(){
  let dialog = document.createElement("dialog");
  dialog.setAttribute("style","display:flex;flex-direction:column;font-size:14px;border: 2px solid black;padding: 5px 10px");

  document.getElementById("ptrTimeStave").style.display = "none";

  let lbl  = document.createElement("label");
  lbl.innerHTML ="Select params for create midi file";
  lbl.setAttribute("style","font-weight: bold;");

  let lblCopyrigh = document.createElement("label");
  lblCopyrigh.innerHTML ="Copyrigh:";
  lblCopyrigh.setAttribute("style","display:block;width:80px;");
  let dialogCopyrigh = document.createElement("input");
  dialogCopyrigh.setAttribute("style", "width: 150px;height:18px;padding:0;outline: none;");
  dialogCopyrigh.id  = "dialogCopyrigh";
  dialogCopyrigh.value = "Copyright Notice";

  let divCopyrigh = document.createElement("div");
  divCopyrigh.setAttribute("style","display:flex;align-items: center;");
  divCopyrigh.appendChild(lblCopyrigh);divCopyrigh.appendChild(dialogCopyrigh);

  let lblFileName = document.createElement("label");
  lblFileName.innerHTML ="File Name:";
  lblFileName.setAttribute("style","display:block;width:80px;");
  let dialogFileName = document.createElement("input");
  dialogFileName.setAttribute("style", "width: 150px;height:18px;padding:0;outline: none;");
  dialogFileName.id = "dialogFileName";
  dialogFileName.value = "fileName.mid";

  let divFileName = document.createElement("div");
  divFileName.setAttribute("style","display:flex;align-items: center;");
  divFileName.appendChild(lblFileName);divFileName.appendChild(dialogFileName);

  let lblTimeSignsture = document.createElement("label");
  lblTimeSignsture.innerHTML ="timeSignature: ";

  let numerator = document.createElement("input");
  numerator.type = "number";numerator.min = "1";numerator.max = "16";
  numerator.value = "4";
  numerator.id = "numeratorDialog";
  numerator.setAttribute("style","width:40px;padding:0;height: 18px;outline: none;");
  let slash = document.createElement("label");
  slash.innerHTML = "/";
  slash.setAttribute("style","width:10px;font-weight: bold;text-align: center;");
  let denominator = document.createElement("select");
  denominator.add(new Option("4", "2"));
  denominator.add(new Option("8", "3"));
  denominator.add(new Option("16", "4"));
  denominator.add(new Option("32", "5"));
  denominator.id = "denominatorDialog";
  denominator.setAttribute("style","width:40px;padding:0;height: 18px;outline: none;");

  let divSignature = document.createElement("div");
  divSignature.setAttribute("style","display:flex;align-items:center;");
  divSignature.appendChild(lblTimeSignsture);
  divSignature.appendChild(numerator);
  divSignature.appendChild(slash);
  divSignature.appendChild(denominator);

  let lblPPQN = document.createElement("label");
  lblPPQN.innerHTML ="ppqn:";
  lblPPQN.setAttribute("style","display:block;width:80px;");
  let dialogPPQN = document.createElement("input");
  dialogPPQN.setAttribute("style", "width: 40px;height:18px;padding:0;outline: none;");
  dialogPPQN.type = "number";
  dialogPPQN.value = "480";
  dialogPPQN.min = "96";
  dialogPPQN.max = "960";
  dialogPPQN.step = "96";
  dialogPPQN.id  = "dialogPPQN";

  let divPPQN = document.createElement("div");
  divPPQN.setAttribute("style","display:flex;align-items: center;");
  divPPQN.appendChild(lblPPQN);divPPQN.appendChild(dialogPPQN);

  let lblbpm = document.createElement("label");
  lblbpm.innerHTML ="bpm:";
  lblbpm.setAttribute("style","display:block;width:80px;");
  let dialogBPM = document.createElement("input");
  dialogBPM.setAttribute("style", "width: 40px;height:18px;padding:0;outline: none;");
  dialogBPM.value = "120";
  dialogBPM.id  = "dialogBPM";

  let divBPM = document.createElement("div");
  divBPM.setAttribute("style","display:flex;align-items: center;");
  divBPM.appendChild(lblbpm);divBPM.appendChild(dialogBPM);

  let lbltrks = document.createElement("label");
  lbltrks.innerHTML ="tracks:";
  lbltrks.setAttribute("style","display:block;width:80px;");
  let dialogTrks = document.createElement("input");
  dialogTrks.setAttribute("style", "width: 40px;height:18px;padding:0;outline: none;");
  dialogTrks.value = "1";
  dialogTrks.id  = "dialogTrks";

  let divTrks = document.createElement("div");
  divTrks.setAttribute("style","display:flex;align-items: center;padding-bottom: 10px;");
  divTrks.appendChild(lbltrks);divTrks.appendChild(dialogTrks);

  let btnEnter = document.createElement("button");
  btnEnter.id  = "dialogEnter";
  btnEnter.className = "btn_sequencer";
  btnEnter.innerHTML = "Enter";
  let btnCansel = document.createElement("button");
  btnCansel.id  = "dialogCansel";
  btnCansel.className = "btn_sequencer";
  btnCansel.innerHTML = "Cansel";
  let divBtns = document.createElement("div");
  divBtns.setAttribute("style","margin: auto;");
  divBtns.appendChild(btnEnter);divBtns.appendChild(btnCansel);

  dialog.appendChild(lbl);
  dialog.appendChild(divCopyrigh);
  dialog.appendChild(divFileName);
  dialog.appendChild(divSignature);
  dialog.appendChild(divPPQN);
  dialog.appendChild(divBPM);
  dialog.appendChild(divTrks);
  dialog.appendChild(divBtns);

  subwin_part2.appendChild(dialog);
  dialog.showModal();

  btnEnter.addEventListener("click", function(){
    dialog.remove();

    //Reset other prm
    scale = 1;
    changedData = false;
    stave.innerHTML = "";
    selTrackNum = "";
    pressBtn    = "";
    arrMeasures = [];
    arrTracksEvents = [];
    arrVexFlow  = [];
    initArrTools();

    //Create empty midi file and load to synth.
    document.getElementById("file_name_midi").value = dialogFileName.value;
    document.getElementById("file_size_midi").value = "";
    let ptrTime = document.getElementById("ptrTime");
    let signature = numerator.value+"/"+Math.pow(2,denominator.value);
    let data = newFileMidi(dialogCopyrigh.value,signature,dialogPPQN.value,dialogBPM.value,dialogTrks.value);
    synth.loadMIDIUrl(data);
    ptrTime.style.left = "-2px";

    document.getElementById("stave").innerHTML = "";
    stave.setAttribute("style","display:flex;height:183px;font-size: 14px;background:#C3C3C3;");
  });

  btnCansel.addEventListener("click", function(){
    dialog.remove();
  });

  btnTool = "";
}
/**
 * [newFileMidi description]
 * @return {bin} [description]
 */
function newFileMidi(copyrigh,signature,ppqn,bpm,tracks){
  let smf = new JZZ.MIDI.SMF(1, ppqn);

  let trk0 = new JZZ.MIDI.SMF.MTrk();
  trk0.smfCopyright(copyrigh).smfBPM(bpm);
  trk0.tick(0).smfBPM(bpm);
  smf.push(trk0);

  for(let i=0; i<tracks; i++){
    let trk = new JZZ.MIDI.SMF.MTrk();
    let numTrk = i+1;
    trk.tick(0).smfTimeSignature(signature);
    trk.smfSeqName("New track "+numTrk).ch(i).program(0);
    trk.tick(ppqn*256).smfEndOfTrack();
    smf.push(trk);
  }

  var str = smf.dump();
  var b64 = JZZ.lib.toBase64(str);
  var uri = 'data:audio/midi;base64,' + b64;
  
  return uri;
}
