<template>
  <div class="schema-input">
    <select v-if="type == 'enum'" v-model="valueCopy" class="form-control" :disabled="isReadOnly">
      <option value="" disabled selected v-html="schema.placeholder"></option>
      <option v-for="opt in orderedSchemaOptions" :key="opt.key" :value="opt.key">{{ opt.displayName || opt.key }}</option>
    </select>

    <template v-if="type == 'enum-radio'">
      <div class="btn-group">
        <label
          v-for="opt in orderedSchemaOptions"
          :key="opt.key"
          class="btn"
          :class="[
            valueCopy === opt.key
              ? isReadOnly
                ? 'btn-secondary'
                : 'btn-primary'
              : isReadOnly
              ? 'btn-outline-secondary'
              : 'btn-outline-primary'
          ]"
          :disabled="isReadOnly"
        >
          <input
            type="radio"
            :name="'radio' + _uid"
            :value="opt.key"
            v-model="valueCopy"
            class="custom-control-input"
            :disabled="isReadOnly"
          />
          <span>{{ opt.displayName || opt.key }}</span>
        </label>
      </div>
    </template>

    <template v-if="type == 'object'">
      <!--
        I keep this case for easier debugging. I'm not 100% sure that this case doesn't exist anymore.
        This happens when you use a `<schema-field ...></schema-field>` for an complex object. Use a
        `<schema-component-part ...></schema-component-part>` instead.
      -->Als het goed is komt deze case niet meer voor. Mocht je dit toch zien, neem dan contact op met het NLO team bij Q42.
    </template>

    <template v-if="type == 'array'">
      <div v-for="(item, key) in valueCopy" :key="key" class="vue-delete-item mb-1">
        <schema-input :schema="schema.items" v-model="valueCopy[key]" :parent-is-read-only="schema.isReadOnly"></schema-input>
        <button type="button" class="btn btn-sm btn-outline-danger vue-delete-button" @click="deleteItem(key)" v-if="!isReadOnly">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>

      <div v-if="!valueCopy || !valueCopy.length" class="col-form-label" v-html="schema.placeholder ? schema.placeholder : '(leeg)'"></div>

      <div :class="{ 'mt-2': valueCopy !== null && valueCopy.length }">
        <schema-add-buttons
          v-model="valueCopy"
          :has-list="true"
          :one-of-title="schema.title"
          :schema="schema.items"
          v-if="(!schema.maxLength || valueCopy.length < schema.maxLength) && !isReadOnly"
        ></schema-add-buttons>
      </div>
    </template>

    <template v-if="type == 'boolean'">
      <div class="btn-group" v-if="!isReadOnly">
        <label class="btn" :class="[valueCopy ? 'btn-primary' : 'btn-outline-primary']">
          <input type="radio" :name="'radio' + _uid" :value="true" v-model="valueCopy" class="custom-control-input" />
          <span>Ja</span>
        </label>
        <label class="btn" :class="[!valueCopy ? 'btn-primary' : 'btn-outline-primary']">
          <input type="radio" :name="'radio' + _uid" :value="false" v-model="valueCopy" class="custom-control-input" />
          <span>Nee</span>
        </label>
      </div>

      <input v-if="isReadOnly" class="form-control" :value="value ? 'Ja' : 'Nee'" readonly />
    </template>

    <dynamic-tag-input
      v-if="isTypeText"
      @input="onInput"
      :type="type"
      :schema="schema"
      :value="valueCopy || ''"
      :preview="schema.placeholder || schema.title"
    />

    <template v-if="isTypeNumber">
      <input
        type="number"
        :name="'number' + _uid"
        v-model="valueCopy"
        class="number form-control"
        :placeholder="schema.placeholder"
        :disabled="isReadOnly"
      />
    </template>

    <component
      v-if="type == 'other'"
      :is="schema.format"
      v-model="valueCopy"
      :schema="schema"
      :required="!!schema.required"
      :readonly="isReadOnly"
    ></component>

    <schema-validator :value="valueCopy" :schema="schema"></schema-validator>
    <validator v-if="schema.type == 'integer'" :test="!valueCopy || !isNaN(parseInt(valueCopy))">
      {{ schema.title }} moet een getal zijn
    </validator>
    <validator v-if="schema.type == 'number'" :test="!valueCopy || !isNaN(parseFloat(valueCopy))">
      {{ schema.title }} moet een getal zijn
    </validator>
  </div>
