<template>
  <MainLayout :gap="false">
    <template #dialogs>
      <InputDialog ref="inputDialog" />
    </template>
    <template #header>
      <TopBar class="navbar" :gap="false">
        <template #left>
        </template>
        <template #center>
          <div class="file-name">{{ entry ? entry.fileName : '' }}</div>
        </template>
        <template #right>
          <Button v-if="!readonly" :icon="busySave ? null : 'fa-regular fa-floppy-disk'" success @click="onSave" :disabled="busySave || !isChanged" style="margin-right: 5px;"><Spinner v-show="busySave" style="stroke: white;"/> {{ tr('filemanager.textEditor.save') }}</Button>
          <Button icon="fa-solid fa-xmark" @click="onClose">{{ tr('main.dialog.close') }}</Button>
        </template>
      </TopBar>
    </template>
    <template #body>
      <div ref="editorView" class="editor"></div>
    </template>
  </MainLayout>
</template>

<script>

import { editor, languages } from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';

import Button from '../components/Button.vue';
import Spinner from '../components/Spinner.vue';
import InputDialog from '../components/InputDialog.vue';
import MainLayout from '../components/MainLayout.vue';
import TopBar from '../components/TopBar.vue';
import utils from '../utils.js';

window.MonacoEnvironment = {
  getWorker(workerId, label) {
    if (label === 'json') return new jsonWorker();
    if (label === 'css' || label === 'scss' || label === 'less') return new cssWorker();
    if (label === 'html' || label === 'handlebars' || label === 'razor') return new htmlWorker();
    if (label === 'typescript' || label === 'javascript') return new tsWorker();
    return new editorWorker();
  }
};

function getLanguage(filename) {
    var ext = '.' + filename.split('.').pop();
    var language = languages.getLanguages().find(function (l) {
        if (!l.extensions) return false;
        return !!l.extensions.find(function (e) { return e === ext; });
    }) || '';
    return language ? language.id : '';
}

export default {
    name: 'TextViewer',
    emits: [ 'close' ],
    props: {
      saveHandler: {
        type: Function,
        default() { console.warn('Missing saveHandler for TextViewer'); }
      },
      tr: {
        type: Function,
        default(id) { console.warn('Missing tr for TextViewer'); return utils.translation(id); }
      },
      readonly: {
        type: Boolean,
        default: false
      }
    },
    components: {
      Button,
      InputDialog,
      MainLayout,
      TopBar,
      Spinner
    },
    data() {
      return {
        entry: {},
        isChanged: false,
        busySave: false,
        lastSavedVersion: 0
      };
    },
    methods: {
      canHandle(entry) {
        if (entry.isBinary) return false;
        return utils.getFileTypeGroup(entry) === 'text' ||
          utils.getFileTypeGroup(entry) === 'application';
      },
      open(entry, content) {
        if (!entry || entry.isDirectory || !this.canHandle(entry)) return;

        this.entry = entry;

        this.editor.setModel(editor.createModel(content, getLanguage(entry.fileName)));

        const initialVersion = this.editor.getModel().getAlternativeVersionId();
        let currentVersion = initialVersion;
        let lastVersion = initialVersion;
        this.lastSavedVersion = initialVersion;

        this.editor.onDidChangeModelContent(e => {
          const versionId = this.editor.getModel().getAlternativeVersionId();

          this.isChanged = this.lastSavedVersion !== versionId;

          if (versionId < currentVersion) {
            this.redoPossible = true;
            // no more undo possible
            if (versionId === initialVersion) {
              this.undoPossible = false;
            }
          } else {
            if (versionId <= lastVersion) {
              // redoing the last change
              if (versionId == lastVersion) {
                this.redoPossible = false;
              }
            } else { // adding new change, disable redo when adding new changes
              this.redoPossible = false;
              if (currentVersion > lastVersion) {
                lastVersion = currentVersion;
              }
            }
            this.undoPossible = true;
          }
          currentVersion = versionId;
        });
      },
      async onClose() {
        if (this.isChanged) {
          const yes = await this.$refs.inputDialog.confirm({
            message: this.tr('filemanager.textEditorCloseDialog.title'),
            confirmStyle: 'danger',
            confirmLabel: 'Discard changes and close',
            rejectLabel: 'Cancel'
          });

          if (!yes) return;

          this.editor.setModel(editor.createModel(''));
          this.$emit('close');
          this.isChanged = false;
        } else {
          this.editor.setModel(editor.createModel(''));
          this.$emit('close');
        }
      },
      async onSave() {
        this.busySave = true;

        await this.saveHandler(this.entry, this.editor.getValue());

        this.isChanged = false;
        this.lastSavedVersion = this.editor.getModel().getAlternativeVersionId();

        this.busySave = false;
        this.editor.focus();
      },
      keydownHandler(event) {
        if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 's') {
          event.preventDefault();
          if (this.isChanged) this.onSave();
        }
      },
      beforeunloadHandler(event) {
        // this prevents accidental closing or reloading
        if (this.isChanged) event.preventDefault();
      }
    },
    mounted() {
      const theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'vs-dark' : 'vs-light';

      this.editor = editor.create(this.$refs.editorView, {
        automaticLayout: true,
        value: '',
        theme
      });

      window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
        editor.setTheme(event.matches ? 'vs-dark' : 'vs-light');
      });

      window.addEventListener('keydown', this.keydownHandler);
      window.addEventListener('beforeunload', this.beforeunloadHandler);
    },
    beforeUnmount() {
      window.removeEventListener('keydown', this.keydownHandler);
      window.removeEventListener('beforeunload', this.beforeunloadHandler);
    }
};

</script>

<style scoped>

.container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.8);
    display: flex;
    flex-direction: column;
}

.editor {
    width: 100%;
    height: 100%;
}

.file-name {
    margin: 0 0.5rem;
}

</style>
