/**
 * Description: This is a SoundFont2 parser.
 *
 * @package     midi-Synth
 * @category    module js
 * @author      Mrtenz <https://github.com/Mrtenz/soundfont2>
 * @copyright   soundfont2
 * @license     GPL-2.0-or-later
 */

/**
 * An array of GeneratorTypes that cannot be specified for presets. If one of these generator types
 * is found, the generator should be ignored.
 */
const PRESET_TYPES_BLACKLIST = [
  GeneratorType.StartAddrsOffset,
  GeneratorType.EndAddrsOffset,
  GeneratorType.StartLoopAddrsOffset,
  GeneratorType.EndLoopAddrsOffset,
  GeneratorType.StartAddrsCoarseOffset,
  GeneratorType.EndAddrsCoarseOffset,
  GeneratorType.StartLoopAddrsCoarseOffset,
  GeneratorType.KeyNum,
  GeneratorType.Velocity,
  GeneratorType.EndLoopAddrsCoarseOffset,
  GeneratorType.SampleModes,
  GeneratorType.ExclusiveClass,
  GeneratorType.OverridingRootKey
]

/**
 * An array of GeneratorTypes that cannot be specified for instruments. If one of these generator
 * types is found, the generator should be ignored.
 */
const INSTRUMENT_TYPES_BLACKLIST = [
  GeneratorType.Unused1,
  GeneratorType.Unused2,
  GeneratorType.Unused3,
  GeneratorType.Unused4,
  GeneratorType.Reserved1,
  GeneratorType.Reserved2,
  GeneratorType.Reserved3
]

/**
 * These GeneratorTypes specify a range of key numbers or velocity.
 */
const RANGE_TYPES = [GeneratorType.KeyRange, GeneratorType.VelRange]

/**
 * Get all generators for either an preset generator chunk or a instrument generator chunk.
 *
 * TODO: Check if generator chunk is valid, by following the rules defined in the spec. See for
 * example: https://github.com/FluidSynth/fluidsynth/blob/v2.0.3/src/sfloader/fluid_sffile.c
 *
 * @param {SF2Chunk} chunk - The input chunk
 * @param {string} type - The type, can be 'pgen' or 'igen'
 */
const getGenerators = (chunk, type) => {
  if (chunk.id !== type) {
    throw new ParseError("Unexpected chunk ID", `'${type}'`, `'${chunk.id}'`)
  }

  if (chunk.length % SF_GENERATOR_SIZE) {
    throw new ParseError(`Invalid size for the '${type}' sub-chunk`)
  }

  return chunk.iterate(iterator => {
    const id = iterator.getInt16()

    // Ignore invalid IDs
    if (!GeneratorType[id]) {
      return null
    }

    if (type === "pgen" && PRESET_TYPES_BLACKLIST.includes(id)) {
      return null
    }

    if (type === "igen" && INSTRUMENT_TYPES_BLACKLIST.includes(id)) {
      return null
    }

    if (RANGE_TYPES.includes(id)) {
      return {
        id,
        range: {
          lo: iterator.getByte(),
          hi: iterator.getByte()
        }
      }
    }

    return {
      id,
      value: iterator.getInt16BE()
    }
  })
}
