<template>
  <div id="search" @click="open" v-bind:class="{ active , ongoing }">
    <div id="input">
      <button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
        <i class="material-icons">arrow_back</i>
      </button>
      <i v-else class="material-icons">search</i>
      <input type="text"
        @keyup="keyup"
        @keyup.enter="submit"
        ref="input"
        :autofocus="active"
        v-model.trim="value"
        :aria-label="$t('search.writeToSearch')"
        :placeholder="placeholder">
    </div>

    <div id="result">
      <div>
        <template v-if="search.length === 0 && commands.length === 0">
          <p>{{ text }}</p>

          <template v-if="value.length === 0">
            <div class="boxes">
              <h3>{{ $t('search.types') }}</h3>
              <div>
                <div tabindex="0"
                  role="button"
                  @click="init('type:image')"
                  :aria-label="$t('search.images')">
                  <i class="material-icons">insert_photo</i>
                  <p>{{ $t('search.images') }}</p>
                </div>

                <div tabindex="0"
                  role="button"
                  @click="init('type:audio')"
                  :aria-label="$t('search.music')">
                  <i class="material-icons">volume_up</i>
                  <p>{{ $t('search.music') }}</p>
                </div>

                <div tabindex="0"
                  role="button"
                  @click="init('type:video')"
                  :aria-label="$t('search.video')">
                  <i class="material-icons">movie</i>
                  <p>{{ $t('search.video') }}</p>
                </div>

                <div tabindex="0"
                  role="button"
                  @click="init('type:pdf')"
                  :aria-label="$t('search.pdf')">
                  <i class="material-icons">picture_as_pdf</i>
                  <p>{{ $t('search.pdf') }}</p>
                </div>
              </div>
            </div>
          </template>

        </template>
        <ul v-else-if="search.length > 0">
          <li v-for="s in search">
            <router-link @click.native="close" :to="'./' + s.path">
              <i v-if="s.dir" class="material-icons">folder</i>
              <i v-else class="material-icons">insert_drive_file</i>
              <span>./{{ s.path }}</span>
            </router-link>
          </li>
        </ul>

        <pre v-else-if="commands.length > 0">
          <template v-for="c in commands">{{ c }}</template>
        </pre>
      </div>
      <p id="renew"><i class="material-icons spin">autorenew</i></p>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import * as api from '@/utils/api'

export default {
  name: 'search',
  data: function () {
    return {
      value: '',
      active: false,
      ongoing: false,
      scrollable: null,
      search: [],
      commands: [],
      reload: false
    }
  },
  watch: {
    show (val, old) {
      this.active = (val === 'search')

      // If the hover was search and now it's something else
      // we should blur the input.
      if (old === 'search' && val !== 'search') {
        if (this.reload) {
          this.$store.commit('setReload', true)
        }

        document.body.style.overflow = 'auto'
        this.reset()
        this.$refs.input.blur()
      }

      // If we are starting to show the search box, we should
      // focus the input.
      if (val === 'search') {
        this.reload = false
        this.$refs.input.focus()
        document.body.style.overflow = 'hidden'
      }
    }
  },
  computed: {
    ...mapState(['user', 'show']),
    // Placeholder value.
    placeholder () {
      if (this.user.allowCommands && this.user.commands.length > 0) {
        return this.$t('search.searchOrCommand')
      }

      return this.$t('search.search')
    },
    // The text that is shown on the results' box while
    // there is no search result or command output to show.
    text () {
      if (this.ongoing) {
        return ''
      }

      if (this.value.length === 0) {
        if (this.user.allowCommands && this.user.commands.length > 0) {
          return `${this.$t('search.searchOrSupportedCommand')} ${this.user.commands.join(', ')}.`
        }

        this.$t('search.typeSearch')
      }

      if (!(this.value[0] === '$') || !this.user.allowCommands) {
        return this.$t('search.pressToSearch')
      } else {
        if (this.command.length === 0) {
          return this.$t('search.typeCommand')
        }
        if (!this.supported()) {
          return this.$t('search.notSupportedCommand')
        }
        return this.$t('search.pressToExecute')
      }
    },
    // The command, without the leading symbol ('$') with or without a following space (' ')
    command () {
      return this.value[1] === ' ' ? this.value.slice(2) : this.value.slice(1)
    }
  },
  mounted () {
    // Gets the result div which will be scrollable.
    this.scrollable = document.querySelector('#search #result')

    // Adds the keydown event on window for the ESC key, so
    // when it's pressed, it closes the search window.
    window.addEventListener('keydown', (event) => {
      if (event.keyCode === 27) {
        this.$store.commit('closeHovers')
      }
    })
  },
  methods: {
    // Sets the search to active.
    open (event) {
      this.$store.commit('showHover', 'search')
    },
    // Closes the search and prevents the event
    // of propagating so it doesn't trigger the
    // click event on #search.
    close (event) {
      event.stopPropagation()
      event.preventDefault()
      this.$store.commit('closeHovers')
    },
    // Checks if the current input is a supported command.
    supported () {
      let cmd = this.command.split(' ')[0]
      let cl = this.user.commands.length
      if (cl !== 0) {
        for (let i = 0; i < cl; i++) {
          if (cmd.match(this.user.commands[i])) {
            return true
          }
        }
      }
      return false
    },
    // Initializes the search with a default value.
    init (string) {
      this.value = string + ' '
      this.$refs.input.focus()
    },
    // Resets the search box value.
    reset () {
      this.value = ''
      this.active = false
      this.ongoing = false
      this.search = []
      this.commands = []
    },
    // When the user presses a key, if it is ESC
    // then it will close the search box. Otherwise,
    // it will set the search box to active and clean
    // the search results, as well as commands'.
    keyup (event) {
      if (event.keyCode === 27) {
        this.close(event)
        return
      }

      this.search.length = 0
      this.commands.length = 0
    },
    // Submits the input to the server and sets ongoing to true.
    submit (event) {
      this.ongoing = true

      let path = this.$route.path
      if (this.$store.state.req.kind !== 'listing') {
        path = url.removeLastDir(path) + '/'
      }

      // In case of being a command.
      if (this.value[0] === '$') {
        if (this.supported() && this.user.allowCommands) {
          api.command(path, this.command,
            (event) => {
              this.commands.push(event.data)
              this.scrollable.scrollTop = this.scrollable.scrollHeight
            },
            (event) => {
              this.reload = true
              this.ongoing = false
              this.scrollable.scrollTop = this.scrollable.scrollHeight
            }
          )
          return
        }
        this.ongoing = false
        return
      }

      // In case of being a search.
      api.search(path, this.value,
        (event) => {
          let response = JSON.parse(event.data)
          if (response.path[0] === '/') {
            response.path = response.path.substring(1)
          }

          this.search.push(response)
          this.scrollable.scrollTop = this.scrollable.scrollHeight
        },
        (event) => {
          this.ongoing = false
          this.scrollable.scrollTop = this.scrollable.scrollHeight
        }
      )
    }
  }
}
</script>