<script lang="ts">
  import AttributeView from "./AttributeView.svelte";
  import { query, useEntity } from "../lib/entity";
  import Address from "./Address.svelte";
  import { UpType } from "../lib/types";
  import type { IEntry } from "upend/types";
  import BlobPreview from "./BlobPreview.svelte";

  export let address: string;
  export let editable = false;

  const { error, revalidate, attributes, backlinks } = useEntity(address);

  $: allTypeAddresses = $attributes
    .map(([_, attr]) => attr)
    .filter((attr) => attr.attribute == "IS")
    .map((attr) => attr.value.c);

  $: allTypeEntries = query(
    () =>
      `(matches (in ${allTypeAddresses
        .map((addr) => `"${addr}"`)
        .join(" ")}) ? ?)`
  ).result;

  let allTypes: { [key: string]: UpType } = {};
  $: {
    allTypes = {};
    $allTypeEntries.forEach(([_, entry]) => {
      if (allTypes[entry.entity] === undefined) {
        allTypes[entry.entity] = new UpType(entry.entity);
      }

      switch (entry.attribute) {
        case "TYPE":
          allTypes[entry.entity].name = entry.value.c;
          break;
        case "TYPE_HAS":
        case "TYPE_REQUIRES":
        case "TYPE_ID":
          allTypes[entry.entity].attributes.push(entry.value.c);
          break;
      }
    });

    allTypes = allTypes;
  }

  let typedAttributes = {} as { [key: string]: [string, IEntry][] };
  let untypedAttributes = [] as [string, IEntry][];

  $: {
    typedAttributes = {};
    untypedAttributes = [];

    $attributes.forEach(([entryAddr, entry]) => {
      const entryTypes = Object.entries(allTypes).filter(([_, t]) =>
        t.attributes.includes(entry.attribute)
      );
      if (entryTypes.length > 0) {
        entryTypes.forEach(([addr, _]) => {
          if (typedAttributes[addr] == undefined) {
            typedAttributes[addr] = [];
          }
          typedAttributes[addr].push([entryAddr, entry]);
        });
      } else {
        untypedAttributes.push([entryAddr, entry]);
      }
    });

    typedAttributes = typedAttributes;
    untypedAttributes = untypedAttributes;
  }

  $: filteredUntypedAttributes = untypedAttributes.filter(
    ([_, entry]) =>
      entry.attribute !== "IS" ||
      !Object.keys(typedAttributes).includes(entry.value.c)
  );
</script>

<div class="inspect">
  <h2>
    <Address
      {address}
      isFile={$backlinks.some(([_, e]) => e.attribute === "FILE_IS")}
    />
  </h2>
  <BlobPreview {address} />
  {#if !$error}
    <div class="attributes">
      {#each Object.entries(typedAttributes) as [typeAddr, attributes] (typeAddr)}
        <AttributeView
          {editable}
          {address}
          type={allTypes[typeAddr]}
          {attributes}
          on:edit={revalidate}
        />
      {/each}
      {#if filteredUntypedAttributes.length > 0 || editable}
        <AttributeView
          title="Other attributes"
          {editable}
          {address}
          attributes={untypedAttributes}
          on:change={revalidate}
        />
      {/if}
      {#if $backlinks.length > 0}
        <AttributeView
          title={`Referred to (${$backlinks.length})`}
          {address}
          attributes={$backlinks}
          reverse
        />
      {/if}
    </div>
  {:else}
    <div class="error">
      {JSON.stringify($error)}
    </div>
  {/if}
</div>

<style scoped lang="scss">
  .inspect {
    flex: auto;
    display: flex;
    flex-direction: column;
    & > * {
      padding: 0 1rem;
    }
  }

  .attributes {
    flex: auto;
    height: 0; // https://stackoverflow.com/a/14964944
    overflow-y: auto;
  }

  .hr {
    position: relative;
    margin: 2rem 0 1rem 0;

    hr {
      border: none;
      border-top: 4px double var(--foreground);
      height: 1rem;
    }

    .hr-label {
      position: absolute;
      top: -1ex;
      left: 50%;
      transform: translateX(-50%);

      font-weight: bold;
      font-size: 18px;

      color: var(--foreground);
      background: var(--background);
      padding: 0 4px;

      sl-icon {
        margin-bottom: -4px;
      }
    }
  }

  .error {
    color: red;
  }
</style>
