<template>
<div>

  <div v-if="source === 'string'">

    <h4>Text</h4>
    <!--<textarea v-model="sourceText"/>-->
    <p>{{sourceText}}</p>

    <h4>Marks</h4>
    <table>
      <tr>
        <!--<th>Delete</th>-->
        <th>begChar</th>
        <th>endChar</th>
        <th>text</th>
        <th>subjects</th>
      </tr>
      <tr v-for="mark of marks" :key="String(mark.begChar) + '.' + String(mark.endChar)">
        <!--<td><button @click="removeMark(mark)"/></td>-->
        <td>{{mark.begChar}}</td>
        <td>{{mark.endChar}}</td>
        <td>{{mark.text || sourceText.substring(mark.begChar, mark.endChar)}}</td>
        <td>{{mark.subjects}}</td>
      </tr>
    </table>

  </div>

  <div v-if="mt != null">
    <h4>Style</h4>
    <table>
      <tr v-for="subject of ['place', 'funny', 'head']" :key="subject">
        <td>{{subject}}</td>
        <td :style="mt.subjectStyle(subject)">Text</td>
      </tr>
    </table>
  </div>

  <div>
    <button @click="switchController">{{switchControllerText}}</button>
    <button @click="toggleMetaStrings()">Toggle meta strings</button>
    <label for="batch-size">Text batch size</label>
    <input type="number" name="batch-size" min="1" max="20" v-model.number="batchSize">
  </div>

  <div>
    <button @click="addMark('place')" :disabled="selectionMark == null">Add mark 'place'</button>
    <button @click="addMark('funny')" :disabled="selectionMark == null">Add mark 'funny'</button>
    <button @click="addMark('head')" :disabled="selectionMark == null">Add mark 'head'</button>
  </div>

  <h4>MarkedText</h4>
  <MarkedText id="mt" ref="mt"
    :sourceText="sourceText" :marks="marks" :controller="controller" :batchSize="batchSize"
  />

  <div v-if="interactive">
    <h4>Selection</h4>
    <p>{{selectionText}}</p>
  </div>

</div>
</template>



<script lang='ts'>
import { Component, defineComponent, PropType, ref } from 'vue'

import MarkedText, * as MT from '@'


