<template>
  <div class="modal-card" style="width: auto">
    <header class="modal-card-head">
      <p class="modal-card-title">{{ this.alert ? 'Edit' : 'Add' }} Alert</p>
    </header>
    <section class="modal-card-body">
      <b-field label="Callsign" :message="isOwnCallsign ? '' : 'You are posting an alert for someone else\'s callsign'" :type="isOwnCallsign ? '' : 'is-info'">
        <b-input type="text" class="callsign" v-model="callsign" pattern="[a-zA-Z0-9/]{3,}" validation-message="Invalid callsign" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" required />
      </b-field>

      <b-field label="Summit reference" :message="summitDisplay" :type="summitType" :class="summitLabelClass" expanded>
        <b-field>
          <b-input type="text" ref="summitCode" v-model="summitCode" placeholder="XX/YY-000" :loading="summitLoading" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" required />
          <p class="control">
            <NearbySummitsList @summitSelected="onSummitSelected" />
          </p>
        </b-field>
      </b-field>

      <b-field label="Activation date" message="dd/mm/yyyy" expanded>
        <b-datepicker v-model="date" icon="calendar-day" :min-date="minDate" :date-formatter="dateFormatter" :date-parser="dateParser" :mobile-native="false" required />
      </b-field>

      <b-field label="ETA" message="e.g. 12:15" class="eta" expanded>
        <b-field>
          <b-input :type="$mq.mobile ? 'time' : 'text'" pattern="([0-1]{1}[0-9]{1}|20|21|22|23):[0-5]{1}[0-9]{1}" class="time-input" v-model="time" icon="clock" required />
          <p class="control">
            <b-radio-button v-model="timeZone" native-value="local">Local</b-radio-button>
          </p>
          <p class="control">
            <b-radio-button v-model="timeZone" native-value="utc">UTC</b-radio-button>
          </p>
        </b-field>
      </b-field>

      <b-field label="Frequency-Mode(s)" :message="freqModeMessage">
        <b-taginput v-model="freqMode" ref="freqMode" autocomplete rounded :data="freqModeSuggestions" :confirm-key-codes="[9,13,32,188]" @typing="updateFreqModeSuggestions" @input="onFreqModeInput" @blur="onFreqModeBlur" @keydown.native="onFreqModeKeyDown" append-to-body />
      </b-field>

      <b-field label="Comments">
        <b-input v-model="comments" type="text" maxlength="60" />
      </b-field>
    </section>
    <footer class="modal-card-foot">
      <b-button @click="$parent.close()">Cancel</b-button>
      <b-button type="is-info" :disabled="!isInputValid" :loading="posting" @click="postAlert">{{ this.alert ? 'Edit' : 'Add' }} Alert</b-button>
    </footer>
  </div>
</template>

<script>
import axios from 'axios'
import moment from 'moment'

import utils from '../mixins/utils.js'
import prefs from '../mixins/prefs.js'
import sotawatch from '../mixins/sotawatch.js'
import NearbySummitsList from './NearbySummitsList.vue'

