package org.ui.syntaxes;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SyntaxHighlighter {
    private final CharAttrib defaultCharAttribs;
    protected JTextPane textPane;
    protected TabSet tabset;
    private final List<Pair<String, CharAttrib>> elements;
    private int selectionStart;
    private int selectionLength;
    private final int fontSize;

    public SyntaxHighlighter(JTextPane textPane, float tabSize, int fontSize) {
        this.textPane = textPane;
        this.fontSize = fontSize;

        this.elements = new ArrayList<>();

        this.defaultCharAttribs = new CharAttrib(false, false, textPane.getForeground());

        createTabset(tabSize);
    }

    public SyntaxHighlighter(JTextPane textPane, float tabSize, int fontSize, Color backgroundColor) {
        this.textPane = textPane;
        this.fontSize = fontSize;

        this.elements = new ArrayList<>();

        this.defaultCharAttribs = new CharAttrib(false, false, textPane.getForeground());
        this.defaultCharAttribs.setBackgroundColor(backgroundColor);

        createTabset(tabSize);
    }

    protected void createHighlightPattern(String regex, CharAttrib attribs) {
        this.elements.add(new Pair<>(regex, attribs));
    }

    private void createTabset(float tabSize) {
        TabStop[] tabs = new TabStop[8];

        for (int i = 0; i < tabs.length; i++) {
            tabs[i] = new TabStop(tabSize * i + tabSize, 0, 0);
        }
        this.tabset = new TabSet(tabs);
    }

    public final void highlight() {
        this.selectionStart = 0;
        this.selectionLength = this.textPane.getDocument().getLength();

        createHighlight(this.defaultCharAttribs);

        try {
            for (Pair<String, CharAttrib> entry : this.elements) {

                Matcher matcher = Pattern.compile(entry.getA(), Pattern.MULTILINE | Pattern.DOTALL).matcher(
                        this.textPane.getDocument().getText(0, this.textPane.getDocument().getLength()));

                while (matcher.find()) {
                    this.selectionStart = matcher.start();
                    this.selectionLength = matcher.end() - this.selectionStart;

                    createHighlight(entry.getB());
                }
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void createHighlight(CharAttrib attribs) {
        StyleContext sc = StyleContext.getDefaultStyleContext();

        AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Background, this.defaultCharAttribs.getBackgroundColor());
        aset = sc.addAttribute(aset, StyleConstants.FontFamily, new Font("Monospaced", Font.PLAIN, 16).getFamily());
        aset = sc.addAttribute(aset, StyleConstants.FontSize, this.fontSize);
        aset = sc.addAttribute(aset, StyleConstants.Bold, attribs.isBold());
        aset = sc.addAttribute(aset, StyleConstants.Italic, attribs.isItalic());
        aset = sc.addAttribute(aset, StyleConstants.TabSet, this.tabset);

        if (attribs.getColor() == null) {

            Color foregroundColor = (Color) this.textPane.getStyledDocument()
                    .getCharacterElement(this.selectionStart).getAttributes()
                    .getAttribute(StyleConstants.Foreground);

            aset = sc.addAttribute(aset, StyleConstants.Foreground, foregroundColor.darker());
        } else {

            aset = sc.addAttribute(aset, StyleConstants.Foreground, attribs.getColor());
        }

        this.textPane.getStyledDocument().setCharacterAttributes(this.selectionStart, this.selectionLength, aset, false);
    }
}
