const SampleVoice = ({ velocity, trigger, gate }, { attack, decay, sustain, release, output, buffer }) => {
  const readIndex = useVal(0);
  const isPlaying = useVal(false);

  const channelBuffer = buffer.channelDataList[0];

  const processEnvelope = useEnvelope();
  const amplitude = processEnvelope(trigger, gate, attack, decay, sustain, release);
  const totalAmplitude = useMonoSignal();
  multiply(amplitude, velocity, totalAmplitude);
  const voiceOutput = useMonoSignal();

  if (channelBuffer.length <= 1) return;

  voiceOutput.fill(0);
  for (let i = 0; i < output.length; i ++) {
    if (trigger[i] === 1) {
      isPlaying.current = true;
      readIndex.current = 0;
    } else if (amplitude[i] === 0) {
      isPlaying.current = false;
    }

    if (isPlaying.current) {
      if (readIndex.current < channelBuffer.length) {
        voiceOutput[i] += channelBuffer[readIndex.current];
        readIndex.current ++;
      }
    }
  }
  multiply(voiceOutput, totalAmplitude, voiceOutput);
  add(output, voiceOutput, output);
}

const sampleIds = ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6', 'sample7', 'sample8'];

const getSampleData = (scope) => {
  const results = [];
  for (let i = 0; i < sampleIds.length; i ++) {
    const pitches = [];
    for (let j = 0; j < 88; j += 12) {
      pitches.push(j + i)
    }
    results.push({
      pitches,
      scope: {
        ...scope,
        buffer: scope[sampleIds[i]]
      }
    });
  }
  return results;
}

const SamplerInstrument = (scope, globals) => {
  scope.output.fill(0);
  const sampleData = useReturnedConst(getSampleData, scope);

  for (let i = 0; i < sampleData.length; i ++) {
    const data = sampleData[i];
    const voice = useSingleVoice(scope.input, data.pitches);
    SampleVoice(voice, data.scope);
  }
}

this.constructDevice = () => constructPerChannelDevice({
  noteInputHint: {
    interface: NoteInterfaceType.Pads,
    usedPitches: {
      48: 'Sample 1',
      50: 'Sample 2',
      52: 'Sample 3',
      53: 'Sample 4',

      55: 'Sample 5',
      57: 'Sample 6',
      59: 'Sample 7',
      60: 'Sample 8',
    }
  },
  implementation: SamplerInstrument,
  inputs: {
    input: midi('In'),
    attack: adsrTimeSetting('Attack', 1),
    decay: adsrTimeSetting('Decay', 100),
    sustain: zeroOneSetting('Sustain', 0.25),
    release: adsrTimeSetting('Release', 50),
  },
  outputs: {
    output: stereo('Out')
  },
  constants: {
    sample1: audioBufferConstant('Sample 1'),
    sample2: audioBufferConstant('Sample 2'),
    sample3: audioBufferConstant('Sample 3'),
    sample4: audioBufferConstant('Sample 4'),

    sample5: audioBufferConstant('Sample 5'),
    sample6: audioBufferConstant('Sample 6'),
    sample7: audioBufferConstant('Sample 7'),
    sample8: audioBufferConstant('Sample 8'),
  },
  layout: col([
    row([
      audioBufferPicker('sample5'),
      audioBufferPicker('sample6'),
      audioBufferPicker('sample7'),
      audioBufferPicker('sample8'),
    ]),
    row([
      audioBufferPicker('sample1'),
      audioBufferPicker('sample2'),
      audioBufferPicker('sample3'),
      audioBufferPicker('sample4'),
    ]),
    themed(DeviceLayoutTheme.Skeuomorphic, row([
      titled.knob('attack'),
      titled.knob('decay'),
      titled.knob('sustain'),
      titled.knob('release'),
    ]))
  ])
});