const MarkedTextTest: Component = defineComponent({

  name: 'MarkedTextTest',

  components: {
    MarkedText
  },

  props: {
    source: {
      type: String as PropType<'string' | 'file' | 'marked-file'>,
      validator: (value: string) => {
        if (['string', 'file', 'marked-file'].indexOf(value) === -1)
          throw EvalError('Unknown option for source: ' + value)
        return true
      },
      default: 'string'
    }
  },

  data() {

    const sourceText = ''
    const selectionText = ''
    const selectionMark: MT.IMark | null = null
    const marks: MT.IMark[] = []
    const controllerType: 'null' | 'text' | 'word' = 'null'
    const controller: MT.IController = new MT.NullController()
    const isMetaEnabled: boolean = false
    const batchSize: number = 3

    return {
      sourceText, selectionText, selectionMark, marks,
      controllerType, controller, isMetaEnabled, batchSize
    }
  },

  computed: {

    interactive() {
      return this.controllerType !== 'null'
    },
    annotateWords() {
      return this.controllerType === 'word'
    },

    switchControllerText() {
      const controllerName =
        (this.controllerType === 'null') ? 'NullController' :
        (this.controllerType === 'text') ? 'TextSelectionController' :
        (this.controllerType === 'word') ? 'WordSelectionController' : ''
      return 'ChangeController: ' + controllerName
    }

  },

  methods: {

    createController() {

      if (this.interactive) {

        const deselectCbk = () => {
          this.selectionText = 'Deselected!'
          this.selectionMark = null
        }
        const selectNewCbk = (begChar: number, endChar: number) => {
          const text = this.sourceText.substring(begChar, endChar)
          this.selectionText = 'Selected text between begChar '
            + begChar + ' and endChar ' + endChar + ': ' + text
          this.selectionMark = {
            begChar,
            endChar,
            subjects: []
          }
        }
        const selectMarkCbk = (marks: MT.IMark[]) => {
          this.selectionText = 'Selected marks: ' + JSON.stringify(marks)
          // We only want selectionMark to reflect an UNMARKED text
          this.selectionMark = null
          if (marks.length > 1) {
            this.selectionText += '\n (not highlighting;'
              + ' highlighting may be manually triggered with markedText.setSelection(begChar, endChar).'
          }
        }

        if (this.annotateWords)
          return new MT.WordSelectionController(deselectCbk, selectNewCbk, selectMarkCbk)
        else
          return new MT.TextSelectionController(deselectCbk, selectNewCbk, selectMarkCbk)

      } else {
        return new MT.NullController()
      }

    },

    switchController() {

      switch (this.controllerType) {
      case 'null':
        this.controllerType = 'text'
        break
      case 'text':
        this.controllerType = 'word'
        break
      case 'word':
        this.controllerType = 'null'
        break
      }
      this.controller = this.createController()

    },

    addMark(subjectName: string) {
      const mark = {
        begChar: this.selectionMark.begChar,
        endChar: this.selectionMark.endChar,
        subjects: [subjectName]
      }
      if (this.selectionMark) {
        this.marks.push(mark)
      }
    },

    toggleMetaStrings() {
      if (this.isMetaEnabled) this.mt.removeMetaStrings()
      else this.mt.metaStringDivideBatches()
      this.isMetaEnabled = !this.isMetaEnabled
    }

  },

  setup() {
    const mt = ref<typeof MarkedText | null>(null)
    return {
      mt
    }
  },

  async created() {

    if (this.source === 'string' ) {

      this.sourceText = [
        'Lincoln strode into the White House two days later with renewed purpose',
        '  declaring that his top hat was too large.',
        'From room to room, he went; he assailed Seward in speech',
        'after speech; he whined to Chase until the latter backed down;',
        '  and all headwear-related decisions went through him.',
        'Lincoln was located in Hodgenville, Kentucky.',
        'It was not a pretty town.',
        'Seward was more sensible about stuff, with many contacts to keep himself informed.'
      ].join('\n')

      const words = new Map<string, number[]>()
      const regex = RegExp('\\w+','g')
      for (let match = regex.exec(this.sourceText);
        match != null; match = regex.exec(this.sourceText)) {
        const matchStr = match.toString()
        words.set(matchStr, (words.get(matchStr) || []).concat(match.index))
      }
      const markRanges = [
        [(words.get('Lincoln') as number[])[1], 'Lincoln'.length, 'funny'] as [number, number, string],
        [(words.get('top') as number[])[0], 'top hat'.length, 'head'] as [number, number, string],
        [(words.get('headwear') as number[])[0], 'headwear'.length, 'head'] as [number, number, string],
        [
          (words.get('Hodgenville') as number[])[0], 'Hodgenville, Kentucky'.length, 'place'
        ] as [number, number, string],
        [
          (words.get('Hodgenville') as number[])[0] + 6, 'ville'.length, 'funny'
        ] as [number, number, string]
      ]
      this.marks = markRanges.map(range => { return {
        begChar: range[0],
        endChar: range[0] + range[1],
        subjects: [range[2]]
      }})

    } else {

      try {
        this.sourceText = await fetch('/test/assets/age_of_invention.txt').then(response => response.text())
      } catch (err) {
        console.error('Could not load \'age_of_invention.txt\'; defaulting to empty string')
        this.sourceText = ''
      }

      if (this.source === 'marked-file') {

        // Mark every 9th and 10th word together

        const words = new Array<[number, number]>()
        const regex = RegExp('\\w+','g')
        for (let match = regex.exec(this.sourceText);
          match != null; match = regex.exec(this.sourceText)) {
          words.push([match.index, match.index + match.toString().length])
        }
        this.marks = []
        let begChar = 0
        let even = false
        for (const [rangeIdx, wordRange] of words.filter(
          (_, idx) => idx % 10 === 8 || idx % 10 === 9).entries()
        ) {
          if (even) {
            this.marks.push({
              begChar,
              endChar: wordRange[1],
              subjects: ['marked-' + rangeIdx]
            })
          } else {
            begChar = wordRange[0]
          }
          even = !even
        }

      } else {
        this.marks = []
      }

    }

  },

  beforeMount() {
    this.controller = this.createController()
  }

})

export default MarkedTextTest
</script>



<style scoped>

p {
  white-space: pre-wrap;
}

#mt {
  text-align: left;
  border: 5px solid black;
  margin-top: 30px;
  margin-bottom: 30px;
  margin-left: auto;
  margin-right: auto;
  padding: 10px 10px;
  border-radius: 10px;
  word-wrap: break-word;
  white-space: pre-wrap;
}

</style>

<style>

.mt-selection {
  background-color: brown;
  color: black;
}

</style>
