package com.abusalimov.mrcalc.ui;

import com.abusalimov.mrcalc.ast.ProgramNode;
import com.abusalimov.mrcalc.compile.CompileErrorException;
import com.abusalimov.mrcalc.compile.Compiler;
import com.abusalimov.mrcalc.diagnostic.Diagnostic;
import com.abusalimov.mrcalc.parse.Parser;
import com.abusalimov.mrcalc.parse.SyntaxErrorException;
import com.abusalimov.mrcalc.parse.impl.antlr.ANTLRParserImpl;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import java.awt.*;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

/**
 * @author - Eldar Abusalimov
 */
public class CodeTextPane extends JTextPane {
    private Consumer<List<Diagnostic>> errorListener;
    private List<Diagnostic> diagnostics = Collections.emptyList();

    public CodeTextPane() {
        getStyledDocument().addDocumentListener(new HighlightListener());
    }

    public void setErrorListener(Consumer<List<Diagnostic>> errorListener) {
        this.errorListener = errorListener;
    }

    private void fireErrorListener(List<Diagnostic> diagnostics) {
        if (errorListener != null)
            errorListener.accept(diagnostics);
    }

    private boolean onSameLine(int startOffset, int endOffset) {
        try {
            return getDocument().getText(startOffset, endOffset - startOffset).indexOf('\n') == -1;
        } catch (BadLocationException e) {
            e.printStackTrace();
            return false;
        }
    }

    private class HighlightListener implements DocumentListener {
        private final SquigglePainter squigglePainter = new SquigglePainter(Color.RED);
        private final DefaultHighlighter.DefaultHighlightPainter defaultPainter =
                new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 0, 0, 16));
        private final EOFPainter eofPainter = new EOFPainter(Color.RED);

        @Override
        public void insertUpdate(DocumentEvent e) {
            SwingUtilities.invokeLater(this::handleEvent);
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            SwingUtilities.invokeLater(this::handleEvent);
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            /* do nothing */
        }

        private void handleEvent() {
            clearHighlight();
            Parser parser = new ANTLRParserImpl();
            Compiler compiler = new Compiler();
//            Interpreter interpreter = new Interpreter();
            try {
                ProgramNode node = parser
                        .parse(getDocument().getText(0, getDocument().getLength()));
//                List<Stmt> stmts =
                compiler.compile(node);
//                interpreter.exec(stmts);
            } catch (SyntaxErrorException | CompileErrorException e) {
                diagnostics = e.getDiagnostics();

                fireErrorListener(diagnostics);

                for (Diagnostic diagnostic : diagnostics) {
                    try {
                        highlight(diagnostic);
                    } catch (BadLocationException e1) {
                        e.printStackTrace();
                    }
                }
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        }

        private void clearHighlight() {
            diagnostics = Collections.emptyList();
            getHighlighter().removeAllHighlights();
            fireErrorListener(diagnostics);
        }

        private void highlight(Diagnostic diagnostic) throws BadLocationException {
            int startOffset = diagnostic.getLocation().getStartOffset();
            int endOffset = diagnostic.getLocation().getEndOffset();

            if (startOffset == endOffset) {
                getHighlighter().addHighlight(startOffset, endOffset + 1, eofPainter);
            } else if (!onSameLine(startOffset, endOffset)) {
                getHighlighter().addHighlight(startOffset, endOffset, eofPainter);
            } else {
                getHighlighter().addHighlight(startOffset, endOffset, defaultPainter);
                getHighlighter().addHighlight(startOffset, endOffset, squigglePainter);
            }
        }
    }
}

@@ -607,24 +607,58 @@
 java.awt.*;%0A
+import java.awt.event.MouseEvent;%0A
 import java.
@@ -1040,24 +1040,953 @@
 istener());%0A
+        ToolTipManager.sharedInstance().registerComponent(this);%0A    %7D%0A%0A    @Override%0A    public String getToolTipText(MouseEvent event) %7B%0A        int offset = viewToModel(event.getPoint());%0A        for (Diagnostic diagnostic : diagnostics) %7B%0A            int startOffset = diagnostic.getLocation().getStartOffset();%0A            int endOffset = diagnostic.getLocation().getEndOffset();%0A%0A            try %7B%0A                if ((startOffset == endOffset %7C%7C !onSameLine(startOffset, endOffset)) &&%0A                    offset == startOffset &&%0A                    modelToView(offset).x + 8 %3E event.getPoint().x)%0A                    return diagnostic.getMessage();%0A%0A                if (offset %3E= startOffset && offset %3C endOffset)%0A                    return diagnostic.getMessage();%0A%0A            %7D catch (BadLocationException e) %7B%0A                e.printStackTrace();%0A            %7D%0A        %7D%0A        return super.getToolTipText(event);%0A
     %7D%0A%0A    p

Add error tooltips on hover