import deepEqual from "fast-deep-equal"

import {
    configureFigureTemplate
} from "./templates"
import {
    ImageSelectionDialog
} from "../../images/selection_dialog"
import {
    dropdownSelect,
    Dialog,
    ContentMenu
} from "../../common"
import {
    randomFigureId
} from "../../schema/common"

export class FigureDialog {
    constructor(editor) {
        this.editor = editor
        this.imageDB = this.editor.mod.db.imageDB
        this.userImageDB = this.editor.app.imageDB
        this.imgId = false
        this.imgDb = false
        this.copyright = false
        this.insideFigure = false
        this.figureNode = false
        this.contentNode = false
        this.caption = true
        this.category = "none"
        this.aligned = "center"
        this.width = "50"
        this.equation = ""
        this.node = false
        this.submitMessage = gettext("Insert")
        this.dialog = false
    }


    layoutMathEditor() {
        this.dialog.dialogEl.querySelector(".inner-figure-preview").innerHTML =
            `<div><span class="math-field" type="text" name="math" ></span></div>
            <p class="formula-or-figure">${gettext("or")}</p>
            <p><button type="button" id="insert-figure-image" class="fw-button fw-light">
                ${gettext("Insert image")} <i class="fa fa-image"></i>
            </button></p>`

        this.mathliveDOM = this.dialog.dialogEl.querySelector(".math-field")
        this.nonMathElements = [
            this.dialog.dialogEl.querySelector("#insert-figure-image"),
            this.dialog.dialogEl.querySelector(".formula-or-figure")
        ]
        import("mathlive").then(MathLive => {
            this.mathField = new MathLive.MathfieldElement({
                virtualKeyboardMode: "manual",
                keypressSound: null,
                plonkSound: null,
                locale: "int",
                strings: {
                    "int": {
                        "keyboard.tooltip.functions": gettext("Functions"),
                        "keyboard.tooltip.greek": gettext("Greek Letters"),
                        "keyboard.tooltip.command": gettext("LaTeX Command Mode"),
                        "keyboard.tooltip.numeric": gettext("Numeric"),
                        "keyboard.tooltip.roman": gettext("Symbols and Roman Letters"),
                        "tooltip.copy to clipboard": gettext("Copy to Clipboard"),
                        "tooltip.redo": gettext("Redo"),
                        "tooltip.toggle virtual keyboard": gettext("Toggle Virtual Keyboard"),
                        "tooltip.undo": gettext("Undo")
                    }
                },
            })
            this.mathField.addEventListener("blur", () => this.showPlaceHolder())
            this.mathField.addEventListener("focus", () => this.hidePlaceHolder())
            this.mathField.addEventListener("input", () => {
                this.equation = this.mathField.getValue()
                this.showHideNonMathElements()
            })
            this.mathField.value = this.equation
            this.mathliveDOM.appendChild(this.mathField)
            this.showPlaceHolder()
            this.showHideNonMathElements()
            this.dialog.dialogEl.querySelector("#insert-figure-image").addEventListener(
                "click",
                () => this.selectImage()
            )
        })

    }

    showPlaceHolder() {
        if (!this.mathField.getValue().length) {
            this.mathliveDOM.insertAdjacentHTML("beforeend", `<span class="placeholder" >${gettext("Type formula")}</span>`)
        }
    }

    hidePlaceHolder() {
        const placeHolder = this.mathliveDOM.querySelector(".placeholder")
        if (placeHolder) {
            this.mathliveDOM.removeChild(placeHolder)
        }
    }

    showHideNonMathElements() {
        if (this.equation.length) {
            this.nonMathElements.forEach(el => el.style.display = "none")
        } else {
            this.nonMathElements.forEach(el => el.style.display = "")
        }
    }

