<script>
  import { writable } from 'svelte/store';
  import { tick, createEventDispatcher } from 'svelte';

  import TriStateCheckbox from './TriStateCheckbox.svelte';
  import { createTriState, createTriStates, UNCHECKED, CHECKED, WEIRD } from './stores';
  import { Icon } from 'sveltestrap';

  import { mkSource } from './calendar.js';

  export let item = null;
  export let level = 1;
  export let generalToggle = undefined;
  export let initial;

  export let children = [];
  export let selected = [];

  const initialState = initial.includes(item) ? CHECKED : UNCHECKED;

  export let filtering = createTriState(initialState);
  let subfiltering = createTriStates(initialState, Object.entries(children).length);
  let subselected = Array.from({ length: Object.entries(children).length }, _ => []);

  let collapsed = Object.entries(children).length ? false : undefined;

  function isVal(val) {
    return function (other) {
      return other === val;
    };
  }

  function areAllChecked(values) {
    return values.every(isVal(CHECKED));
  }

  function areAllUnchecked(values) {
    return values.every(isVal(UNCHECKED));
  }

  function handleChildChange(i) {
    return function handler(evt) {
      subfiltering.setAt(i, evt.detail.value);
    };
  }

  subfiltering.subscribe(subvalues => {
    if (areAllChecked(subvalues) && $filtering !== CHECKED) {
      filtering.setChecked();
    } else if (areAllUnchecked(subvalues) && $filtering !== UNCHECKED) {
      filtering.setUnchecked();
    } else if (
      !areAllChecked(subvalues) &&
      !areAllUnchecked(subvalues) &&
      $filtering !== WEIRD
    ) {
      filtering.setWeird();
    }
  });

  if (subfiltering.length > 0) {
    filtering.subscribe(value => {
      switch (value) {
        case CHECKED:
          subfiltering.updateAll(_ => CHECKED);
          break;
        case UNCHECKED:
          subfiltering.updateAll(_ => UNCHECKED);
          break;
      }
    });
  }

  const dispatch = createEventDispatcher();
  function handleChange(evt) {
    filtering.set(evt.detail.value);
    dispatch('change', { value: evt.detail.value });
  }

  function handleGeneralToggleChange(value) {
    if (value) {
      filtering.setChecked();
    } else {
      filtering.setUnchecked();
    }

    dispatch('change', { value: $filtering });
  }

  $: if (generalToggle != null) {
    handleGeneralToggleChange(generalToggle);
  }

  const url = (() => {
    const source = mkSource(item);
    return source ? source.export_url : undefined;
  })();

  setTimeout(
    () => (initial.includes(item) ? filtering.setChecked() : filtering.setUnchecked()),
    0
  );

  $: selected =
    $filtering === CHECKED ? [item, ...subselected.flat()] : subselected.flat();
  $: subHidden = () => (collapsed ? 'd-none' : '');
</script>

{#if item}
  <div class="ml-1" style="padding-left: {2 * level}em">
    <TriStateCheckbox
      state={$filtering}
      on:change={handleChange}
      value={item}
      bind:collapsed
      {url}
    />
  </div>

  <div class={subHidden()}>
    {#each Object.entries(children) as [toplevel, subchildren], i}
      {#if subchildren}
        <svelte:self
          item={toplevel}
          children={subchildren}
          on:change={handleChildChange(i)}
          filtering={subfiltering.storeAt(i)}
          bind:selected={subselected[i]}
          level={level + 1}
          {initial}
        />
      {:else}
        <svelte:self
          item={toplevel}
          on:change={handleChildChange(i)}
          filtering={subfiltering.storeAt(i)}
          bind:selected={subselected[i]}
          level={level + 1}
          {initial}
        />
      {/if}
    {/each}
  </div>
{/if}
