<template>
  <div class="value-viewer">
    <div class="value-viewer-toolbar">
      <button
        v-for="format in formats"
        :key="format.value"
        type="button"
        :aria-selected="currentFormat === format.value"
        :class="format.value"
        @click="currentFormat = format.value"
      >
        {{ format.text }}
      </button>

      <button type="button" class="copy" @click="copyToClipboard">Copy</button>
    </div>
    <div class="value-body">
      <codemirror
        v-if="currentFormat === 'json' && formattedJson"
        :value="formattedJson"
        :options="cmOptions"
        class="json-value original-style"
      />
      <pre
        v-if="currentFormat === 'text'"
        :class="['text-value', { 'meta-value': isNull || isBlob }]"
        >{{ cellText }}</pre
      >
      <logs
        v-if="messages && messages.length > 0"
        :messages="messages"
        class="messages"
      />
    </div>
  </div>
</template>

<script>
import Codemirror from 'codemirror-editor-vue3'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/brace-fold.js'
import 'codemirror/theme/neo.css'
import cIo from '@/lib/utils/clipboardIo'
import Logs from '@/components/Logs'

export default {
  components: {
    Codemirror,
    Logs
  },
  props: {
    cellValue: [String, Number, Uint8Array]
  },
  data() {
    return {
      formats: [
        { text: 'Text', value: 'text' },
        { text: 'JSON', value: 'json' }
      ],
      currentFormat: 'text',
      cmOptions: {
        tabSize: 4,
        mode: { name: 'javascript', json: true },
        theme: 'neo',
        lineNumbers: true,
        line: true,
        foldGutter: true,
        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
        readOnly: true
      },
      formattedJson: '',
      messages: []
    }
  },
  computed: {
    isBlob() {
      return this.cellValue && ArrayBuffer.isView(this.cellValue)
    },
    isNull() {
      return this.cellValue === null
    },
    cellText() {
      const value = this.cellValue
      if (this.isNull) {
        return 'NULL'
      }
      if (this.isBlob) {
        return 'BLOB'
      }
      return value
    }
  },
  watch: {
    currentFormat() {
      this.messages = []
      this.formattedJson = ''
      if (this.currentFormat === 'json') {
        this.formatJson(this.cellValue)
      }
    },
    cellValue() {
      this.messages = []
      if (this.currentFormat === 'json') {
        this.formatJson(this.cellValue)
      }
    }
  },
  methods: {
    formatJson(jsonStr) {
      try {
        this.formattedJson = JSON.stringify(JSON.parse(jsonStr), null, 4)
      } catch (e) {
        console.error(e)
        this.formattedJson = ''
        this.messages = [
          {
            type: 'error',
            message: "Can't parse JSON."
          }
        ]
      }
    },
    copyToClipboard() {
      cIo.copyText(
        this.currentFormat === 'json' ? this.formattedJson : this.cellValue,
        'The value is copied to clipboard.'
      )
    }
  }
}
</script>

<style scoped>
.value-viewer {
  background-color: var(--color-white);
  height: 100%;
  display: flex;
  flex-direction: column;
}
.value-viewer-toolbar {
  display: flex;
  justify-content: end;
}
.value-body {
  flex-grow: 1;
  overflow: auto;
}
.text-value {
  padding: 0 8px;
  margin: 0;
  color: var(--color-text-base);
}

.json-value {
  margin-top: -4px;
}

.text-value.meta-value {
  font-style: italic;
  color: var(--color-text-light-2);
}

.messages {
  margin: 0 8px;
}

.value-viewer-toolbar button {
  font-size: 10px;
  height: 20px;
  padding: 0 8px;
  border: none;
  background: transparent;
  color: var(--color-text-base);
  border-radius: var(--border-radius-small);
}

.value-viewer-toolbar button:hover {
  background-color: var(--color-bg-light);
}

.value-viewer-toolbar button[aria-selected='true'] {
  color: var(--color-accent);
}

:deep(.codemirror-container) {
  display: block;
  height: 100%;
  max-height: 100%;
}
:deep(.CodeMirror) {
  height: 100%;
  max-height: 100%;
}
:deep(.CodeMirror-cursor) {
  width: 1px;
  background: var(--color-text-base);
}
</style>
