window.receiptLanguage = {
    _registered: false,

    register: function () {
        if (this._registered) return;
        this._registered = true;

        monaco.languages.register({ id: 'receipt-xml' });

        monaco.languages.setLanguageConfiguration('receipt-xml', {
            comments: {
                blockComment: ['<!--', '-->']
            },
            brackets: [
                ['<', '>'],
                ['{{', '}}']
            ],
            autoClosingPairs: [
                { open: '<', close: '>' },
                { open: '{{', close: '}}' },
                { open: '"', close: '"' },
                { open: "'", close: "'" }
            ],
            surroundingPairs: [
                { open: '<', close: '>' },
                { open: '"', close: '"' },
                { open: "'", close: "'" }
            ]
        });

        var knownElements = [
            'receipt', 'line', 'columns', 'row', 'col', 'separator',
            'cut', 'feed', 'table', 'foreach', 'if', 'else'
        ];

        monaco.languages.setMonarchTokensProvider('receipt-xml', {
            knownElements: knownElements,

            tokenizer: {
                root: [
                    // Expression open
                    [/\{\{/, { token: 'delimiter.expression', next: '@expression' }],

                    // XML comment
                    [/<!--/, { token: 'comment', next: '@comment' }],

                    // Closing tag
                    [/<\//, { token: 'delimiter.tag', next: '@closingTag' }],

                    // Self-closing or opening tag
                    [/</, { token: 'delimiter.tag', next: '@openingTag' }],

                    // Plain text
                    [/[^<{]+/, 'text']
                ],

                expression: [
                    [/\}\}/, { token: 'delimiter.expression', next: '@pop' }],
                    [/\$index|\$first|\$last/, 'variable.special'],
                    [/:[^}]+/, 'string.format'],
                    [/[a-zA-Z_][\w]*(?:\.[a-zA-Z_][\w]*)*/, 'variable.property'],
                    [/./, 'variable.property']
                ],

                openingTag: [
                    // Match element name
                    [/([a-zA-Z][\w-]*)/, {
                        cases: {
                            '@knownElements': { token: 'tag.element', next: '@tagAttributes' },
                            '@default': { token: 'tag.unknown', next: '@tagAttributes' }
                        }
                    }],
                    [/>/, { token: 'delimiter.tag', next: '@pop' }]
                ],

                closingTag: [
                    [/([a-zA-Z][\w-]*)/, {
                        cases: {
                            '@knownElements': 'tag.element',
                            '@default': 'tag.unknown'
                        }
                    }],
                    [/>/, { token: 'delimiter.tag', next: '@pop' }],
                    [/\s+/, '']
                ],

                tagAttributes: [
                    // Self-closing
                    [/\/>/, { token: 'delimiter.tag', next: '@popall' }],

                    // Close tag
                    [/>/, { token: 'delimiter.tag', next: '@popall' }],

                    // Attribute name
                    [/[a-zA-Z_][\w-]*/, 'attribute.name'],

                    // Equals sign
                    [/=/, 'delimiter'],

                    // Quoted attribute values
                    [/"/, { token: 'attribute.value.delimiter', next: '@attrValueDouble' }],
                    [/'/, { token: 'attribute.value.delimiter', next: '@attrValueSingle' }],

                    // Whitespace
                    [/\s+/, '']
                ],

                attrValueDouble: [
                    [/\{\{/, { token: 'delimiter.expression', next: '@expression' }],
                    [/"/, { token: 'attribute.value.delimiter', next: '@pop' }],
                    [/[^"{]+/, 'attribute.value']
                ],

                attrValueSingle: [
                    [/\{\{/, { token: 'delimiter.expression', next: '@expression' }],
                    [/'/, { token: 'attribute.value.delimiter', next: '@pop' }],
                    [/[^'{]+/, 'attribute.value']
                ],

                comment: [
                    [/-->/, { token: 'comment', next: '@pop' }],
                    [/./, 'comment']
                ]
            }
        });
    },

    defineTheme: function () {
        monaco.editor.defineTheme('receipt-dark', {
            base: 'vs-dark',
            inherit: true,
            rules: [
                { token: 'tag.element', foreground: '569CD6', fontStyle: 'bold' },
                { token: 'tag.unknown', foreground: 'F44747' },
                { token: 'attribute.name', foreground: '9CDCFE' },
                { token: 'attribute.value', foreground: 'CE9178' },
                { token: 'attribute.value.delimiter', foreground: 'CE9178' },
                { token: 'delimiter.tag', foreground: '808080' },
                { token: 'delimiter', foreground: '808080' },
                { token: 'delimiter.expression', foreground: 'DCDCAA', fontStyle: 'bold' },
                { token: 'variable.property', foreground: '4EC9B0' },
                { token: 'variable.special', foreground: 'C586C0', fontStyle: 'italic' },
                { token: 'string.format', foreground: 'D7BA7D' },
                { token: 'comment', foreground: '6A9955', fontStyle: 'italic' },
                { token: 'text', foreground: 'D4D4D4' }
            ],
            colors: {}
        });
    },

    getEditorContent: function (editorId) {
        var editor = blazorMonaco.editor.getEditorById(editorId);
        if (editor) {
            return editor.getValue();
        }
        return '';
    }
};

window.setPreviewContent = function(html) {
    var iframe = document.getElementById('preview-iframe');
    if (iframe) iframe.srcdoc = html;
};

window.registerHotkeys = function(dotNetRef) {
    document.addEventListener('keydown', function(e) {
        if (!(e.ctrlKey || e.metaKey)) return;

        var action = null;
        switch (e.key.toLowerCase()) {
            case 'n': action = 'New'; break;
            case 'o': action = 'Open'; break;
            case 's': action = 'Save'; break;
            case 'j': action = 'LoadJson'; break;
        }

        if (action) {
            e.preventDefault();
            e.stopPropagation();
            dotNetRef.invokeMethodAsync('OnHotkeyAsync', action);
        }
    }, true);
};
