<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';

  export let item = null;

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

  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 });
  }

  $: icon = () => {
    switch ($filtering) {
      case UNCHECKED:
        return 'circle';
      case WEIRD:
        return 'circle-half';
      case CHECKED:
        return 'check-circle-fill';
    }
  };

  $: selected =
    $filtering === CHECKED ? [item, ...subselected.flat()] : subselected.flat();
</script>

{#if item != null}
  <li>
    <TriStateCheckbox
      state={$filtering}
      on:change={handleChange}
      value={item}
      initialValue={true}
    />
    <span><Icon name={icon()} />{item}</span>
    <ul>
      {#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]}
          />
        {:else}
          <svelte:self
            item={toplevel}
            on:change={handleChildChange(i)}
            filtering={subfiltering.storeAt(i)}
            bind:selected={subselected[i]}
          />
        {/if}
      {/each}
    </ul>
  </li>
{/if}
