<template>
  <f7-page @page:afterin="onPageAfterIn" @page:beforeout="onPageBeforeOut">
    <f7-navbar :title="createMode ? 'Create Transformation' : 'Edit Transformation'" :subtitle="(!createMode && transformation) ? editorMode : ''" back-link="Back">
      <f7-nav-right>
        <f7-link v-if="createMode" @click="createTransformation" icon-md="material:save">
          {{ $theme.md ? '' : 'Create' }}
        </f7-link>
        <f7-link v-else-if="isEditable" @click="save()" icon-md="material:save">
          <template v-if="!$theme.md">
            Save<span v-if="$device.desktop">&nbsp;(Ctrl-S)</span>
          </template>
        </f7-link>
        <f7-link v-else icon-f7="lock_fill" tooltip="This transformation is not editable through the UI">
          Details
        </f7-link>
      </f7-nav-right>
    </f7-navbar>
    <!-- Create Transformation -->
    <transformation-general-settings v-if="createMode && ready" :createMode="true" :transformation="transformation" :types="types" :languages="languages" :language="language" @newType="transformation.type = $event" @newLanguage="language = $event" />
    <div v-if="ready && createMode" class="if-aurora display-flex justify-content-center margin padding">
      <div class="flex-shrink-0">
        <f7-button class="padding-left padding-right" style="width: 150px" color="blue" large raised fill @click="createTransformation">
          Create
        </f7-button>
      </div>
    </div>
    <!-- Edit Transformation -->
    <f7-toolbar v-if="!createMode && ready" position="bottom">
      <span class="display-flex flex-direction-row align-items-center" />
      <span class="display-flex flex-direction-row align-items-center">
        <f7-segmented v-if="isBlockly" class="margin-right">
          <f7-button outline small :active="!blocklyCodePreview" icon-f7="ticket" :icon-size="($theme.aurora) ? 20 : 22" class="no-ripple" @click="blocklyCodePreview = false" />
          <f7-button outline small :active="blocklyCodePreview" icon-f7="doc_text" :icon-size="($theme.aurora) ? 20 : 22" class="no-ripple" @click="showBlocklyCode" />
        </f7-segmented>
        <f7-link class="right details-link padding-right" ref="detailsLink" @click="detailsOpened = true" icon-f7="chevron_up" />
      </span>
    </f7-toolbar>
    <f7-icon v-if="!createMode && ready && (!isBlockly && !isEditable) || (blocklyCodePreview && isBlockly)" f7="lock" class="float-right margin" style="opacity:0.5; z-index: 4000; user-select: none;" size="50" color="gray"
             :tooltip="(isBlockly) ? 'Cannot edit the code generated by Blockly' : 'This transformation is not editable because it has been provisioned from a file'" />
    <editor v-if="!createMode && ready && (!isBlockly || blocklyCodePreview)" class="rule-script-editor" :mode="editorMode" :value="transformation.configuration.function" @input="onEditorInput" :read-only="isBlockly || !isEditable" :tern-autocompletion-hook="true" />
    <blockly-editor ref="blocklyEditor" v-else-if="isBlockly" :blocks="transformation.configuration.blockSource" @change="dirty = true" />
    <!-- TODO: Enable Blockly after blocks have been adjusted
    <f7-fab v-show="transformation.configuration && !transformation.configuration.function && transformation.configuration.mode === 'application/javascript' && !isBlockly" position="center-bottom" slot="fixed" color="blue" @click="convertToBlockly" text="Design with Blockly">
      <f7-icon f7="ticket_fill" />
    </f7-fab>
    -->

    <f7-sheet v-if="ready" ref="detailsSheet" class="transformation-details-sheet" :backdrop="false" :close-on-escape="true" :opened="detailsOpened" @sheet:closed="detailsOpened = false">
      <f7-page>
        <f7-toolbar tabbar bottom>
          <span class="margin-left">Transformation details</span>
          <div class="right">
            <f7-link sheet-close class="padding-right">
              <f7-icon f7="chevron_down" />
            </f7-link>
          </div>
        </f7-toolbar>
        <f7-block class="block-narrow">
          <transformation-general-settings :createMode="createMode" :transformation="transformation" />
          <f7-col>
            <f7-list v-if="isEditable">
              <f7-list-button color="red" @click="deleteTransformation">
                Remove Transformation
              </f7-list-button>
            </f7-list>
            <p class="text-align-center">
              Tip: Use <code>{{ itemStateTransformationCode }}</code> <clipboard-icon :value="itemStateTransformationCode" tooltip="Copy transformation" /> for Item state transformations.
            </p>
          </f7-col>
        </f7-block>
      </f7-page>
    </f7-sheet>
  </f7-page>
