<script>
  import { createEventDispatcher, onMount } from "svelte";
  import Spinner from "./Spinner.svelte";
  import CodeFormatter from "./CodeFormatter.svelte";

  const dispatch = createEventDispatcher();
  export let content = "";
  export let src="";
  export let original = "";
  // as usual there are timing issues so give some default values
  let slot_content = {textContent: "Loading..."};
  let code = "";
  let copied = false;
  export let language="generic";
  let timer;
  let code_promise;

  const load_code = async () => {
    if(src) {
      // load from url
      let res = await fetch(src);
      let content = res.status == 200 ? await res.text() : `Error getting ${src}: ${res.status}`;
      original = content;
    } else if(content) {
      // it's in a variable
      original = content;
    } else {
      // must be in the slot
      original = slot_content.textContent;
    }

    // we keep the original content for the copy/past functionality
    code = original.trim();
  }

  const copy_code = () => {
    navigator.clipboard.writeText(original).then(() => {
      if(timer) clearTimeout(timer);
      copied = true;
      timer = setTimeout(() => copied = false, 200);
      dispatch("copy");
    }, () => {
      copied = false;
    });
  }

  // A common Svelte footgun is that updating the variables to a component do not cause it to update. They have "reasons" but I feel all of their reasons are wrong.

  $: if(content !== code) code_promise = load_code();

  onMount(() => {
    code_promise = load_code();
  });
</script>

<style>
  /* NOTE: .line-numbers is really only used to make the Prism
     line-numbers plugin run. The CSS doesn't use that class and
     instead just add line numbers to every code block inside a pre.
   */
  #slot-content {
    display: none;
  }

  pre:hover {
    cursor: copy;
  }

  @keyframes flash {
    0% { opacity: 0.5; }
    100% { opacity: 1; }
  }

  pre.copied {
    animation: flash 0.2s;
  }
</style>

{#if content}
  {#await code_promise}
    <Spinner />
  {:then}
<pre on:click={ copy_code } class:copied>
<code data-language={ language } class="line-numbers language-{ language }">{code}</code>
</pre>
    <CodeFormatter />
  {/await}
{:else}
  <span id="slot-content" bind:this={slot_content}><slot /></span>
  {#await code_promise}
    <Spinner />
  {:then}
<pre on:click={ copy_code } class:copied>
<code data-language={ language } class="line-numbers language-{ language }">
{code}
</code>
</pre>
    <CodeFormatter />
  {/await}
{/if}
