import { Comment, Note } from '../types';
import { getAsciiNoteName, getPitchFromAsciiNoteName } from './utils';

export const regex = /\[(\[(\[.*\],?)*\],?)*\]/;

export const descriptionOfFormat =
  'The format represents an array of Voices. Voices are arrays of Bars. Bars are array of Notes. Notes are strings. The format of a Note is [Length][Pitch]. Lengths are numbers representing a number of quarter notes or beats. 1 = quarter note. 0.25 = sixteenth note. 0.5 = eighth note. 0.75 = dotted eighth note, et cetera. Pitches are A-G, followed by an optional # or b for sharp or flat, followed by an octave number. For a rest, add "R0" instead of a pitch name. When the last note in a bar is specified, close the array!';

export const notesToString = (notes: (Note | Comment)[]): string => {
  const voices = [[[]]];

  const sortedNotes = ([...notes].filter((n) => n.type !== 'Comment') as Note[]).sort((a, b) => a.start - b.start);

  let offset = 0;
  let bar = 0;
  for (let i = 0; i < sortedNotes.length; i++) {
    const note = sortedNotes[i];
    const roundedStart = Math.round(note.start * 4) / 4;

    if (roundedStart > offset + 4 * bar) {
      let restLength = roundedStart - (offset + 4 * bar);
      offset += restLength;
      if (offset >= 4) {
        restLength -= 4 - (offset - restLength);
        bar++;
        offset = restLength;
        voices[0].push([]);
      }
      voices[0][bar].push(`${restLength}R0`);
    }

    const roundedLength = Math.max(0.25, Math.round((note.end - note.start) * 4) / 4);
    const name = getAsciiNoteName(note.pitch);
    voices[0][bar].push(`${roundedLength}${name}`);
    offset += roundedLength;
    if (offset >= 4) {
      bar++;
      offset = 0;
      voices[0].push([]);
    }
  }

  return JSON.stringify(voices);
};

export const stringToNotes = (notes: string): Note[] => {
  const voices = JSON.parse(notes);
  const result: Note[] = [];

  for (let i = 0; i < voices.length; i++) {
    let offset = 0;
    for (let j = 0; j < voices[i].length; j++) {
      offset = Math.ceil(offset / 4) * 4;
      for (let k = 0; k < voices[i][j].length; k++) {
        const noteStr = voices[i][j][k];
        // console.log(noteStr);

        // console.log('length');
        const lengthStr = noteStr.match(/^[0-9]+\.?[0-9]*/)[0];

        // console.log('pitch');
        const pitchStr = noteStr.match(/[a-gA-GrR][#b]?[0-9]+/)[0];

        // console.log("Parsed note with", { lengthStr, pitchStr });
        const length = Number(lengthStr);

        if (length <= 0) {
          continue;
        }

        let startBeats = offset;
        offset += length;
        let endBeats = offset;

        if (pitchStr.toLowerCase().includes('r')) continue;
        const pitch = getPitchFromAsciiNoteName(pitchStr);

        result.push({
          pitch,
          start: startBeats,
          end: endBeats,
          velocity: 100 / 127,
        });
      }
    }
  }

  return result;
};