</template>

<style lang="scss" scoped>
.schema-input {
  position: relative;
}

.btn-group .btn-outline-primary span {
  opacity: 0.6;
}
</style>

<script>
import _orderBy from 'lodash/orderBy';
import dynamicTagInput from '../shared/dynamic-tag-input.vue';
import formElements from '../shared/form/all';
import { ValueSyncMixin } from '../shared/value-sync-mixin.js';
import schemaAddButtons from './schema-add-buttons.vue';
import schemaValidator from './schema-validator.vue';

export default {
  name: 'schema-input',
  docs: `Kiest de juiste input op basis van het JSON schema, met bijbehorende validatieregels.`,
  mixins: [ValueSyncMixin],
  props: {
    value: {
      required: true,
      demoValue: 'Hallo!'
    },
    schema: {
      type: Object,
      required: true,
      demoValue: () => ({
        type: 'string',
        format: 'textarea'
      })
    },
    // in case of an array, the items should also have 'readonly'
    parentIsReadOnly: {
      type: Boolean,
      required: false
    }
  },
  mounted() {
    // When value is not nullable it should be set to an empty string instead of null
    if (this.type == 'string' || this.type == 'textarea') {
      if (!this.schema.nullable && this.value === null) {
        this.valueCopy = '';
      }
    }
  },
  watch: {
    // overwrite valueCopy from ValueSyncMixin
    valueCopy(newValue) {
      // format the input value
      let formattedValue = newValue;

      if (this.type == 'string' || this.type == 'textarea') {
        if (formattedValue === '' && !this.schema.required && this.schema.nullable) {
          // example use case: string with C# UrlAttribute is valid on server when it is 'null' or starting with 'https/http/ftp'. Empty string is not valid.
          formattedValue = null;
        }
      }

      if (typeof formattedValue == 'number' && isNaN(formattedValue)) formattedValue = null;

      this.$emit('input', formattedValue); // vue syntax: update prop 'value' in the parent
    }
  },
  computed: {
    type() {
      const { format, options, properties, type } = this.schema;

      if (format === 'string-input') return 'string';
      if (format === 'textarea') return 'textarea';
      if (format === 'markdown') return 'markdown';
      if (format === 'rich-text') return 'rich-text';
      if (format) return 'other';
      if (this.schema.enum) {
        const opts = options.map((opt) => opt.displayName || opt.key).toString();
        return opts.length < 30 ? 'enum-radio' : 'enum';
      }
      if (properties) return 'object'; // obsolete, can be removed in the future. See also comment in template
      if (type === 'array') return 'array';
      if (type === 'boolean') return 'boolean';
      if (type === 'number') return 'number';
      if (type === 'integer') return 'integer';
      return 'string';
    },
    isTypeText() {
      return ['string', 'textarea', 'markdown', 'rich-text'].includes(this.type);
    },
    isTypeNumber() {
      return ['integer', 'number'].includes(this.type);
    },
    isReadOnly() {
      return this.parentIsReadOnly || this.schema.isReadOnly;
    },
    orderedSchemaOptions() {
      return _orderBy(this.schema.options, 'order');
    }
  },
  methods: {
    deleteItem(index) {
      this.valueCopy.splice(index, 1);
    },
    onInput(input) {
      this.valueCopy = input;
      this.$emit('input', input);
    }
  },
  components: {
    ...formElements,
    schemaAddButtons,
    schemaValidator,
    dynamicTagInput
  }
};
</script>
