<template>
    <default-field :field="field" :errors="errors" :full-width-content="true">
        <template slot="field">
            <div
                class="bg-white rounded-lg"
                :class="{
                    'markdown-fullscreen fixed pin z-50': isFullScreen,
                    'form-input form-input-bordered px-0': !isFullScreen,
                    'form-control-focus': isFocused,
                    'border-danger': errors.has('body'),
                }"
            >
                <header class="flex items-center content-center justify-between border-b border-60">
                    <ul class="w-full flex items-center content-center list-reset">
                        <button
                            :class="{ 'text-primary font-bold': this.mode == 'write' }"
                            @click.prevent="write"
                            class="ml-1 text-90 px-3 py-2"
                        >
                            {{ __('Write') }}
                        </button>
                        <button
                            :class="{ 'text-primary font-bold': this.mode == 'preview' }"
                            @click.prevent="preview"
                            class="text-90 px-3 py-2"
                        >
                            {{ __('Preview') }}
                        </button>
                    </ul>
                    <ul class="flex items-center list-reset">
                        <button
                            :key="tool.action"
                            @click.prevent="callAction(tool.action)"
                            v-for="tool in tools"
                            class="rounded-none ico-button inline-flex justify-center px-2 text-sm text-80 border-l border-60"
                        >
                            <component
                                :is="tool.icon"
                                class="fill-80 w-editor-icon h-editor-icon"
                            />
                        </button>
                    </ul>
                </header>

                <div v-show="mode == 'write'" class="flex markdown-content relative p-4">
                    <textarea ref="theTextarea" />
                </div>

                <div
                    v-if="mode == 'preview'"
                    class="markdown overflow-scroll p-4"
                    v-html="previewContent"
                ></div>
            </div>
        </template>
    </default-field>
</template>

<script>
import _ from 'lodash'
import marked from 'marked'
import CodeMirror from 'codemirror'
import 'codemirror/mode/markdown/markdown'
import { FormField, HandlesValidationErrors } from 'laravel-nova'

const actions = {
    bold() {
        this.insertAround('**', '**')
    },

    italicize() {
        this.insertAround('*', '*')
    },

    image() {
        this.insertBefore('![](http://)', 2)
    },

    link() {
        this.insertAround('[', '](http://)')
    },

    toggleFullScreen() {
        this.fullScreen = !this.fullScreen
        this.$nextTick(() => this.codemirror.refresh())
    },

    fullScreen() {
        this.fullScreen = true
    },

    exitFullScreen() {
        this.fullScreen = false
    },
}

const keyMaps = {
    'Cmd-B': 'bold',
    'Cmd-I': 'italicize',
    'Cmd-Alt-I': 'image',
    'Cmd-K': 'link',
    F11: 'fullScreen',
    Esc: 'exitFullScreen',
}