    selectImage() {
        const imageSelection = new ImageSelectionDialog(
            this.imageDB,
            this.userImageDB,
            this.imgId,
            this.editor
        )
        imageSelection.init().then(
            ({
                id,
                db
            }) => {
                if (id) {
                    this.imgId = id
                    this.imgDb = db
                    // We take a copy of the object in case of the image coming from the user db in order
                    // not to overwrite the copyright info from the user's image db.
                    this.copyright = db === "document" ?
                        this.imageDB.db[this.imgId].copyright :
                        JSON.parse(JSON.stringify(this.userImageDB.db[this.imgId].copyright))
                    this.layoutImagePreview()
                } else {
                    this.imgId = false
                    this.imgDb = false
                    this.layoutMathEditor()
                }
            }
        )
    }

    layoutImagePreview() {
        if (this.imgId) {
            if (this.mathField) {
                this.mathField = false
            }
            const db = this.imgDb === "document" ? this.imageDB.db : this.userImageDB.db

            this.dialog.dialogEl.querySelector(".inner-figure-preview").innerHTML =
                `<img src="${db[this.imgId].image}" style="max-width: 400px;max-height:220px">
                <span class="dot-menu-icon"><i class="fa fa-ellipsis-v"></i></span>`


            this.dialog.dialogEl.querySelector(".dot-menu-icon").addEventListener(
                "click",
                event => {
                    const contentMenu = new ContentMenu({
                        menu: this.editor.menu.imageMenuModel,
                        page: this,
                        menuPos: {X: event.pageX, Y: event.pageY},
                    })
                    contentMenu.open()
                }
            )
        }
    }

    submitForm() {

        if ((new RegExp(/^\s*$/)).test(this.equation) && (!this.imgId)) {
            // The math input is empty. Delete a math node if it exist. Then close the dialog.
            if (this.insideFigure) {
                const tr = this.editor.currentView.state.tr.deleteSelection()
                this.editor.currentView.dispatch(tr)
            }
            this.dialog.close()
            return false
        }

        if (this.imgDb === "user") {
            // Add image to document db.
            const imageEntry = JSON.parse(JSON.stringify(this.userImageDB.db[this.imgId]))
            imageEntry.copyright = this.copyright
            this.imageDB.setImage(this.imgId, imageEntry)
            this.imgDb = "document"
        } else if (this.imgId && this.imageDB.db[this.imgId] && !deepEqual(this.copyright, this.imageDB.db[this.imgId].copyright)) {
            const imageEntry = JSON.parse(JSON.stringify(this.imageDB.db[this.imgId]))
            imageEntry.copyright = this.copyright
            this.imageDB.setImage(this.imgId, imageEntry)
        }


        if (
            this.insideFigure &&
            this.equation === (this.node.content.content.find(node => node.type.name === "figure_equation")?.attrs.equation || "") &&
            this.imgId === (this.node.content.content.find(node => node.type.name === "image")?.attrs.image || false) &&
            this.imgDb === "document" &&
            this.caption === this.node.attrs.caption &&
            this.category === this.node.attrs.category &&
            this.aligned === this.node.attrs.aligned &&
            this.width === this.node.attrs.width
        ) {
            // The figure has not been changed, just close the dialog
            this.dialog.close()
            return false
        }
        const content = []
        if (this.imgId) {
            content.push(
                this.editor.currentView.state.schema.nodes["image"].create({image: this.imgId})
            )
        } else {
            content.push(
                this.editor.currentView.state.schema.nodes["figure_equation"].create({equation: this.equation})
            )
        }
        const captionNode = this.node.content?.content.find(node => node.type.name === "figure_caption") ||
            this.editor.currentView.state.schema.nodes["figure_caption"].create()
        if (this.category === "table") {
            content.unshift(captionNode)
        } else {
            content.push(captionNode)
        }
        const tr = this.editor.currentView.state.tr.replaceSelectionWith(
            this.editor.currentView.state.schema.nodes["figure"].createAndFill(
                {
                    aligned: this.aligned,
                    width: this.width,
                    category: this.category,
                    caption: this.caption,
                    id: this.insideFigure ? this.node.attrs.id : randomFigureId()
                },
                content
            ),
            false
        )
        this.editor.currentView.dispatch(tr)

        this.dialog.close()
    }

