package de.duehl.swing.ui.tabs.logic;

import java.util.ArrayList;
import java.util.List;

import de.duehl.basics.debug.DebugHelper;
import de.duehl.basics.io.FileHelper;
import de.duehl.swing.ui.highlightingeditor.HighlightingEditor;
import de.duehl.swing.ui.highlightingeditor.HighlightingEditorWithoutButtonBar;
import de.duehl.swing.ui.highlightingeditor.HighlightingViewer;
import de.duehl.swing.ui.highlightingeditor.syntax.SyntaxHighlighting;
import de.duehl.swing.ui.highlightingeditor.syntax.highlighting.HighlightingFactory;
import de.duehl.swing.ui.tabs.Tabulator;
import de.duehl.swing.ui.tabs.bars.editors.TabBarWithEditors;
import de.duehl.swing.ui.tabs.elements.TabElementsWithEditor;

/**
 * Diese Klasse verwaltet Editoren einer TabBarWithEditorTabs, die sich nicht ändern.
 * Eng auf WzPreparation zugeschnitten.
 *
 * @version 1.01     2017-12-07
 * @author Christian Dühl
 */

public class EditorBarAndEditors {

    private static final int DEFAULT_FONT_SIZE = 20;

    private final static boolean DEBUG = true;

    private final List<EditorAndFilename> editors;

    private final TabBarWithEditors tabBar;

    public EditorBarAndEditors(TabBarWithEditors tabBar) {
        this.tabBar = tabBar;

        editors = new ArrayList<>();
    }

    public HighlightingEditor addEditor(String barename) {
        checkForKnownBarename(barename);
        HighlightingEditor editor = createEditor(barename);
        addToTabBar(editor);
        addToEditors(barename, editor);
        return editor;
    }

    private HighlightingEditor createEditor(String barename) {
        HighlightingEditor editor = new HighlightingEditor();
        initEditorFont(editor);
        editor.useSyntaxHighlighting(generateSyntaxHighlighting());
        editor.setFilenameButDoNotDoAnythingElse(barename);
        return editor;
    }

    public HighlightingEditor addEditorWithoutButtonBar(String barename) {
        checkForKnownBarename(barename);
        HighlightingEditor editor = createEditorWithoutButtonBar(barename);
        addToTabBar(editor);
        addToEditors(barename, editor);
        return editor;
    }

    private HighlightingEditor createEditorWithoutButtonBar(String barename) {
        HighlightingEditor editor = new HighlightingEditorWithoutButtonBar();
        initEditorFont(editor);
        editor.useSyntaxHighlighting(generateSyntaxHighlighting());
        editor.setFilenameButDoNotDoAnythingElse(barename);
        return editor;
    }

    public HighlightingEditor addViewer(String filename) {
        String barename = FileHelper.getBareName(filename);
        checkForKnownBarename(barename);
        HighlightingViewer editor = createViewer(barename);
        addToTabBar(editor);
        addToEditors(barename, editor);

        return editor;
    }

    private HighlightingViewer createViewer(String barename) {
        HighlightingViewer viewer = new HighlightingViewer();
        initEditorFont(viewer);
        viewer.setFilenameButDoNotDoAnythingElse(barename);
        return viewer;
    }

    private void checkForKnownBarename(String barename) {
        for (EditorAndFilename editorPlus : editors) {
            String editorBarename = editorPlus.getBarename();
            if (editorBarename.equals(barename)) {
                throw new IllegalArgumentException("Der Dateiname ohne Pfad '" + barename
                        + "' ist bereits in verwendung, daher wird die Aufnahme von '" + barename
                        + "' abgelent.");
            }
        }
    }

    private void initEditorFont(HighlightingEditor editor) {
        editor.useMonospaceFont();
        editor.setFontSize(DEFAULT_FONT_SIZE);
    }

    private SyntaxHighlighting generateSyntaxHighlighting() {
        HighlightingFactory factory = new HighlightingFactory();
        String name = HighlightingFactory.WZ_PREPARATION;
        SyntaxHighlighting highlighting = factory.create(name);
        return highlighting;
    }