export default {
    mixins: [HandlesValidationErrors, FormField],

    data: () => ({
        fullScreen: false,
        isFocused: false,
        codemirror: null,
        mode: 'write',
        tools: [
            { name: 'bold', action: 'bold', className: 'fa fa-bold', icon: 'editor-bold' },
            {
                name: 'italicize',
                action: 'italicize',
                className: 'fa fa-italic',
                icon: 'editor-italic',
            },
            { name: 'link', action: 'link', className: 'fa fa-link', icon: 'editor-link' },
            { name: 'image', action: 'image', className: 'fa fa-image', icon: 'editor-image' },
            {
                name: 'fullScreen',
                action: 'toggleFullScreen',
                className: 'fa fa-expand',
                icon: 'editor-fullscreen',
            },
        ],
    }),

    mounted() {
        this.codemirror = CodeMirror.fromTextArea(this.$refs.theTextarea, {
            tabSize: 4,
            indentWithTabs: true,
            lineWrapping: true,
            mode: 'markdown',
            viewportMargin: Infinity,
            extraKeys: {
                Enter: 'newlineAndIndentContinueMarkdownList',
                ..._.map(this.tools, tool => {
                    return tool.action
                }),
            },
        })

        _.each(keyMaps, (action, map) => {
            const realMap = map.replace(
                'Cmd-',
                CodeMirror.keyMap['default'] == CodeMirror.keyMap.macDefault ? 'Cmd-' : 'Ctrl-'
            )
            this.codemirror.options.extraKeys[realMap] = actions[keyMaps[map]].bind(this)
        })

        this.doc.on('change', (cm, changeObj) => {
            this.value = cm.getValue()
        })

        this.codemirror.on('focus', () => (this.isFocused = true))
        this.codemirror.on('blur', () => (this.isFocused = false))

        if (this.field.value) {
            this.doc.setValue(this.field.value)
        }

        this.$nextTick(() => this.codemirror.refresh())
    },

    methods: {
        focus() {
            this.codemirror.focus()
        },

        write() {
            this.mode = 'write'
            this.codemirror.refresh()
        },

        preview() {
            this.mode = 'preview'
        },

        insert(insertion) {
            this.doc.replaceRange(insertion, {
                line: this.cursor.line,
                ch: this.cursor.ch,
            })
        },

        insertAround(start, end) {
            if (this.doc.somethingSelected()) {
                const selection = this.doc.getSelection()
                this.doc.replaceSelection(start + selection + end)
            } else {
                this.doc.replaceRange(start + end, {
                    line: this.cursor.line,
                    ch: this.cursor.ch,
                })
                this.doc.setCursor({
                    line: this.cursor.line,
                    ch: this.cursor.ch - end.length,
                })
            }
        },

        insertBefore(insertion, cursorOffset) {
            if (this.doc.somethingSelected()) {
                const selects = this.doc.listSelections()
                selects.forEach(selection => {
                    const pos = [selection.head.line, selection.anchor.line].sort()

                    for (let i = pos[0]; i <= pos[1]; i++) {
                        this.doc.replaceRange(insertion, { line: i, ch: 0 })
                    }

                    this.doc.setCursor({ line: pos[0], ch: cursorOffset || 0 })
                })
            } else {
                this.doc.replaceRange(insertion, {
                    line: this.cursor.line,
                    ch: 0,
                })
                this.doc.setCursor({
                    line: this.cursor.line,
                    ch: cursorOffset || 0,
                })
            }
        },

        callAction(action) {
            this.focus()
            actions[action].call(this)
        },
    },

    computed: {
        doc() {
            return this.codemirror.getDoc()
        },

        isFullScreen() {
            return this.fullScreen == true
        },

        cursor() {
            return this.doc.getCursor()
        },

        rawContent() {
            return this.codemirror.getValue()
        },

        previewContent() {
            return marked(this.rawContent)
        },
    },
}
</script>

<style src="codemirror/lib/codemirror.css" />

<style>
.ico-button {
    width: 35px;
    height: 35px;
}

.ico-button:hover {
    color: var(--primary);
}

.ico-button:active {
    color: var(--brand-80);
}

.cm-fat-cursor .CodeMirror-cursor {
    background: #000;
}

.cm-s-default .cm-header {
    color: black;
}
.cm-s-default .cm-link {
    color: var(--primary);
}
.CodeMirror-line {
    color: var(--gray-60);
}
.cm-s-default .cm-variable-2 {
    color: var(--gray-60);
}
.cm-s-default .cm-quote {
    color: var(--gray-60);
}
.cm-s-default .cm-comment {
    color: var(--gray-60);
}
.cm-s-default .cm-string {
    color: var(--gray-40);
}
.cm-s-default .cm-url {
    color: var(--gray-40);
}

.CodeMirror {
    height: auto;
    font: 14px/1.5 Menlo, Consolas, Monaco, 'Andale Mono', monospace;
    box-sizing: border-box;
    width: 100%;
}

.markdown-fullscreen .markdown-content {
    height: calc(100vh - 30px);
}

.markdown-fullscreen .CodeMirror {
    height: 100%;
}
</style>
