package de.duehl.swing.ui.key;

/*
 * Copyright 2025 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import static de.duehl.swing.ui.key.KeybingingDefinitions.*;

import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;

/**
 * Diese Klasse belegt eine RootPane mit bestimmten Tastenkombinationen.
 *
 * @version 1.01     2025-07-08
 * @author Christian Dühl
 */

public class BindKeysOnRootPane {

    private final JRootPane rootPane;

    public BindKeysOnRootPane(JRootPane rootPane) {
        this.rootPane = rootPane;
    }

    /** Fügt die Tastaturbindung Escape hinzu. */
    public void setKeyBindingEscape(Runnable escapeRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ESCAPE, KEYBINDING_ESCAPE);
        putIntoActionMapOfRootPane(KEYBINDING_ESCAPE, runnableToAction(escapeRunnable));
    }

    /** Fügt die Tastaturbindung Home (Pos1) hinzu. */
    public void setKeyBindingHome(Runnable homeRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_POS1, KEYBINDING_POS1);
        putIntoActionMapOfRootPane(KEYBINDING_POS1, runnableToAction(homeRunnable));
    }

    /** Fügt die Tastaturbindung End (Ende) hinzu. */
    public void setKeyBindingEnd(Runnable endRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_END, KEYBINDING_END);
        putIntoActionMapOfRootPane(KEYBINDING_END, runnableToAction(endRunnable));
    }

    /** Fügt die Tastaturbindung Home (Pos1) hinzu. */
    public void setKeyBindingCtrlHome(Runnable ctrlHomeRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_POS1, KEYBINDING_CTRL_POS1);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_POS1, runnableToAction(ctrlHomeRunnable));
    }

    /** Fügt die Tastaturbindung End (Ende) hinzu. */
    public void setKeyBindingCtrlEnd(Runnable ctrlEndRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_END, KEYBINDING_CTRL_END);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_END, runnableToAction(ctrlEndRunnable));
    }

    /** Fügt die Tastaturbindung PageUp (Bild auf) hinzu. */
    public void setKeyBindingPageUp(Runnable pageUpRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_PAGE_UP, KEYBINDING_PAGE_UP);
        putIntoActionMapOfRootPane(KEYBINDING_PAGE_UP, runnableToAction(pageUpRunnable));
    }

    /** Fügt die Tastaturbindung PageDown (Bild runter) hinzu. */
    public void setKeyBindingPageDown(Runnable pageDownRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_PAGE_DOWN, KEYBINDING_PAGE_DOWN);
        putIntoActionMapOfRootPane(KEYBINDING_PAGE_DOWN, runnableToAction(pageDownRunnable));
    }

    /** Fügt die Tastaturbindung Enter hinzu. */
    public void setKeyBindingEnter(Runnable enterRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ENTER, KEYBINDING_ENTER);
        putIntoActionMapOfRootPane(KEYBINDING_ENTER, runnableToAction(enterRunnable));
    }

    /** Fügt die Tastaturbindung Ctrl-Enter hinzu. */
    public void setKeyBindingCtrlEnter(Runnable ctrlEnterRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_ENTER, KEYBINDING_CTRL_ENTER);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_ENTER, runnableToAction(ctrlEnterRunnable));
    }

    /** Fügt die Tastaturbindung F1 hinzu. */
    public void setKeyBindingF1(Runnable f1Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F1, KEYBINDING_F1);
        putIntoActionMapOfRootPane(KEYBINDING_F1, runnableToAction(f1Runnable));
    }

    /** Fügt die Tastaturbindung F2 hinzu. */
    public void setKeyBindingF2(Runnable f2Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F2, KEYBINDING_F2);
        putIntoActionMapOfRootPane(KEYBINDING_F2, runnableToAction(f2Runnable));
    }

    /** Fügt die Tastaturbindung F3 hinzu. */
    public void setKeyBindingF3(Runnable f3Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F3, KEYBINDING_F3);
        putIntoActionMapOfRootPane(KEYBINDING_F3, runnableToAction(f3Runnable));
    }

    /** Fügt die Tastaturbindung F4 hinzu. */
    public void setKeyBindingF4(Runnable f4Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F4, KEYBINDING_F4);
        putIntoActionMapOfRootPane(KEYBINDING_F4, runnableToAction(f4Runnable));
    }

    /** Fügt die Tastaturbindung F5 hinzu. */
    public void setKeyBindingF5(Runnable f5Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F5, KEYBINDING_F5);
        putIntoActionMapOfRootPane(KEYBINDING_F5, runnableToAction(f5Runnable));
    }

    /** Fügt die Tastaturbindung F6 hinzu. */
    public void setKeyBindingF6(Runnable f6Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F6, KEYBINDING_F6);
        putIntoActionMapOfRootPane(KEYBINDING_F6, runnableToAction(f6Runnable));
    }

    /** Fügt die Tastaturbindung F7 hinzu. */
    public void setKeyBindingF7(Runnable f7Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F7, KEYBINDING_F7);
        putIntoActionMapOfRootPane(KEYBINDING_F7, runnableToAction(f7Runnable));
    }

    /** Fügt die Tastaturbindung F8 hinzu. */
    public void setKeyBindingF8(Runnable f8Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F8, KEYBINDING_F8);
        putIntoActionMapOfRootPane(KEYBINDING_F8, runnableToAction(f8Runnable));
    }

    /** Fügt die Tastaturbindung F9 hinzu. */
    public void setKeyBindingF9(Runnable f9Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F9, KEYBINDING_F9);
        putIntoActionMapOfRootPane(KEYBINDING_F9, runnableToAction(f9Runnable));
    }

    /** Fügt die Tastaturbindung F10 hinzu. */
    public void setKeyBindingF10(Runnable f10Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F10, KEYBINDING_F10);
        putIntoActionMapOfRootPane(KEYBINDING_F10, runnableToAction(f10Runnable));
    }

    /** Fügt die Tastaturbindung F11 hinzu. */
    public void setKeyBindingF11(Runnable f11Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F11, KEYBINDING_F11);
        putIntoActionMapOfRootPane(KEYBINDING_F11, runnableToAction(f11Runnable));
    }

    /** Fügt die Tastaturbindung F12 hinzu. */
    public void setKeyBindingF12(Runnable f12Runnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_F12, KEYBINDING_F12);
        putIntoActionMapOfRootPane(KEYBINDING_F12, runnableToAction(f12Runnable));
    }

    /** Fügt die Tastaturbindung "links" hinzu. */
    public void setKeyBindingLeft(Runnable leftRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_LEFT, KEYBINDING_LEFT);
        putIntoActionMapOfRootPane(KEYBINDING_LEFT, runnableToAction(leftRunnable));
    }

    /** Fügt die Tastaturbindung "rechts" hinzu. */
    public void setKeyBindingRight(Runnable rightRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_RIGTH, KEYBINDING_RIGHT);
        putIntoActionMapOfRootPane(KEYBINDING_RIGHT, runnableToAction(rightRunnable));
    }

    /** Fügt die Tastaturbindung "oben" hinzu. */
    public void setKeyBindingUp(Runnable upRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_UP, KEYBINDING_UP);
        putIntoActionMapOfRootPane(KEYBINDING_UP, runnableToAction(upRunnable));
    }

    /** Fügt die Tastaturbindung "unten" hinzu. */
    public void setKeyBindingDown(Runnable downRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_DOWN, KEYBINDING_DOWN);
        putIntoActionMapOfRootPane(KEYBINDING_DOWN, runnableToAction(downRunnable));
    }

    /** Fügt die Tastaturbindung Ctrl-links hinzu. */
    public void setKeyBindingCtrlLeft(Runnable ctrlLeftRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_LEFT, KEYBINDING_CTRL_LEFT);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_LEFT, runnableToAction(ctrlLeftRunnable));
    }

    /** Fügt die Tastaturbindung Ctrl-rechts hinzu. */
    public void setKeyBindingCtrlRight(Runnable ctrlRightRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_RIGTH, KEYBINDING_CTRL_RIGHT);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_RIGHT, runnableToAction(ctrlRightRunnable));
    }

    /** Fügt die Tastaturbindung Ctrl-oben hinzu. */
    public void setKeyBindingCtrlUp(Runnable ctrlUpRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_UP, KEYBINDING_CTRL_UP);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_UP, runnableToAction(ctrlUpRunnable));
    }

    /** Fügt die Tastaturbindung Ctrl-unten hinzu. */
    public void setKeyBindingCtrlDown(Runnable ctrlDownRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_CTRL_DOWN, KEYBINDING_CTRL_DOWN);
        putIntoActionMapOfRootPane(KEYBINDING_CTRL_DOWN, runnableToAction(ctrlDownRunnable));
    }

    /** Fügt die Tastaturbindung Alt-links hinzu. */
    public void setKeyBindingAltLeft(Runnable altLeftRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ALT_LEFT, KEYBINDING_ALT_LEFT);
        putIntoActionMapOfRootPane(KEYBINDING_ALT_LEFT, runnableToAction(altLeftRunnable));
    }

    /** Fügt die Tastaturbindung Alt-rechts hinzu. */
    public void setKeyBindingAltRight(Runnable altRightRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ALT_RIGTH, KEYBINDING_ALT_RIGHT);
        putIntoActionMapOfRootPane(KEYBINDING_ALT_RIGHT, runnableToAction(altRightRunnable));
    }

    /** Fügt die Tastaturbindung Alt-oben hinzu. */
    public void setKeyBindingAltUp(Runnable altUpRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ALT_UP, KEYBINDING_ALT_UP);
        putIntoActionMapOfRootPane(KEYBINDING_ALT_UP, runnableToAction(altUpRunnable));
    }

    /** Fügt die Tastaturbindung Alt-unten hinzu. */
    public void setKeyBindingAltDown(Runnable altDownRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_ALT_DOWN, KEYBINDING_ALT_DOWN);
        putIntoActionMapOfRootPane(KEYBINDING_ALT_DOWN, runnableToAction(altDownRunnable));
    }

    /** Fügt die Tastaturbindung Tabulator hinzu. */
    public void setKeyBindingTabulator(Runnable tabulatorRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_TABULATOR, KEYBINDING_TABULATOR);
        putIntoActionMapOfRootPane(KEYBINDING_TABULATOR, runnableToAction(tabulatorRunnable));
    }

    /** Fügt die Tastaturbindung Entfernen hinzu. */
    public void setKeyBindingDelete(Runnable deleteRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_DELETE, KEYBINDING_DELETE);
        putIntoActionMapOfRootPane(KEYBINDING_DELETE, runnableToAction(deleteRunnable));
    }

    /** Fügt die Tastaturbindung Einfügen hinzu. */
    public void setKeyBindingInsert(Runnable insertRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_INSERT, KEYBINDING_INSERT);
        putIntoActionMapOfRootPane(KEYBINDING_INSERT, runnableToAction(insertRunnable));
    }

    /** Fügt die Tastaturbindung Nummmernblock-Plus hinzu. */
    public void setKeyBindingNumpadPlus(Runnable numpadPlusRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_NUMPAD_PLUS, KEYBINDING_NUMPAD_PLUS);
        putIntoActionMapOfRootPane(KEYBINDING_NUMPAD_PLUS, runnableToAction(numpadPlusRunnable));
    }

    /** Fügt die Tastaturbindung Nummmernblock-Minus hinzu. */
    public void setKeyBindingNumpadMinus(Runnable numpadMinusRunnable) {
        putIntoInputMapWhenInFocusedWindow(KEYSTROKE_NUMPAD_MINUS, KEYBINDING_NUMPAD_MINUS);
        putIntoActionMapOfRootPane(KEYBINDING_NUMPAD_MINUS, runnableToAction(numpadMinusRunnable));
    }

    private void putIntoInputMapWhenInFocusedWindow(KeyStroke keyStroke, String actionMapKey) {
        InputMap inputMap = getInputMapWhenInFocusedWindow();
        inputMap.put(keyStroke, actionMapKey);
    }

    private InputMap getInputMapWhenInFocusedWindow() {
        InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        return inputMap;
    }

    private void putIntoActionMapOfRootPane(String keybinding, Action action) {
        rootPane.getActionMap().put(keybinding, action);
    }

    /** Erzeugt aus einem Runnable eine AbstractAction für Keybindings. */
    public static AbstractAction runnableToAction(Runnable runnable) {
        return new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                runnable.run();
            }
        };
    }

}
