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

export const regex = /start\(\).*end\(\)/s;

export const descriptionOfFormat = `We start by calling start(). We will declare notes through a series of calls to the "n" function. The signature of the "n" function is "n(length: number, pitch: string, startTime: number)". "length" is a number representing the duration the note is held, in quarter notes. 1 = quarter note. 0.25 = sixteenth note. 0.5 = eighth note. 0.75 = dotted eighth note, etc. Pitch strings composed of A-G, followed by an optional # or b for sharp or flat, followed by an octave number. startTime is a number in beats, starting from 1. At the end, we call end(). Don't miss the semicolons!`;

// Est. tokens per element of the notes array: 15
// This avg is brought up by the presence of comments.
export const notesToString = (notes: (Note | Comment)[]): string => {
  const lines = [`start();`];

  // notes assumed to be sorted.
  for (let i = 0; i < notes.length; i++) {
    const note = notes[i];
    if (note.type === 'Comment') {
      lines.push(`// ${note.comment}`);
      continue;
    }
    const roundedStart = Math.round(note.start * 4) / 4;
    const roundedLength = Math.max(0.25, Math.round((note.end - note.start) * 4) / 4);
    const name = getAsciiNoteName(note.pitch);
    lines.push(`n(${roundedLength}, '${name}', ${roundedStart + 1});`);
  }

  lines.push(`end();`);

  return lines.join('\n');
};

export const stringToNotes = (notes: string): Note[] => {
  const lines = notes.replace(/\s/g, '').split(';');
  const result: Note[] = [];

  lines.forEach((line) => {
    if (line.includes('n(') && line.includes(')')) {
      const regexResult = line.match(/n\(([0-9]+\.?[0-9]*),['"]([a-gA-G][#b]?[0-9]+)['"],([0-9]+(\.[0-9]+)?)\)/);
      if (!regexResult) {
        console.log("Couldn't parse line (did not match)", line);
        return;
      } else if (!regexResult[1] || !regexResult[2] || !regexResult[3]) {
        console.log(`Couldn't parse line (missing argument)`, line);
        return;
      }
      const [_match, lengthStr, pitchStr, startStr] = regexResult;
      if (isNaN(Number(lengthStr)) || isNaN(Number(startStr))) {
        console.log(`Couldn't parse line (length or start not a number)`, line);
        return;
      }
      const pitch = getPitchFromAsciiNoteName(pitchStr);
      if (pitch === null) {
        console.log(`Couldn't parse line (pitch ${pitchStr} not valid)`, line);
        return;
      }

      const startBeats = Number(startStr) - 1;
      const endBeats = startBeats + Number(lengthStr);

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

  return result;
};
