<template lang="pug">
  div(@click="setSelectedRange")
    span.text-sequence(
      v-for="r in chunksWithLabel"
      v-bind:class="getChunkClass(r)"
      v-bind:data-tooltip="id2label[r.label].text"
      v-bind:style="{ \
        color: id2label[r.label].text_color, \
        backgroundColor: id2label[r.label].background_color \
      }"
    )
      span
        span(
          v-for="highlightedChunk in getSearchHighlightedChunks(textPart(r))"
          v-bind:class="highlightedChunk.highlight && 'has-background-warning'"
        ) {{ highlightedChunk.content }}
      button.delete.is-small(v-if="id2label[r.label].text_color", v-on:click="removeLabel(r)")
</template>

<script>
export default {
  props: {
    labels: {
      type: Array, // [{id: Integer, color: String, text: String}]
      default: () => [],
    },
    searchQuery: {
      type: String,
      default: '',
    },
    text: {
      type: String,
      default: '',
    },
    entityPositions: {
      type: Array, // [{'startOffset': 10, 'endOffset': 15, 'label_id': 1}]
      default: () => [],
    },
  },

  data: () => ({
    startOffset: 0,
    endOffset: 0,
  }),

  computed: {
    sortedEntityPositions() {
      /* eslint-disable vue/no-side-effects-in-computed-properties */
      this.entityPositions = this.entityPositions.sort((a, b) => a.start_offset - b.start_offset);
      return this.entityPositions;
      /* eslint-enable vue/no-side-effects-in-computed-properties */
    },

    chunks() {
      const res = [];
      let left = 0;
      for (let i = 0; i < this.sortedEntityPositions.length; i++) {
        const e = this.sortedEntityPositions[i];
        const l = this.makeLabel(left, e.start_offset);
        res.push(l);
        res.push(e);
        left = e.end_offset;
      }
      const l = this.makeLabel(left, this.text.length);
      res.push(l);

      return res;
    },

    chunksWithLabel() {
      return this.chunks.filter(r => this.id2label[r.label]);
    },

    id2label() {
      const id2label = {};
      // default value;
      id2label[-1] = {
        text_color: '',
        background_color: '',
      };
      for (let i = 0; i < this.labels.length; i++) {
        const label = this.labels[i];
        id2label[label.id] = label;
      }
      return id2label;
    },
  },

  watch: {
    entityPositions() {
      this.resetRange();
    },
  },

  methods: {
    getSearchHighlightedChunks(text) {
      if (this.searchQuery) {
        const chunks = [];
        let currentText = text;
        let nextIndex;

        do {
          nextIndex = currentText.toLowerCase().indexOf(this.searchQuery.toLowerCase());

          if (nextIndex !== -1) {
            chunks.push({
              content: currentText.substring(0, nextIndex),
              highlight: false,
            });
            chunks.push({
              content: currentText.substring(nextIndex, nextIndex + this.searchQuery.length),
              highlight: true,
            });
            nextIndex += this.searchQuery.length;
            currentText = currentText.substring(nextIndex);
          } else {
            chunks.push({
              content: currentText.substring(nextIndex),
              highlight: false,
            });
          }
        } while (nextIndex !== -1);

        return chunks.filter(({ content }) => content);
      }

      return [{ content: text, highlight: false }];
    },

    getChunkClass(chunk) {
      if (!chunk.id) {
        return {};
      }

      const label = this.id2label[chunk.label];
      return [
        'tooltip is-tooltip-bottom',
        { tag: label.text_color },
      ];
    },

    setSelectedRange() {
      let start;
      let end;
      if (window.getSelection) {
        const range = window.getSelection().getRangeAt(0);
        const preSelectionRange = range.cloneRange();
        preSelectionRange.selectNodeContents(this.$el);
        preSelectionRange.setEnd(range.startContainer, range.startOffset);
        start = [...preSelectionRange.toString()].length;
        end = start + [...range.toString()].length;
      } else if (document.selection && document.selection.type !== 'Control') {
        const selectedTextRange = document.selection.createRange();
        const preSelectionTextRange = document.body.createTextRange();
        preSelectionTextRange.moveToElementText(this.$el);
        preSelectionTextRange.setEndPoint('EndToStart', selectedTextRange);
        start = [...preSelectionTextRange.text].length;
        end = start + [...selectedTextRange.text].length;
      }
      this.startOffset = start;
      this.endOffset = end;
      console.log(start, end); // eslint-disable-line no-console
    },

    validRange() {
      if (this.startOffset === this.endOffset) {
        return false;
      }
      if (this.startOffset > this.text.length || this.endOffset > this.text.length) {
        return false;
      }
      if (this.startOffset < 0 || this.endOffset < 0) {
        return false;
      }
      for (let i = 0; i < this.entityPositions.length; i++) {
        const e = this.entityPositions[i];
        if ((e.start_offset <= this.startOffset) && (this.startOffset < e.end_offset)) {
          return false;
        }
        if ((e.start_offset < this.endOffset) && (this.endOffset < e.end_offset)) {
          return false;
        }
        if ((this.startOffset < e.start_offset) && (e.start_offset < this.endOffset)) {
          return false;
        }
        if ((this.startOffset < e.end_offset) && (e.end_offset < this.endOffset)) {
          return false;
        }
      }
      return true;
    },

    resetRange() {
      this.startOffset = 0;
      this.endOffset = 0;
    },

    textPart(r) {
      return [...this.text].slice(r.start_offset, r.end_offset).join('');
    },

    addLabel(labelId) {
      if (this.validRange()) {
        const label = {
          start_offset: this.startOffset,
          end_offset: this.endOffset,
          label: labelId,
        };
        this.$emit('add-label', label);
      }
    },

    removeLabel(index) {
      this.$emit('remove-label', index);
    },

    makeLabel(startOffset, endOffset) {
      const label = {
        id: 0,
        label: -1,
        start_offset: startOffset,
        end_offset: endOffset,
      };
      return label;
    },
  },
};
</script>