    private HighlightingEditor addToTabBar(HighlightingEditor editor) {
        TabElementsWithEditor tabElements = tabBar.addTab(editor);
        Tabulator tabulator = tabElements.getTabulator();
        editor.addChangeStateDisplayer(tabulator);
        return editor;
    }

    private void addToEditors(String barename, HighlightingEditor editor) {
        EditorAndFilename editorPlus = new EditorAndFilename(editor, barename);
        editors.add(editorPlus);
    }

    public void updateFilennameForBarename(String barename, String filename) {
        say("update filename " + barename + " -> " + filename);
        EditorAndFilename editorPlus = getByBarename(barename);
        editorPlus.setFilename(filename);
    }

    private EditorAndFilename getByBarename(String barename) {
        for (EditorAndFilename editorPlus : editors) {
            if (barename.equals(editorPlus.getBarename())) {
                return editorPlus;
            }
        }
        throw new IllegalArgumentException("Der Dateiname ohne Pfad '" + barename
                + "' ist nicht bekannt.");
    }

    public void loadAllEditorFiles() {
        for (EditorAndFilename editorPlus : editors) {
            loadEditorFile(editorPlus);
        }
    }

    public void loadAllEditorFilesInThreads() {
        for (EditorAndFilename editorPlus : editors) {
            loadEditorFileInThread(editorPlus);
        }
    }

    private void loadEditorFileInThread(EditorAndFilename editorPlus) {
        new Thread(() -> loadEditorFile(editorPlus)).start();
    }

    private void loadEditorFile(EditorAndFilename editorPlus) {
        HighlightingEditor editor = editorPlus.getEditor();
        String filename = editorPlus.getFilename();
        say("lese Datei " + filename);
        editor.openFile(filename);
    }

    /** Entfernt den Editor über den barename. */
    public void removeEditorByBarename(String barename) {
        /*
         * Das geht hier so, da die barenames als Titel verwendet werden...
         * Sonst müsste man über
         *     EditorAndFilename editorPlus = getByBarename(barename);
         * arbeiten und in TabBarWithEditorTabs die passende Methode schreiben für
         *     tabBar.removeTab(editorPlus.getEditor());
         */

        removeEditorByBarenameFromEditorList(barename);
        tabBar.removeTab(barename);
    }

    private void removeEditorByBarenameFromEditorList(String barename) {
        for (EditorAndFilename editorPlus : editors) {
            if (barename.equals(editorPlus.getBarename())) {
                editors.remove(editorPlus);
                return;
            }
        }
    }

    /** Gibt alle barenames zurück. */
    public List<String> getBarenames() {
        List<String> list = new ArrayList<>();

        for (EditorAndFilename editorPlus : editors) {
            String barename = editorPlus.getBarename();
            list.add(barename);
        }

        return list;
    }

    /** Erzeugt eine Liste der reinen HighlightingEditors. */
    public List<HighlightingEditor> getEditors() {
        List<HighlightingEditor> allEditors = new ArrayList<>();

        for (EditorAndFilename editorPlus : editors) {
            allEditors.add(editorPlus.getEditor());
        }

        return allEditors;
    }

    /** Gibt an, ob der angegebene Editor hier enthalten ist. */
    public boolean contains(HighlightingEditor editor) {
        return getEditors().contains(editor);
    }

    /** Legt den Fokus auf den angezeigten Editor. */
    public void focusSelectedEditor() {
        tabBar.focusSelectedEditor();
    }

    /** Gibt an, ob ein Editor zu dem Barename vorliegt. */
    public boolean containsBarename(String barename) {
        for (EditorAndFilename editorPlus : editors) {
            String editorBarename = editorPlus.getBarename();
            if (barename.equals(editorBarename)) {
                return true;
            }
        }

        return false;
    }

    /** Gibt den Editor über den Barename zurück. */
    public HighlightingEditor getEditorByBarename(String barename) {
        for (EditorAndFilename editorPlus : editors) {
            String editorBarename = editorPlus.getBarename();
            if (barename.equals(editorBarename)) {
                return editorPlus.getEditor();
            }
        }
        throw new IllegalArgumentException("Der Barename '" + barename + "' ist unbekannt.");
    }

    private void say(String message) {
        if (DEBUG) {
            DebugHelper.sayWithClassAndMethodAndTime(message);
        }
    }

}
