import { hoverTooltip } from "@codemirror/view";
import { type EditorView } from "@codemirror/view";
import { CompletionContext } from "@codemirror/autocomplete";
// @ts-ignore
import { soundMap } from "superdough";

interface InlineCompletion {
  name: string;
  category: string;
  description: string;
  example: string;
}

type CompletionDatabase = {
  [key: string]: InlineCompletion;
};

const completionDatabase: CompletionDatabase = {
  seed: {
    name: "seed",
    category: "randomness",
    description: "Seed the random generator",
    example: "seed(1234)",
  },
  delayr: {
    name: "delayr",
    category: "time",
    description: "Delay a function <i>n</i> times by <i>t</i> ms",
    example: "delayr(50,3,()=> beat(1)::log('hey!'))",
  },
  toss: {
    name: "toss",
    category: "randomness",
    description: "Toss a coin, true or false",
    example: "toss() : log('heads')",
  },
  lpadsr: {
    name: "lpadsr",
    category: "synthesis",
    description: "Lowpass filter ADSR envelope",
    example: "sound('sawtooth').lpadsr(2,0,.1,0,0).out()",
  },
  lpenv: {
    name: "lpenv",
    category: "synthesis",
    description: "Lowpass filter ADSR envelope amount",
    example: "sound('sawtooth').lpenv(2).lpd(.2).out()",
  },
  hpadsr: {
    name: "hpadsr",
    category: "synthesis",
    description: "Highpass filter ADSR envelope",
    example: "sound('sawtooth').hpadsr(2, 0, .1, 0, 0).out()",
  },
  hpenv: {
    name: "hpenv",
    category: "synthesis",
    description: "Highpass filter ADSR envelope amount",
    example: "sound('sawtooth').hpenv(2).hpd(.2).out()",
  },
  bpadsr: {
    name: "bpadsr",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope",
    example: "sound('sawtooth').bpadsr(2, 0, .1, 0, 0).out()",
  },
  bpattack: {
    name: "bpattack",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope attack time (in seconds)",
    example: "sound('sawtooth').bpf(2000).bpenv(2).bpa(.2).out()",
  },
  bpdecay: {
    name: "bpdecay",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope decay time (in seconds)",
    example: "sound('sawtooth').bpf(2000).bpenv(2).bpd(.2).out()",
  },
  bpsustain: {
    name: "bpsustain",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope sustain level (0-1)",
    example: "sound('sawtooth').bpf(2000).bpenv(2).bpsustain(0.5).out()",
  },
  bprelease: {
    name: "bprelease",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope release time (in seconds)",
    example: "sound('sawtooth').bpf(2000).bpenv(2).bpr(.2).out()",
  },
  bpenv: {
    name: "bpenv",
    category: "synthesis",
    description: "Bandpass filter ADSR envelope amount",
    example: "sound('sawtooth').bpenv(2).bpd(.2).out()",
  },
  adsr: {
    name: "adsr",
    category: "synthesis",
    description: "ADSR envelope",
    example: "sound('sawtooth').adsr(0, .1, 0, 0).out()",
  },
  ad: {
    name: "ad",
    category: "synthesis",
    description: "AD envelope times (in seconds)",
    example: "sound('sawtooth').ad(0, .1).out()",
  },
  attack: {
    name: "attack",
    category: "synthesis",
    description: "ADSR envelope attack time (in seconds)",
    example: "sound('sawtooth').attack(.5).out()",
  },
  decay: {
    name: "decay",
    category: "synthesis",
    description: "ADSR envelope decay time (in seconds)",
    example: "sound('sawtooth').decay(.5).out()",
  },
  sustain: {
    name: "sustain",
    category: "synthesis",
    description: "ADSR envelope sustain level (0-1)",
    example: "sound('sawtooth').sustain(.5).out()",
  },
  release: {
    name: "release",
    category: "synthesis",
    description: "ADSR envelope release time (in seconds)",
    example: "sound('sawtooth').release(.5).out()",
  },
  fmwave: {
    name: "fmwave",
    category: "synthesis",
    description: "FM synth modulator waveform",
    example: "sound('fm').fmwave('sine').out()",
  },
  fmi: {
    name: "fmi",
    category: "audio",
    description: "FM synth modulator index",
    example: "sound('fm').fmi([1,2].beat()).out()",
  },
  fmh: {
    name: "fmh",
    category: "audio",
    description: "FM synth modulator ratio",
    example: "sound('fm').fmi(2).fmh(2).out()",
  },
  fmattack: {
    name: "fmattack",
    category: "synthesis",
    description: "FM synth modulator ADSR envelope attack time (in seconds)",
    example: "sound('sine').fmi(2).fmattack(.5).out()",
  },
  fmdecay: {
    name: "fmdecay",
    category: "synthesis",
    description: "FM synth modulator ADSR envelope decay time (in seconds)",
    example: "sound('sine').fmi(2).fmdecay(.5).out()",
  },
  fmsustain: {
    name: "fmsustain",
    category: "synthesis",
    description: "FM synth modulator ADSR envelope sustain level (0-1)",
    example: "sound('sine').fmi(2).fmsustain(.5).out()",
  },
  fmrelease: {
    name: "fmrelease",
    category: "synthesis",
    description: "FM synth modulator ADSR envelope release time (in seconds)",
    example: "sound('sine').fmi(2).fmrelease(.5).out()",
  },
  repeat: {
    name: "repeat",
    category: "patterns",
    description: "Repeat every array elements <i>n</i> times",
    example: "[0,1,2,3].repeat(2)",
  },
  quant: {
    name: "quant",
    category: "functions",
    description: "Quantize a value in the given array",
    example: "quant(30, [0,1,2,3])",
  },
  log: {
    name: "log",
    category: "javascript",
    description: "Log a value in the console",
    example: "log('Hello, world')",
  },
  flip: {
    name: "flip",
    category: "patterns",
    description:
      "Returns  true and false alternatively or next value every <i>n</i> beats (arrays)",
    example: "flip(4, 50) // 4 beats of true, 4 beats of false, 50/50.",
  },
  n: {
    name: "n",
    category: "audio",
    description: "Sample number or synth oscillator partials count",
    example: "sound('dr').n([1,2].beat()).out()",
  },
  note: {
    name: "note",
    category: "patterns",
    description: "MIDI note number (0-127)",
    example: "sound('jvbass').note(50).out()",
  },
  vel: {
    name: "vel",
    category: "audio",
    description: "Velocity or sound volume (0-1)",
    example: "sound('cp').vel(.5).out()",
  },
  palindrome: {
    name: "palindrome",
    category: "patterns",
    description: "Returns palindrome of the current array",
    example: "[0,1,2,3].palindrome()",
  },
  cutoff: {
    name: "cutoff",
    category: "filter",
    description: "Lowpass filter cutoff frequency",
    example: "sound('cp').cutoff(1000).out()",
  },
  resonance: {
    name: "resonance",
    category: "filter",
    description: "Lowpass filter resonance",
    example: "sound('cp').resonance(1).out()",
  },
  hcutoff: {
    name: "hcutoff",
    category: "filter",
    description: "Highpass filter cutoff frequency",
    example: "sound('cp').hcutoff(1000).out()",
  },
  hresonance: {
    name: "hresonance",
    category: "filter",
    description: "Highpass filter resonance",
    example: "sound('cp').hresonance(1).out()",
  },
  bandf: {
    name: "bandf",
    category: "filter",
    description: "Bandpass filter cutoff frequency",
    example: "sound('cp').bandf(1000).out()",
  },
  bandq: {
    name: "bandq",
    category: "filter",
    description: "Bandpass filter resonance",
    example: "sound('cp').bandq(1).out()",
  },
  vowel: {
    name: "vowel",
    category: "filter",
    description: "Vowel filter type",
    example: "sound('cp').vowel('a').out()",
  },
  coarse: {
    name: "coarse",
    category: "synthesis",
    description: "Artificial sample-rate lowering",
    example: "beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out();",
  },
  crush: {
    name: "crush",
    category: "synthesis",
    description:
      "Bitcrushing effect. <i>1</i> is extreme, superior values are more subtle.",
    example: "",
  },
  speed: {
    name: "speed",
    category: "sampling",
    description: "Sample playback speed",
    example: "sound('cp').speed(.5).out()",
  },
  shape: {
    name: "shape",
    category: "synthesis",
    description: "Waveshaping distorsion",
    example: "sound('cp').shape(.5).out()",
  },
  delay: {
    name: "delay",
    category: "effect",
    description: "Delay effect dry/wet",
    example: "sound('cp').delay(.5).out()",
  },
  delayfb: {
    name: "delayfb",
    category: "effect",
    description: "Delay effect feedback amount (0-1)",
    example: "sound('cp').delay(0.2).delayfb(.5).out()",
  },
  delaytime: {
    name: "delaytime",
    category: "effect",
    description: "Delay effect delay time (in seconds)",
    example: "sound('cp').delay(0.2).delaytime(.5).out()",
  },
  gain: {
    name: "gain",
    category: "audio",
    description: "Playback volume",
    example: "sound('cp').gain(.5).out()",
  },
  bar: {
    name: "bar",
    category: "patterns",
    description: "Returns list index for the current bar (with wrapping)",
    example: "[0,1,2,3].bar()",
  },
  room: {
    name: "room",
    category: "effect",
    description: "Reverb effect room amount (0-1 or +)",
    example: "sound('cp').room(.5).out()",
  },
  size: {
    name: "size",
    category: "effect",
    description: "Reverb effect room size",
    example: "sound('cp').size(.5).out()",
  },
  roomlp: {
    name: "roomlp",
    category: "effect",
    description: "Reverb effect room lowpass filter cutoff frequency",
    example: "sound('cp').room(.5).size(.5).roomlp(.5).out()",
  },
  roomfade: {
    name: "roomfade",
    category: "effect",
    description: "Reverb effect room fade time (in seconds)",
    example: "sound('cp').room(.5).size(.5).roomfade(.5).out()",
  },
  roomdim: {
    name: "roomdim",
    category: "effect",
    description: "Reverb lowpass frequency at -60db (in hertz)",
    example: "sound('cp').room(.5).size(.5).roomdim(.5).out()",
  },
  usine: {
    name: "usine",
    category: "modulation",
    description: "Unipolar sinusoïdal low-frequency oscillator",
    example: "usine(5) // 5 hz oscillation",
  },
  sine: {
    name: "usine",
    category: "modulation",
    description: "Sinusoïdal low-frequency oscillator",
    example: "usine(5) // 5 hz oscillation",
  },
  utriangle: {
    name: "utriangle",
    category: "modulation",
    description: "Unipolar triangular low-frequency oscillator",
    example: "utriangle(5) // 5 hz oscillation",
  },
  triangle: {
    name: "triangle",
    category: "modulation",
    description: "Triangular low-frequency oscillator",
    example: "triangle(5) // 5 hz oscillation",
  },
  usaw: {
    name: "usaw",
    category: "modulation",
    description: "Unipolar sawtooth low-frequency oscillator",
    example: "usaw(5) // 5 hz oscillation",
  },
  saw: {
    name: "saw",
    category: "modulation",
    description: "Sawtooth low-frequency oscillator",
    example: "saw(5) // 5 hz oscillation",
  },
  square: {
    name: "square",
    category: "modulation",
    description: "Square low-frequency oscillator",
    example: "square(5) // 5 hz oscillation",
  },
  usquare: {
    name: "usquare",
    category: "modulation",
    description: "Unipolar square low-frequency oscillator",
    example: "usquare(5) // 5 hz oscillation",
  },
  rhythm: {
    name: "rhythm",
    category: "rhythm",
    description: "Variant of the euclidian algorithm function",
    example: "rhythm(.5, 3, 8) // time, pulses, steps",
  },
  let: {
    name: "let",
    category: "javascript",
    description: "Variable assignation",
    example: "let baba = 10",
  },
  onbeat: {
    name: "onbeat",
    category: "rhythm",
    description: "Return true when targetted beat(s) is/are reached",
    example: "onbeat([1,2,3]) // true on beats 1, 2 and 3",
  },
  oncount: {
    name: "oncount",
    category: "rhythm",
    description:
      "Return true when targetted beat(s) is/are reached in the given period",
    example:
      "oncount([1,2,3], 4) // true on beats 1, 2 and 3 in a 4 beats period",
  },
  beat: {
    name: "beat",
    category: "rhythm",
    description: "return true every <i>n</i> beats.",
    example: "beat(1) :: log(rand(1,5))",
  },
  pulse: {
    name: "pulse",
    category: "rhythm",
    description: "return true every <i>n</i> pulses.",
    example: "pulse(8) :: log(rand(1,5))",
  },
  euclid: {
    name: "euclid",
    category: "rhythm",
    description: "Iterator-based euclidian rhythm generator",
    example: "euclid($(1), 3, 8) // iterator, pulses",
  },
  oneuclid: {
    name: "oneuclid",
    category: "rhythm",
    description: "Variant of the euclidian rhythm generator",
    example: "oneuclid(3, 8) // iterator, pulses",
  },
  bin: {
    name: "bin",
    category: "rhythm",
    description: "Convert a decimal number to binary rhythm generator",
    example: "bin($(1), 9223) // iterator, number to convert",
  },
  binrhythm: {
    name: "binrhythm",
    category: "rhythm",
    description: "Binary rhythm generator (time, number)",
    example: "binrhythm(.5, 9223) :: sound('cp').out()",
  },
  prob: {
    name: "prob",
    category: "randomness",
    description: "Return true with a probability of <i>n</i> %",
    example: "prob(50) // 50% probability",
  },
  rand: {
    name: "rand",
    category: "randomness",
    description: "random floating point number between x and y",
    example: "rand(1, 10) // between 1 and 10",
  },
  irand: {
    name: "irand",
    category: "randomness",
    description: "random integer number between x and y",
    example: "irand(1, 10) // between 1 and 10",
  },
  pick: {
    name: "pick",
    category: "randomness",
    description: "Pick a value in the given array",
    example: "[1,4,10].pick()",
  },
  odds: {
    name: "odds",
    category: "randomness",
    description: "Return true with a probability of <i>n</i> %",
    example: "odds(1/2) // 50% probability",
  },
  never: {
    name: "never",
    category: "randomness",
    description: "Return false",
    example: "never() // false",
  },
  almostNever: {
    name: "almostNever",
    category: "randomness",
    description: "Return true with a probability of 2.5%",
    example: "almostNever() // 2.5% chance",
  },
  rarely: {
    name: "rarely",
    category: "randomness",
    description: "Return true with a probability of 10%",
    example: "rarely() // 10% chance",
  },
  scarcely: {
    name: "scarcely",
    category: "randomness",
    description: "Return true with a probability of 25%",
    example: "scarcely() // 25% chance",
  },
  sometimes: {
    name: "sometimes",
    category: "randomness",
    description: "Return true with a probability of 50%",
    example: "sometimes() // 50% chance",
  },
  often: {
    name: "often",
    category: "randomness",
    description: "Return true with a probability of 75%",
    example: "often() // 75% chance",
  },
  frequently: {
    name: "frequently",
    category: "randomness",
    description: "Return true with a probability of 90%",
    example: "frequently() // chance",
  },
  almostAlways: {
    name: "almostAlways",
    category: "randomness",
    description: "Return true with a probability of 98.5%",
    example: "almostAlways() // 98.5% chance",
  },
  always: {
    name: "always",
    category: "randomness",
    description: "Return true",
    example: "always() // true",
  },
  sound: {
    name: "sound",
    category: "audio",
    description: "Base function to play audio (samples / synths)",
    example: "sound('bd').out()",
  },
  snd: {
    name: "snd",
    category: "audio",
    description:
      "Base function to play audio (samples / synths). Alias for <code>sound<code>.",
    example: "sound('bd').out()",
  },
  tempo: {
    name: "tempo",
    category: "time",
    description: "Get or set the current beats per minute.",
    example: "tempo(135) // set the bpm to 135",
  },
  out: {
    name: "out",
    category: "audio",
    description: "Connect the <code>sound()</code> chain to the output",
    example: "sound('clap').out()",
  },
  script: {
    name: "script",
    category: "core",
    description: "Execute one or more local scripts",
    example: "beat(1) :: script(1)",
  },
  clear_script: {
    name: "clear_script",
    category: "core",
    description: "Deletes the given script",
    example: "clear_script(2)",
  },
  copy_script: {
    name: "copy_script",
    category: "core",
    description: "Copy the script <i>from</i> to the script <i>to</i>",
    example: "copy_script(1, 2)",
  },
  warp: {
    name: "warp",
    category: "core",
    description: "jumps to the <i>n</i> tick of the clock.",
    example: "warp(1) :: log('back to the big bang!')",
  },
  beat_warp: {
    name: "beat_warp",
    category: "core",
    description: "jumps to the <i>n</i> beat of the clock.",
    example: "beat_warp(1) :: log('back to the first beat!')",
  },
  flipbar: {
    name: "flipbar",
    category: "time",
    description:
      "works just like <i>flip</i> at the level of bars instead of beats",
    example: "flipbar(2)::beat(1)::snd('kick').out()",
  },
  onbar: {
    name: "onbar",
    category: "time",
    description: "return true when targetted bar(s) is/are reached in period",
    example: "onbar(4, 4)::beat(.5)::snd('hh').out();",
  },
  begin: {
    name: "begin",
    category: "sampling",
    description: "Audio playback start time (0-1)",
    example: "sound('cp').begin(.5).out()",
  },
  end: {
    name: "end",
    category: "sampling",
    description: "Audio playback end time (0-1)",
    example: "sound('cp').end(.5).out()",
  },
  mouseX: {
    name: "mouseX",
    category: "mouse",
    description: "Mouse X position (big float)",
    example: "log(mouseX())",
  },
  mouseY: {
    name: "mouseY",
    category: "mouse",
    description: "Mouse Y position (big float)",
    example: "log(mouseY())",
  },
  noteX: {
    name: "noteX",
    category: "mouse",
    description: "Mouse X position (as MIDI note)",
    example: "log(noteX())",
  },
  noteY: {
    name: "noteY",
    category: "mouse",
    description: "Mouse Y position (as MIDI note)",
    example: "log(noteY())",
  },
  cut: {
    name: "cut",
    category: "sampling",
    description: "Cutting sample when other sample met on same orbit (0 or 1)",
    example: "sound('cp').cut(1).out()",
  },
  pan: {
    name: "pan",
    category: "audio",
    description: "Stereo panning (-1 to 1)",
    example: "sound('cp').pan(-1).out()",
  },
  zrand: {
    name: "zrand",
    category: "synthesis",
    description: "ZzFX randomisation factor",
    example: "sound('zzfx').zrand(.5).out()",
  },
  curve: {
    name: "curve",
    category: "synthesis",
    description: "ZzFX waveshaping (0-3)",
    example: "sound('zzfx').curve(1).out()",
  },
  slide: {
    name: "slide",
    category: "synthesis",
    description: "ZzFX pitch slide",
    example: "sound('zzfx').slide(1).out()",
  },
  deltaSlide: {
    name: "deltaSlide",
    category: "synthesis",
    description: "ZzFX pitch delta slide",
    example: "sound('zzfx').deltaSlide(1).out()",
  },
  pitchJump: {
    name: "pitchJump",
    category: "synthesis",
    description: "ZzFX pitch jump",
    example: "sound('zzfx').pitchJump(1).out()",
  },
  pitchJumpTime: {
    name: "pitchJumpTime",
    category: "synthesis",
    description: "ZzFX pitch jump time (time before jump)",
    example: "sound('zzfx').pitchJumpTime(1).out()",
  },
  zcrush: {
    name: "zcrush",
    category: "synthesis",
    description: "ZzFX bitcrushing",
    example: "sound('zzfx').zcrush(1).out()",
  },
  zdelay: {
    name: "zdelay",
    category: "synthesis",
    description: "ZzFX delay",
    example: "sound('zzfx').zdelay(1).out()",
  },
  tremolo: {
    name: "tremolo",
    category: "synthesis",
    description: "ZzFX weird tremolo effect",
    example: "sound('zzfx').tremolo(1).out()",
  },
  speak: {
    name: "speak",
    category: "synthesis",
    description: "Text to speech synthesizer",
    example: "beat(2) :: speak('Topos!','fr',irand(0,5))",
  },
  midi_outputs: {
    name: "midi_outputs",
    category: "midi",
    description: "List of available MIDI outputs",
    example: "midi_outputs()",
  },
  midi_output: {
    name: "midi_output",
    category: "midi",
    description: "Set the current MIDI output",
    example: "midi_output('IAC Driver Bus 1')",
  },
  midi: {
    name: "midi",
    category: "midi",
    description: "Send a MIDI message",
    example: "midi(144, 60, 100)",
  },
  control_change: {
    name: "control_change",
    category: "midi",
    description: "Send a MIDI control change message",
    example: "control_change({control: 1, value: 60, channel: 10})",
  },
  program_change: {
    name: "program_change",
    category: "midi",
    description: "Send a MIDI program change message",
    example: "program_change(1, 10)",
  },
  sysex: {
    name: "sysex",
    category: "midi",
    description: "Send a MIDI sysex message",
    example: "sysex(0xF0, 0x7D, 0x00, 0x06, 0x01, 0xF7)",
  },
  midi_clock: {
    name: "midi_clock",
    category: "midi",
    description: "Send a MIDI clock message",
    example: "midi_clock()",
  },
  degrade: {
    name: "degrade",
    category: "patterns",
    description: "Removes <i>n</i>% of the given array randomly",
    example: "[0,1,2,3].degrade(20)",
  },
  loop: {
    name: "loop",
    category: "patterns",
    description: "Loop over the given array using an iterator",
    example: "[0,1,2,3].loop($(1))",
  },
  $: {
    name: "$",
    category: "patterns",
    description: "Iterator",
    example: "[0,1,2,3].loop($(1))",
  },
  counter: {
    name: "counter",
    category: "patterns",
    description: "Counter/iterator",
    example: "counter('my_counter_, 20, 1)",
  },
  drunk: {
    name: "drunk",
    category: "patterns",
    description: "Returns the next value in a drunk walk",
    example: "drunk()",
  },
  drunk_max: {
    name: "drunk_max",
    category: "patterns",
    description: "Sets the maximum value of the drunk walk",
    example: "drunk_max(10)",
  },
  drunk_min: {
    name: "drunk_min",
    category: "patterns",
    description: "Sets the minimum value of the drunk walk",
    example: "drunk_min(0)",
  },
  drunk_wrap: {
    name: "drunk_wrap",
    category: "patterns",
    description: "Wraps (or not) of the drunk walk (boolean)",
    example: "drunk_wrap(true)",
  },
  v: {
    name: "v",
    category: "variable",
    description: "Global Variable setter or getter",
    example: "v('my_var', 10) // Sets global variable 'my_var' to 10",
  },
  delete_variable: {
    name: "delete_variable",
    category: "variable",
    description: "Deletes the given global variable",
    example: "delete_variable('my_var')",
  },
  clear_variables: {
    name: "clear_variables",
    category: "variable",
    description: "Clears all global variables",
    example: "clear_variables()",
  },
  shuffle: {
    name: "shuffle",
    category: "patterns",
    description: "Shuffle the given array",
    example: "[0,1,2,3].shuffle()",
  },
  rotate: {
    name: "rotate",
    category: "patterns",
    description: "Rotate the given array to the right for <i>n</i> indexes",
    example: "[0,1,2,3].rotate(2)",
  },
  unique: {
    name: "unique",
    category: "patterns",
    description: "Remove duplicates from the given array",
    example: "[0,1,2,3,3,3].unique()",
  },
  add: {
    name: "add",
    category: "patterns",
    description: "Add a value to each element of the given array",
    example: "[0,1,2,3].add(1)",
  },
  sub: {
    name: "sub",
    category: "patterns",
    description: "Substract a value to each element of the given array",
    example: "[0,1,2,3].sub(1)",
  },
  mul: {
    name: "mul",
    category: "patterns",
    description: "Multiply each element of the given array by a value",
    example: "[0,1,2,3].mul(2)",
  },
  div: {
    name: "div",
    category: "patterns",
    description: "Divide each element of the given array by a value",
    example: "[0,1,2,3].div(2)",
  },
  scale: {
    name: "scale",
    category: "patterns",
    description: "Scale setter used by Ziffers",
    example: "z0('0 1 2 3').scale('major').out()",
  },
  zzfx: {
    name: "zzfx",
    category: "synthesis",
    description: "ZzFX sound generator",
    example: "sound('zzfx').zzfx(...).out()",
  },
  vib: {
    name: "vib",
    category: "synthesis",
    description: "Vibrato frequency",
    example: "sound('sine').vib(1).vibmod(0.5).out()",
  },
  vibmod: {
    name: "vibmod",
    category: "synthesis",
    description: "Vibrato modulation amount",
    example: "sound('sine').vib(1).vibmod(4).out()",
  },
  fill: {
    name: "fill",
    category: "performance",
    description: "Detects if the Alt key is pressed",
    example: "fill() ? 1 : 0.5",
  },
  ratio: {
    name: "ratio",
    category: "synthesis",
    description: "Compressor ratio",
    example: "sound('sine').comp(1).ratio(4).out()",
  },
  knee: {
    name: "knee",
    category: "synthesis",
    description: "Compressor knee (dB)",
    example: "sound('sine').comp(-4).knee(4).out()",
  },
  compAttack: {
    name: "compAttack",
    category: "synthesis",
    description: "Compressor attack time (in seconds)",
    example: "sound('sine').comp(-4).compAttack(0.1).out()",
  },
  compRelease: {
    name: "compRelease",
    category: "synthesis",
    description: "Compressor release time (in seconds)",
    example: "sound('sine').comp(-4).compRelease(0.1).out()",
  },
  noise: {
    name: "noise",
    category: "synthesis",
    description: "Noise amount in the signal (0-1)",
    example: "sound('triangle').noise(.25).out()",
  },
};