    findFigure(state) {
        if (state.selection.node && state.selection.node.type.name == "figure") {
            return state.selection.node
        }
        const $head = state.selection.$head
        for (let d = $head.depth; d > 0; d--) {
            if ($head.node(d).type.name == "figure") {
                return $head.node(d)
            }
        }
        return false
    }

    init() {
        this.node = this.findFigure(this.editor.currentView.state)

        if (this.node?.attrs?.track?.find(track => track.type === "deletion")) {
            // The figure is marked as deleted so we don't allow editing it.
            return true
        }

        const buttons = []

        if (this.node?.type && this.node?.type.name === "figure") {
            this.insideFigure = true
            this.submitMessage = gettext("Update")
            this.equation = this.node.content.content.find(node => node.type.name === "figure_equation")?.attrs.equation || ""
            this.imgId = this.node.content.content.find(node => node.type.name === "image")?.attrs.image || false
            this.imgDb = "document"
            this.category = this.node.attrs.category
            this.caption = this.node.attrs.caption
            this.aligned = this.node.attrs.aligned
            this.width = this.node.attrs.width
            buttons.push({
                text: gettext("Remove"),
                classes: "fw-orange",
                click: () => {
                    const tr = this.editor.currentView.state.tr.deleteSelection()
                    this.editor.currentView.dispatch(tr)
                    this.dialog.close()
                }
            })

        }
        // Image positioning both at the time of updating and inserting for the first time
        buttons.push({ // Update
            text: this.submitMessage,
            classes: "fw-dark",
            click: () => this.submitForm()
        })

        buttons.push({
            type: "cancel"
        })

        this.dialog = new Dialog({
            id: "figure-dialog",
            title: gettext("Enter latex math or insert an image"),
            body: configureFigureTemplate({
                language: this.editor.view.state.doc.firstChild.attrs.language
            }),
            buttons,
            beforeClose: () => {
                if (this.mathField) {
                    this.mathField = false
                }
            },
            onClose: () => this.editor.currentView.focus()
        })

        this.dialog.open()

        const alignmentSelector = dropdownSelect(
            this.dialog.dialogEl.querySelector(".figure-alignment"),
            {
                onChange: newValue => {
                    this.aligned = newValue
                },
                width: "80%",
                value: this.aligned
            }
        )

        if (this.width == "100") {
            alignmentSelector.setValue("center")
            alignmentSelector.disable()
            this.aligned = "center"
        }

        const figureWidthDOM = this.dialog.dialogEl.querySelector(".figure-width")
        figureWidthDOM.style.width = "80%"
        figureWidthDOM.firstElementChild.innerText = `${this.width} %`
        figureWidthDOM.addEventListener(
            "click",
            event => {
                const contentMenu = new ContentMenu({
                    menu: this.editor.menu.figureWidthMenuModel,
                    page: this,
                    menuPos: {X: event.pageX, Y: event.pageY},
                    onClose: () => {
                        if (this.width == "100") {
                            alignmentSelector.setValue("center")
                            alignmentSelector.disable()
                            this.aligned = "center"
                        } else {
                            alignmentSelector.enable()
                        }
                    }
                })
                contentMenu.open()
            }
        )

        dropdownSelect(
            this.dialog.dialogEl.querySelector(".figure-category"),
            {
                onChange: newValue => {
                    this.category = newValue
                },
                width: "80%",
                value: this.category
            }
        )

        dropdownSelect(
            this.dialog.dialogEl.querySelector(".figure-caption"),
            {
                onChange: newValue => {
                    this.caption = newValue === "true"
                },
                width: "80%",
                value: String(this.caption)
            }
        )

        if (this.imgId && this.imageDB.db[this.imgId]) {
            this.copyright = this.imageDB.db[this.imgId].copyright
            this.layoutImagePreview()
        } else {
            this.layoutMathEditor()
        }

    }
}