</template>

<style lang="stylus">
.transformation-editor.vue-codemirror
  display block
  top calc(var(--f7-navbar-height) + var(--f7-tabbar-height))
  height calc(100% - 2*var(--f7-navbar-height))
  width 100%
</style>

<script>
import DirtyMixin from '../dirty-mixin'
import TransformationGeneralSettings from '@/pages/settings/transformations/transformation-general-settings'
import { CodeSnippets, EditorModes } from '@/assets/definitions/transformations.js'
import ClipboardIcon from '@/components/util/clipboard-icon.vue'

export default {
  mixins: [DirtyMixin],
  components: {
    ClipboardIcon,
    TransformationGeneralSettings,
    'editor': () => import(/* webpackChunkName: "script-editor" */ '@/components/config/controls/script-editor.vue'),
    'blockly-editor': () => import(/* webpackChunkName: "blockly-editor" */ '@/components/config/controls/blockly-editor.vue')
  },
  props: ['transformationId', 'createMode'],
  data () {
    return {
      ready: false,
      loading: false,
      transformation: {},
      types: [],
      languages: [],
      language: '',
      detailsOpened: false,
      editorMode: '',
      blocklyCodePreview: false
    }
  },
  computed: {
    isEditable () {
      return this.transformation && this.transformation.editable !== false
    },
    isBlockly () {
      // TODO: Enable Blockly after blocks have been adjusted
      // return this.transformation.configuration && this.transformation.configuration.blockSource
      return false
    },
    itemStateTransformationCode () {
      return `${this.transformation.type.toUpperCase()}(${this.transformationId.replace(/:([A-Z][a-z]{1,2}-)?([a-z]{2,3})(-[A-Z]{2,3})?$/, '')})`
    }
  },
  methods: {
    onPageAfterIn () {
      if (this.ready) return
      if (this.createMode) {
        this.initializeNewTransformation()
        return
      }
      if (window) {
        window.addEventListener('keydown', this.keyDown)
      }
      this.load()
    },
    onPageBeforeOut () {
      if (this.$refs.detailsSheet.f7Sheet) this.$refs.detailsSheet.f7Sheet.close()
      if (window) {
        window.removeEventListener('keydown', this.keyDown)
      }
    },
    initializeNewTransformation () {
      this.transformation = {
        uid: this.$f7.utils.id(),
        label: '',
        type: '',
        configuration: {
          function: ''
        },
        editable: true
      }
      Promise.all([this.$oh.api.get('/rest/transformations/services'), this.$oh.api.get('/rest/config-descriptions/system:i18n')]).then((data) => {
        this.$set(this, 'types', data[0])

        this.$set(this, 'languages', data[1].parameters.find(p => p.name === 'language').options)
      })
      this.language = ''
      this.ready = true
    },
    createTransformation () {
      if (!this.transformation.uid) {
        this.$f7.dialog.alert('Please give an ID for the transformation')
        return
      }
      if (!this.transformation.type) {
        this.$f7.dialog.alert('Please give the type of the transformation')
        return
      }
      // openHAB core expects lowercase, however the list of available transformation services is uppercase
      this.transformation.type = this.transformation.type.toLowerCase()
      if (!this.transformation.label) {
        this.$f7.dialog.alert('Please give a label for this transformation')
        return
      }

      this.transformation.uid = 'config:' + this.transformation.type + ':' + this.transformation.uid
      if (this.language) {
        this.transformation.uid += ':' + this.language
      }

      // Insert code example if available
      if (CodeSnippets[this.transformation.type.toLowerCase()]) this.transformation.configuration.function = CodeSnippets[this.transformation.type.toLowerCase()]

      this.$oh.api.put('/rest/transformations/' + this.transformation.uid, this.transformation).then(() => {
        this.$f7.toast.create({
          text: 'Transformation created',
          destroyOnClose: true,
          closeTimeout: 2000
        }).open()
        this.$f7router.navigate(this.$f7route.url.replace('/add', '/' + this.transformation.uid), { reloadCurrent: true })
      })
    },
    load () {
      if (this.loading) return
      this.loading = true

      this.$oh.api.get('/rest/transformations/' + this.transformationId).then((data) => {
        this.$set(this, 'transformation', data)
        this.editorMode = EditorModes[this.transformation.type.toLowerCase()] || this.transformation.type
        this.loading = false
        this.ready = true
      })
    },
    save (noToast) {
      if (!this.isEditable) return
      if (this.isBlockly) {
        try {
          this.transformation.configuration.blockSource = this.$refs.blocklyEditor.getBlocks()
          this.transformation.configuration.function = this.$refs.blocklyEditor.getCode()
        } catch (e) {
          this.$f7.dialog.alert(e)
          return Promise.reject(e)
        }
      }
      return this.$oh.api.put('/rest/transformations/' + this.transformation.uid, this.transformation).then((data) => {
        this.dirty = false
        if (!noToast) {
          this.$f7.toast.create({
            text: 'Transformation updated',
            destroyOnClose: true,
            closeTimeout: 2000
          }).open()
        }
      }).catch((err) => {
        this.$f7.toast.create({
          text: 'Error while saving transformation configuration: ' + err,
          destroyOnClose: true,
          closeTimeout: 2000
        }).open()
      })
    },
    deleteTransformation () {
      this.$f7.dialog.confirm(
        `Are you sure you want to delete ${this.transformation.uid}?`,
        'Delete Transformation',
        () => {
          this.$oh.api.delete('/rest/transformations/' + this.transformation.uid).then(() => {
            this.dirty = false
            this.$f7router.back('/settings/transformations/', { force: true })
          })
        }
      )
    },
    showBlocklyCode () {
      try {
        this.transformation.configuration.blockSource = this.$refs.blocklyEditor.getBlocks()
        this.transformation.configuration.function = this.$refs.blocklyEditor.getCode()
        if (this.isBlockly) this.blocklyCodePreview = true
      } catch (e) {
        this.$f7.dialog.alert(e)
      }
    },
    convertToBlockly () {
      if ((this.configuration && this.transformation.configuration.function) || this.isBlockly || this.transformation.configuration.mode !== 'application/javascript') return
      this.$set(this.transformation.configuration, 'blockSource', '<xml xmlns="https://developers.google.com/blockly/xml"></xml>')
    },
    onEditorInput (value) {
      this.transformation.configuration.function = value
      this.dirty = true
    },
    keyDown (ev) {
      if ((ev.ctrlKey || ev.metaKey) && !(ev.altKey || ev.shiftKey)) {
        switch (ev.keyCode) {
          case 66:
            if (this.isBlockly) {
              if (this.blocklyCodePreview) {
                this.blocklyCodePreview = false
              } else {
                this.showBlocklyCode()
              }
            } else {
              this.convertToBlockly()
            }
            ev.stopPropagation()
            ev.preventDefault()
            break
          case 83:
            this.save()
            ev.stopPropagation()
            ev.preventDefault()
            break
        }
      }
    }
  }
}
</script>
