import { useEffect, useState } from 'react';
import { isNote } from 'tone';
import _WebMidi from 'webmidi';
import { Pattern as _Pattern } from '../../strudel.mjs';
import * as Tone from 'tone';

const WebMidi: any = _WebMidi;
const Pattern = _Pattern as any;

export default function enableWebMidi() {
  return new Promise((resolve, reject) => {
    if (WebMidi.enabled) {
      // if already enabled, just resolve WebMidi
      resolve(WebMidi);
      return;
    }
    WebMidi.enable((err: any) => {
      if (err) {
        reject(err);
      }
      resolve(WebMidi);
    });
  });
}
const outputByName = (name: string) => WebMidi.getOutputByName(name);

Pattern.prototype.midi = function (output: string, channel = 1) {
  if (output?.constructor?.name === 'Pattern') {
    throw new Error(
      `.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
        WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
      }')`
    );
  }
  return this._withEvent((event: any) => {
    const onTrigger = (time: number, event: any) => {
      let note = event.value;
      const velocity = event.context?.velocity ?? 0.9;
      if (!isNote(note)) {
        throw new Error('not a note: ' + note);
      }
      if (!WebMidi.enabled) {
        throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
      }
      if (!WebMidi.outputs.length) {
        throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
      }
      const device = output ? outputByName(output) : WebMidi.outputs[0];
      if (!device) {
        throw new Error(
          `🔌 MIDI device '${output ? output : ''}' not found. Use one of ${WebMidi.outputs
            .map((o: any) => `'${o.name}'`)
            .join(' | ')}`
        );
      }
      // console.log('midi', value, output);
      const timingOffset = WebMidi.time - Tone.context.currentTime * 1000;
      time = time * 1000 + timingOffset;
      // const inMs = '+' + (time - Tone.context.currentTime) * 1000;
      // await enableWebMidi()
      device.playNote(note, channel, {
        time,
        duration: event.duration * 1000 - 5,
        velocity,
      });
    };
    return event.setContext({ ...event.context, onTrigger });
  });
};

export function useWebMidi(props?: any) {
  const { ready, connected, disconnected } = props;
  const [loading, setLoading] = useState(true);
  const [outputs, setOutputs] = useState<any[]>(WebMidi?.outputs || []);
  useEffect(() => {
    enableWebMidi()
      .then(() => {
        // Reacting when a new device becomes available
        WebMidi.addListener('connected', (e: any) => {
          setOutputs([...WebMidi.outputs]);
          connected?.(WebMidi, e);
        });
        // Reacting when a device becomes unavailable
        WebMidi.addListener('disconnected', (e: any) => {
          setOutputs([...WebMidi.outputs]);
          disconnected?.(WebMidi, e);
        });
        ready?.(WebMidi);
        setLoading(false);
      })
      .catch((err: Error) => {
        if (err) {
          //throw new Error("Web Midi could not be enabled...");
          console.warn('Web Midi could not be enabled..');
          return;
        }
      });
  }, [ready, connected, disconnected, outputs]);
  const outputByName = (name: string) => WebMidi.getOutputByName(name);
  return { loading, outputs, outputByName };
}
