<template>
  <div
    class="nv-selection-control nv-checkbox"
    :class="[
      !customSize ? sizeableClasses : undefined,
      { 'nv-selection-control--disabled': computedDisabled },
      { 'nv-selection-control--readonly': computedReadonly },
      { 'nv-selection-control--selected': isTrue },
    ]"
    v-bind="attrs"
    @click="onClick"
    @keydown="onKeydown"
    @keyup="onKeyup"
  >
    <div class="nv-selection-control__wrap" :class="classInput">
      <input :checked="isTrue" type="checkbox" />
      <nv-icon :icon="icon" />
    </div>
    <label class="nv-selection-control__label" :class="classLabel">
      <template v-if="label">{{ label }}</template>
      <slot v-else />
    </label>
  </div>
</template>

<script>
import { computed, inject } from "vue";

import useSize, { useSizeProps } from "../../composables/sizeable";
import { stopAndPrevent } from "../../utils/helpers";
import { NvIcon } from "../NvIcon";

export default {
  name: "NvCheckbox",
  props: {
    modelValue: { required: true },
    val: { required: false },
    label: String,
    disabled: Boolean,
    readonly: Boolean,
    tabindex: [String, Number],
    classInput: [String, Array, Object],
    classLabel: [String, Array, Object],
    trueIcon: {
      type: String,
      default: "nvi-checkbox-on",
    },
    falseIcon: {
      type: String,
      default: "nvi-checkbox-off",
    },
    falseDisabledIcon: {
      type: String,
      default: "nvi-checkbox-blank",
    },
    indeterminateIcon: {
      type: String,
      default: "nvi-checkbox-indeterminate",
    },
    trueValue: {
      default: true,
    },
    falseValue: {
      default: false,
    },
    toggleOrder: {
      type: String,
      validator: (v) => v === "tf" || v === "ft",
    },
    toggleIndeterminate: Boolean,
    indeterminateValue: {
      default: null,
    },
    ...useSizeProps,
  },
  components: { NvIcon },
  setup(props, ctx) {
    const { sizeableClasses, customSize } = useSize(props);

    const parentDisabled = inject("parentDisabled", undefined);
    const computedDisabled = computed(() => parentDisabled || props.disabled);

    const parentReadonly = inject("parentReadonly", undefined);
    const computedReadonly = computed(() => parentReadonly || props.readonly);

    const modelIsArray = computed(
      () => props.val !== void 0 && Array.isArray(props.modelValue)
    );
    const index = computed(() =>
      modelIsArray.value === true ? props.modelValue.indexOf(props.val) : -1
    );

    const isTrue = computed(() =>
      modelIsArray.value === true
        ? index.value > -1
        : props.modelValue === props.trueValue
    );

    const isFalse = computed(() =>
      modelIsArray.value === true
        ? index.value === -1
        : props.modelValue === props.falseValue
    );

    const isIndeterminate = computed(
      () => isTrue.value === false && isFalse.value === false
    );

    function getNextValue() {
      if (modelIsArray.value === true) {
        if (isTrue.value === true) {
          const val = props.modelValue.slice();
          val.splice(index.value, 1);
          return val;
        }

        return props.modelValue.concat([props.val]);
      }

      if (isTrue.value === true) {
        if (props.toggleOrder !== "ft" || props.toggleIndeterminate === false) {
          return props.falseValue;
        }
      } else if (isFalse.value === true) {
        if (props.toggleOrder === "ft" || props.toggleIndeterminate === false) {
          return props.trueValue;
        }
      } else {
        return props.toggleOrder !== "ft" ? props.trueValue : props.falseValue;
      }

      return props.indeterminateValue;
    }

    function onClick(e) {
      if (e !== void 0) {
        stopAndPrevent(e);
      }

      if (computedReadonly.value !== true && computedDisabled.value !== true) {
        ctx.emit("update:modelValue", getNextValue(), e);
      }
    }

    function onKeydown(e) {
      if (e.keyCode === 13 || e.keyCode === 32) {
        stopAndPrevent(e);
      }
    }

    function onKeyup(e) {
      if (e.keyCode === 13 || e.keyCode === 32) {
        onClick(e);
      }
    }

    const icon = computed(
      () =>
        (isTrue.value === true
          ? props.trueIcon
          : computedDisabled.value
          ? props.falseDisabledIcon
          : isIndeterminate.value === true
          ? props.indeterminateIcon
          : props.falseIcon) || null
    );

    const tabindex = computed(() =>
      computedDisabled.value || computedReadonly.value
        ? -1
        : props.tabindex || 0
    );

    const attrs = computed(() => ({
      tabindex: tabindex.value,
      role: "checkbox",
      "aria-label": props.label,
      "aria-checked":
        isIndeterminate.value === true
          ? "mixed"
          : isTrue.value === true
          ? "true"
          : "false",
      "aria-disabled": computedDisabled.value,
    }));

    return {
      attrs,
      computedDisabled,
      computedReadonly,
      customSize,
      icon,
      isTrue,
      onClick,
      onKeydown,
      onKeyup,
      sizeableClasses,
    };
  },
};
</script>
