type Note = {
  pitch: number;
  start: number;
  end: number;
  velocity: number;
};

const scale: Note[] = [];

const scales = {
  'Minor Pentatonic': [0, 3, 5, 7, 10],
  Minor: [0, 2, 3, 5, 7, 8, 10],
  'Minor Blues': [0, 3, 5, 6, 7, 10],
  'Minor Harmonic': [0, 2, 3, 5, 7, 8, 11],
  'Minor Hungarian': [0, 2, 3, 6, 7, 8, 11],
  'Minor Melodic Down': [0, 2, 3, 5, 7, 8, 10],
  'Minor Melodic Up': [0, 2, 3, 5, 7, 9, 11],
  Major: [0, 2, 4, 5, 7, 9, 11],
  'Major Harmonic': [0, 2, 4, 5, 7, 8, 11],
  'Major Pentatonic': [0, 2, 4, 7, 9],
  '8-Tone Spanish': [0, 1, 3, 4, 5, 6, 8, 10],
  Bhairav: [0, 1, 4, 5, 7, 8, 11],
  'Dorian #4': [0, 2, 3, 6, 7, 9, 10],
  'Dorian Mode': [0, 2, 3, 5, 7, 9, 10],
  'Half-Whole Diminished': [0, 1, 3, 4, 6, 7, 9, 10],
  Hirajoshi: [0, 2, 3, 7, 8],
  Insen: [0, 1, 5, 7, 10],
  Iwato: [0, 1, 5, 6, 10],
  Kumoi: [0, 2, 3, 7, 9],
  'Locrian Mode': [0, 1, 3, 5, 6, 8, 10],
  'Locrian Super': [0, 1, 3, 4, 6, 8, 10],
  'Lydian Augmented': [0, 2, 4, 6, 8, 9, 11],
  'Lydian Dominant': [0, 2, 4, 6, 7, 9, 10],
  'Lydian Mode': [0, 2, 4, 6, 7, 9, 11],
  'Messiaen 3': [0, 2, 3, 4, 6, 7, 8, 10, 11],
  'Messiaen 4': [0, 1, 2, 5, 6, 7, 8, 11],
  'Messiaen 5': [0, 1, 5, 6, 7, 11],
  'Messiaen 6': [0, 2, 4, 5, 6, 8, 10, 11],
  'Messiaen 7': [0, 1, 2, 3, 5, 6, 7, 8, 9, 11],
  'Mixolydian Mode': [0, 2, 4, 5, 7, 9, 10],
  'Pelog Selisir': [0, 1, 3, 7, 8],
  'Pelog Tembung': [0, 1, 5, 7, 8],
  'Phrygian Dominant': [0, 1, 4, 5, 7, 8, 10],
  'Phrygian Mode': [0, 1, 3, 5, 7, 8, 10],
  'Whole Tone': [0, 2, 4, 6, 8, 10],
  'Whole-Half Diminished': [0, 2, 3, 5, 6, 8, 9, 11],
};

const sortedScalesNames = Object.keys(scales).sort();

const noteLengths: number[] = []
const spacesBetweenNotes: number[] = []
for (let i = 0.25; i <= 4; i += 0.25) {
  noteLengths.push(i)
  spacesBetweenNotes.push(i)
}

const startOctaves: number[] = []
const endOctaves: number[] = []
for (let i = 0; i <= 10; i ++) {
  startOctaves.push(i)
  endOctaves.push(i)
}

const roots: number[] = []
for (let i = 0; i <= 11; i ++) {
  roots.push(i)
}

type GenerateScaleOptions = {
  noteLength: number;
  spaceBetweenNotes: number;
  startOctave: number;
  endOctave: number;
  root: number;
}

const generateScale = (scale: number[], options: GenerateScaleOptions) => {
  const generatedScale: Note[] = [];
  const noOfNotes = Math.abs(options.endOctave - options.startOctave) * scale.length + 1
  const isAscending = (options.endOctave - options.startOctave) > 0
  const pitches: number[] = [];
  
  for (let i = 0; i < noOfNotes; i ++) {
    isAscending ? (
      pitches.push(options.root + (options.startOctave + Math.floor(i / scale.length)) * 12 + scale[i % scale.length])
     ) : (
      pitches.unshift(options.root + (options.endOctave + Math.floor(i / scale.length)) * 12 + scale[i % scale.length])
     )
  }

  for (let i = 0; i < noOfNotes; i ++) {
    generatedScale.push({
      start: i * options.spaceBetweenNotes,
      end: i * options.spaceBetweenNotes + options.noteLength,
      pitch: pitches[i],
      velocity: 1
    })
  }

  return generatedScale
}

const describeAndGenerateScale = (scaleName: string, options: GenerateScaleOptions) => {
  console.log(`scaleName: ${scaleName}`)
  return {
    notes: generateScale(scales[scaleName], options),
    description: {
      scale: scaleName,
      ...options
    }
  }
}

const generate = (key: number) => {
  const scalesIndex = Math.floor(key / (roots.length * startOctaves.length * (endOctaves.length - 1) * spacesBetweenNotes.length * noteLengths.length)) % sortedScalesNames.length;
  const noteLengthsIndex = Math.floor(key / (roots.length * startOctaves.length * (endOctaves.length - 1) * spacesBetweenNotes.length)) % noteLengths.length;
  const spacesBetweenNotesIndex = Math.floor(key / (roots.length * startOctaves.length * (endOctaves.length - 1))) % spacesBetweenNotes.length;
  const endOctavesIndex = Math.floor(key / (roots.length * startOctaves.length)) % (endOctaves.length - 1);
  const startOctavesIndex = Math.floor(key / roots.length) % startOctaves.length;
  const rootsIndex = key % roots.length;

  const sameEndOctaveIndex = endOctaves.indexOf(startOctaves[startOctavesIndex]);
  if (sameEndOctaveIndex > -1) {
    endOctaves.splice(sameEndOctaveIndex, 1);
  }

  return describeAndGenerateScale(
    sortedScalesNames[scalesIndex],
    {
      noteLength: noteLengths[noteLengthsIndex],
      spaceBetweenNotes: spacesBetweenNotes[spacesBetweenNotesIndex],
      startOctave: startOctaves[startOctavesIndex],
      endOctave: endOctaves[endOctavesIndex],
      root: roots[rootsIndex]
    }
  )
}

console.log(JSON.stringify(generate(Number(process.argv[2])), null, 2))