export default {
  components: {
    NearbySummitsList
  },
  mixins: [utils, prefs, sotawatch],
  props: {
    defaultSummitCode: String,
    alert: Object
  },
  prefs: {
    key: 'editAlertPrefs',
    props: ['lastCallsign', 'timeZone', 'defaultComments']
  },
  mounted () {
    if (!this.callsign) {
      if (this.lastCallsign) {
        this.callsign = this.lastCallsign
      } else if (this.myCallsign) {
        this.callsign = this.myCallsign
        if (!/\/P$/.test(this.callsign)) {
          this.callsign += '/P'
        }
      }
    }

    if (!this.timeZone) {
      this.timeZone = 'utc'
    }

    if (!this.comments && this.defaultComments) {
      this.comments = this.defaultComments
    }
  },
  computed: {
    minDate () {
      if (this.timeZone === 'local') {
        return moment(moment().startOf('day').format('YYYY-MM-DD')).toDate()
      } else {
        return moment(moment.utc().startOf('day').format('YYYY-MM-DD')).toDate()
      }
    },
    summitDisplay () {
      if (this.summit) {
        if (this.$store.state.altitudeUnits === 'ft') {
          return this.summit.name + ' (' + Math.round(this.summit.altitude * 3.28084) + ' ft)'
        } else {
          return this.summit.name + ' (' + this.summit.altitude + ' m)'
        }
      } else if (this.summitInvalid) {
        return 'Summit not found'
      } else {
        return 'You can enter spaces instead of / and -'
      }
    },
    summitType () {
      if (this.summitInvalid) {
        return 'is-danger'
      } else {
        return ''
      }
    },
    freqModeMessage () {
      return 'Format: <em>freq-mode, ...</em> (e.g. <em>7.030-cw, 14.250-ssb</em>)'
    },
    isInputValid () {
      return /^[a-zA-Z0-9/]{3,}$/.test(this.callsign) && this.summit !== null && this.isSummitValid(this.summit) && this.date && /^\d\d:\d\d$/.test(this.time) && this.freqMode.length > 0 && (this.freqMode.join(', ').length <= 40 || this.freqMode.join(',').length <= 40)
    },
    summitLabelClass () {
      if (!this.summit || this.isSummitValid(this.summit)) {
        return { summitref: true }
      } else {
        return { summitref: true, invalid: true }
      }
    },
    isOwnCallsign () {
      return (!this.callsign || !this.myCallsign || (this.homeCallsign(this.callsign) === this.homeCallsign(this.myCallsign)))
    }
  },
  watch: {
    defaultSummitCode: {
      immediate: true,
      handler () {
        if (!this.summitCode) {
          this.summitCode = this.defaultSummitCode
        }
      }
    },
    summitCode: {
      immediate: true,
      handler () {
        if (this.summitCode) {
          // Shorthand input
          let summitRegex = /^([A-Z0-9]{1,8})[/ ]([A-Z]{2})[- ]?([0-9]{3})$/i
          let matches = this.summitCode.match(summitRegex)
          if (matches) {
            this.summitCode = (matches[1] + '/' + matches[2] + '-' + matches[3]).toUpperCase()
            this.summitLoading = true
            axios.get('https://api.sotl.as/summits/' + this.summitCode)
              .then(response => {
                this.summitLoading = false
                this.summitInvalid = false
                this.summit = response.data
              })
              .catch(() => {
                this.summitLoading = false
                this.summitInvalid = true
                this.summit = null
              })
          } else {
            this.summit = null
            this.summitInvalid = false
          }
        } else {
          this.summit = null
          this.summitInvalid = false
        }
      }
    },
    alert: {
      immediate: true,
      handler () {
        if (this.alert) {
          this.callsign = this.alert.activatorCallsign
          this.summitCode = this.alert.summit.code
          this.date = moment(this.alert.dateActivated.substring(0, 19)).toDate()
          this.time = moment(this.alert.dateActivated.substring(0, 19)).format('HH:mm')
          this.freqMode = this.alert.frequency.split(/\s*,\s*/)
          this.comments = this.alert.comments
        }
      }
    },
    timeZone (newTimeZone) {
      if (!this.date || !this.time) {
        return
      }

      if (newTimeZone === 'local') {
        let conv = this.utcToLocal(this.date, this.time)
        if (conv) {
          this.date = conv.date
          this.time = conv.time
        }
      } else {
        let conv = this.localToUtc(this.date, this.time)
        if (conv) {
          this.date = conv.date
          this.time = conv.time
        }
      }
    },
    time (newTime) {
      // Add colon to nnnn style times
      let matches = newTime.match(/^(\d\d)(\d\d)$/)
      if (matches) {
        this.time = matches[1] + ':' + matches[2]
      }
    }
  },
  methods: {
    postAlert () {
      this.lastCallsign = this.callsign.toUpperCase()
      let freqMode = this.freqMode.join(', ')
      if (freqMode.length > 40) {
        freqMode = this.freqMode.join(',')
      }
      let utcDate = this.date
      let utcTime = this.time
      if (this.timeZone === 'local') {
        let conv = this.localToUtc(this.date, this.time)
        utcDate = conv.date
        utcTime = conv.time
      }
      let params = {
        activatingCallsign: this.callsign.toUpperCase(),
        associationCode: this.summitCode.substring(0, this.summitCode.indexOf('/')),
        summitCode: this.summitCode.substring(this.summitCode.indexOf('/') + 1),
        dateActivated: moment(utcDate).format('DD/MM/YYYY'),
        eta: utcTime.replace(':', ''),
        frequency: freqMode,
        comments: this.comments,
        posterCallsign: this.myCallsign
      }
      if (this.alert) {
        params.id = this.alert.id
      }
      this.posting = true
      this.postSotaWatchAlert(params)
        .then(response => {
          this.$store.dispatch('reloadAlerts')
          this.$parent.close()
        })
        .finally(() => {
          this.posting = false
        })
    },
    dateFormatter (date) {
      return moment(date).format('DD/MM/YYYY')
    },
    dateParser (date) {
      return moment(date).toDate()
    },
    updateFreqModeSuggestions (text) {
      let matches = text.match(/^([0-9.]+)/)
      if (matches) {
        this.freqModeSuggestions = Object.keys(this.allModes()).map(mode => {
          return matches[1] + '-' + mode
        }).filter(suggestion => {
          return suggestion.startsWith(text.toLowerCase())
        })
      } else {
        this.freqModeSuggestions = []
      }
    },
    onFreqModeInput () {
      let splitFreqModes = []
      this.freqMode.forEach(fm => {
        splitFreqModes = splitFreqModes.concat(fm.split(/\s*[, ]\s*/))
      })
      this.freqMode = splitFreqModes
    },
    onFreqModeBlur () {
      // Delay to avoid double entry when clicking a tag suggestion
      setTimeout(() => {
        this.$refs.freqMode.addTag()
      }, 100)
    },
    onFreqModeKeyDown () {
      // Hack to allow us to get keep-first behavior on autocomplete despite the fact
      // that b-taginput sets keepFirst = !allowNew
      if (this.$refs.freqMode.confirmKeyCodes.indexOf(event.keyCode) >= 0) {
        event.preventDefault()
        this.$refs.freqMode.addTag()
      }
    },
    onSummitSelected (summit) {
      this.summitCode = summit.code
      this.$nextTick(() => {
        this.$refs.summitCode.checkHtml5Validity()
      })
    },
    localToUtc (date, time) {
      let utc = moment(moment(date).format('YYYY-MM-DD') + ' ' + time).utc()
      if (!utc.isValid()) {
        return undefined
      }
      return {
        date: moment(moment(utc).startOf('day').format('YYYY-MM-DD')).toDate(),
        time: utc.format('HH:mm')
      }
    },
    utcToLocal (date, time) {
      let local = moment.utc(moment(date).format('YYYY-MM-DD') + ' ' + time).local()
      if (!local.isValid()) {
        return undefined
      }
      return {
        date: moment(moment(local).startOf('day').format('YYYY-MM-DD')).toDate(),
        time: local.format('HH:mm')
      }
    }
  },
  data () {
    return {
      callsign: '',
      lastCallsign: null,
      defaultComments: '',
      summitCode: '',
      date: new Date(),
      time: '',
      freqMode: [],
      freqModeSuggestions: [],
      comments: '',
      summit: null,
      summitInvalid: false,
      summitLoading: false,
      timeZone: 'utc',
      posting: false
    }
  }
}
</script>

<style scoped>
.callsign >>> input {
  text-transform: uppercase;
}
@media (max-width: 1023px) {
  >>> .datepicker .dropdown.is-mobile-modal .dropdown-menu {
    width: calc(100vw - 40px);
  }
}
>>> .datepicker .dropdown.is-active .dropdown-menu {
  position: fixed !important;
  left: 50% !important;
  top: 50% !important;
  transform: translate(-50%, -50%);
  z-index: 100;
}
.invalid >>> .help {
  text-decoration: line-through;
}
.taginput {
  max-width: 30em;
}
.eta .field {
  margin-bottom: 0;
}
.eta >>> .time-input {
  width: 8em;
}
.summitref .field {
  margin-bottom: 0;
}

/* Fix from https://github.com/buefy/buefy/issues/1932#issuecomment-551453842 */
>>> .field.has-addons {
  flex-wrap: wrap;
}
>>> .field.has-addons .help {
  width: 100%;
}
</style>