export const inlineHoveringTips = hoverTooltip(
  (view: any, pos: any, side: any) => {
    let { from, to, text } = view.state.doc.lineAt(pos);
    let start = pos,
      end = pos;
    while (start > from && /\w/.test(text[start - from - 1])) start--;
    while (end < to && /\w/.test(text[end - from])) end++;
    if ((start == pos && side < 0) || (end == pos && side > 0)) return null;
    return {
      pos: start,
      end,
      above: true,
      // @ts-ignore
      create(view: EditorView) {
        if (
          text.slice(start - from, end - from) in completionDatabase ===
          false
        ) {
          return { dom: document.createElement("div") };
        }
        let completion =
          completionDatabase[text.slice(start - from, end - from)] || {};
        let divContent = `
      <h1 class="text-orange-300 text-base pb-1">${completion.name} [<em class="text-white">${completion.category}</em>]</h1>
      <p class="text-base pl-4">${completion.description}</p>
      <pre class="-mt-2"><code class="pl-4 text-base">${completion.example}</code></pre></div>
      `;
        let dom = document.createElement("div");
        dom.classList.add("px-4", "py-2", "bg-neutral-700", "rounded-lg");
        dom.innerHTML = divContent;
        return { dom };
      },
    };
  },
);

export const toposCompletions = (context: CompletionContext) => {
  let word = context.matchBefore(/\w*/);
  if (word) {
    if (word.from == word.to && !context.explicit) return null;
    return {
      from: word.from,
      options: Object.keys(completionDatabase).map((key) => ({
        label: key,
        type: completionDatabase[key].category,
        info: () => {
          let div = document.createElement("div");
          div.innerHTML = `
      <h1 class="text-orange-300 text-base pb-1">${completionDatabase[key].name} [<em class="text-white">${completionDatabase[key].category}</em>]</h1>
      <p class="text-base pl-4">${completionDatabase[key].description}</p>
      <div class="overflow-hidden overflow-scroll rounded px-2 ml-4 mt-2 bg-neutral-800"><code class="text-sm">${completionDatabase[key].example}</code></div>
      `;
          div.classList.add("px-4", "py-2", "rounded-lg", "w-92");
          return div;
        },
      })),
    };
  }
};

export const soundCompletions = (context: CompletionContext) => {
  let map = soundMap.get();
  delete map._base;
  let match = context.matchBefore(/sound\(/);
  if (match) {
    let from = match.from + "sound(".length;
    return {
      from,
      options: Object.keys(map).map((key) => ({
        label: key,
        type: map[key].data.type,
        apply: `"${key}"`,
      })),
    };
  }
  return null;
};
