/**
 *  Copyright © 2022-2025, Luis Andrés Lange <https://javacomm.net>
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 *  ----------------------------------------------------------------------------
 *
 *  Exhibit B - "Incompatible With Secondary Licenses" Notice
 *
 *  This Source Code Form is "Incompatible With Secondary Licenses",
 *  as defined by the Mozilla Public License, v. 2.0.
 *
 */
package net.javacomm.window.manager;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.Vector;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nexuswob.util.Util;
import net.javacomm.multilingual.Babelfish;
import net.javacomm.multilingual.Multilingual;
import net.javacomm.multilingual.schema.COMMAND;
import net.javacomm.multilingual.schema.ISO639;
import net.javacomm.multilingual.schema.KEY;
import net.javacomm.multilingual.schema.LANGUAGE;
import net.javacomm.multilingual.schema.LANGUAGES;



/*****************************************************************************/
/*                                                                           */
/*                                Class WindowManager                        */
/*                                                                           */
/*****************************************************************************/

@SuppressWarnings("serial")
public class WindowManager extends JPanel implements Babelfish {

  public final static String Tester = "Tester";
  public final static String Programmierer = "Programmierer";
  public final static String ODX = "ODX";
  public final static String Schach = "Schach";
  public final static String Welt = "El Mundo";

  private final static Logger log = LogManager.getLogger(WindowManager.class);
  private DefaultMutableTreeNode root = new DefaultMutableTreeNode();
  private DefaultTreeModel model = new DefaultTreeModel(root);
  private JScrollPane scroll = new JScrollPane();
  private JavacommTreeUI treeUI;
  private JTree tree = new JTree() {

    @Override
    public void updateUI() {
      treeUI = new JavacommTreeUI();
      setUI(treeUI);
    }

  };
  private Renderer renderer = new Renderer();
  private Object nodeSelected = new Object();
  private DefaultMutableTreeNode treeNode;
  private Class<? extends WindowManager> resource;
  private ImageIcon launchIcon;
  private ImageIcon windowList;
  private ImageIcon windowNew;
  private ImageIcon infoIcon;
  private ImageIcon privateChatIcon;
  private ImageIcon ftsIcon;
  private ImageIcon telefonEinrichtenIcon;
  private ImageIcon playerIcon;
  private ImageIcon configureIcon;
  private ImageIcon callIcon;
  private ImageIcon abnehmenIcon;
  private ImageIcon elmundoIcon;
  private ImageIcon forumIcon;
  private ImageIcon stammtischIcon;
  private ImageIcon pausenraumIcon;
  private ImageIcon schwarzeListeIcon;
  private ImageIcon administratorIcon;
  private ImageIcon screencastIcon;
  private ImageIcon screencastReceiverIcon;
  private ImageIcon projektorIcon;
  private ImageIcon telkoprojektorIcon;
  private ImageIcon tvIcon;
  private ImageIcon gruppenraumIcon;
  private ImageIcon telefonkonferenzIcon;
  private ImageIcon odxIcon;
  private ImageIcon odxreportIcon;
  private ImageIcon odxMergeIcon;
  private ImageIcon odxValidatorIcon;
  private ImageIcon mastermindIcon;
  private ImageIcon testerIcon;
  private ImageIcon kingIcon;
  private ImageIcon developerIcon;
  private ImageIcon odxChatIcon;

  private DefaultMutableTreeNode node;
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private TreeSelectionModel selectionModel;
  private Vector<WM> internalFramelist = new Vector<>(6, 6);
  private String record;
  private ISO639 language;
  private KEY key;
  private int metricwidth;

  public WindowManager() {
    language = ISO639.DE;
    setLayout(new BorderLayout());
    resource = getClass();

    launchIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.LAUNCH));
    windowList = new ImageIcon(resource.getClassLoader().getResource(WMResource.WINDOW_LIST));
    windowNew = new ImageIcon(resource.getClassLoader().getResource(WMResource.WINDOW_NEW));
    infoIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.INFO_25x25));
    privateChatIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.PRIVAT_26x26));
    ftsIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.FTP));
    telefonEinrichtenIcon = new ImageIcon(
        resource.getClassLoader().getResource(WMResource.TELEFONEINRICHTEN_42x26)
    );
    playerIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.MP3PLAYER));
    configureIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.CONFIGURE));
    callIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.CALL));
    abnehmenIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.PHONE_RING));
    elmundoIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.EL_MUNDO));
    forumIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.FORUM_26x26));
    stammtischIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.BESPRECUNGSRAUM_35x26));
    pausenraumIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.PAUSENRAUM));
    gruppenraumIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.GRUPPENRAUM_25x26));
    schwarzeListeIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.SCHWARZE_LISTE));
    administratorIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.ADMINISTRATOR));
    screencastIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.SHARE_36x26));
    screencastReceiverIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.HANDSHAKE_39x26));
    projektorIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.PROJEKTOR_43x26));
    telkoprojektorIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.VIDEOMEETING_25x25));
    tvIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.LIVESTREAM_25x25));
    telefonkonferenzIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.CONFERENCE_23x26));
    odxIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.ODX));
    odxreportIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.ODXREPORT));
    odxMergeIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.ODXMERGE_25x26));
    odxValidatorIcon = new ImageIcon(
        resource.getClassLoader().getResource(WMResource.ODX_SCHEMA_VALIDIEREN_19x26)
    );
    mastermindIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.MASTERMIND_19x26));
    testerIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.TESTER_26x26));
    kingIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.KING_26x26));
    developerIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.DEVELOPER_26x26));
    odxChatIcon = new ImageIcon(resource.getClassLoader().getResource(WMResource.ODX_26x26));

    // root.setUserObject ("Fenster hervorheben");

    // tree.setExpandsSelectedPaths (true);
    tree.setToggleClickCount(1);
    tree.setModel(model);
    tree.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

    tree.addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent event) {
        TreePath path = tree.getPathForLocation(event.getX(), event.getY());
        if (path == null) {
          selectionModel.clearSelection();
          return;
        }
        if (path.getParentPath() == null) {
          selectionModel.clearSelection();
          return;
        }
        nodeSelected = path.getLastPathComponent();
        treeNode = (DefaultMutableTreeNode) nodeSelected;
        selectionModel.clearSelection();
        changes.firePropertyChange(treeNode.toString(), Control.NULL, Control.SELECTED);
      }
    });

    tree.setCellRenderer(renderer);
    selectionModel = tree.getSelectionModel();
    scroll.setViewportView(tree);
    scroll.setBorder(null);
    scroll.getHorizontalScrollBar().setUnitIncrement(12);
    scroll.getVerticalScrollBar().setUnitIncrement(12);

    add(BorderLayout.CENTER, scroll);

  }



  /***************************************************************************/
  /*                                                                         */
  /* Innerclass Renderer */
  /*                                                                         */
  /***************************************************************************/

  class Renderer extends DefaultTreeCellRenderer {

    private static final long serialVersionUID = -4795184497920125981L;

    public Renderer() {
      setOpaque(false);
    }



    @Override
    public Dimension getPreferredSize() {
      int width = Math.max(WindowManager.this.getSize().width, metricwidth + 70);
      return new Dimension(width, 32);
    }



    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
        boolean leaf, int row, boolean hasFocus) {

      setTextSelectionColor(
          value.toString().equals(record) ? WMResource.JQUERY_BLAU : WMResource.JQUERY_TEXTBRAUN
      );
      setTextNonSelectionColor(
          value.toString().equals(record) ? WMResource.JQUERY_BLAU : WMResource.JQUERY_TEXTBRAUN
      );
      setBorder(new EmptyBorder(3, 3, 3, 3));
      super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);

      // Hier kannst du die Knoten individuell anpassen
      if (value instanceof DefaultMutableTreeNode) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
        if (node.isRoot()) {
          setIcon(expanded ? windowList : windowNew);
          return this;
        }
        if (node.getUserObject() == null) return this;
        String nodeName = node.getUserObject().toString();

        Optional<WM> found = internalFramelist.stream().filter(wm -> wm.getBaumansicht().equals(nodeName))
            .findFirst();

        if (found.isPresent()) {
          WM wm = found.get();
          switch(wm.getType()) {
            case ABNEHMEN:
              setIcon(abnehmenIcon);
              break;
            case ADMINISTRATOR:
              setIcon(administratorIcon);
              break;
            case ANRUFEN:
              setIcon(callIcon);
              break;
            case BESPRECHNUNGSRAUM:
              if (nodeName.equals(WindowManager.Schach)) {
                setIcon(kingIcon);
              }
              else if (nodeName.equals(WindowManager.Tester)) {
                setIcon(testerIcon);
              }
              else if (nodeName.equals(WindowManager.Programmierer)) {
                setIcon(developerIcon);
              }
              else if (nodeName.equals(WindowManager.ODX)) {
                setIcon(odxChatIcon);
              }
              else {
                setIcon(stammtischIcon);
              }
              break;
            case CONFIGURE:
              setIcon(configureIcon);
              break;
            case FORUM:
              if (nodeName.equals(WindowManager.Welt)) {
                setIcon(elmundoIcon);
              }
              else {
                setIcon(forumIcon);
              }
              break;
            case FTS:
              setIcon(ftsIcon);
              break;
            case GRUPPENRAUM:
              setIcon(gruppenraumIcon);
              break;
            case INFO:
              setIcon(infoIcon);
              break;
            case CHATROOMS:
              setIcon(launchIcon);
              break;
            case MASTERMIND:
              setIcon(mastermindIcon);
              break;
            case PAUSENRAUM:
              setIcon(pausenraumIcon);
              break;
            case PLAYER:
              setIcon(playerIcon);
              break;
            case PRIVATE_CHAT:
              setIcon(privateChatIcon);
              break;
            case PROJEKTOR:
              setIcon(projektorIcon);
              break;
            case SCHWARZE_LISTE:
              setIcon(schwarzeListeIcon);
              break;
            case SCREENRECEIVER:
              setIcon(screencastReceiverIcon);
              break;
            case TELEFONKONFERENZ:
              setIcon(telefonkonferenzIcon);
              break;
            case TELEFON_EINRICHTEN:
              setIcon(telefonEinrichtenIcon);
              break;
            case LIVESTREAMS:
              setIcon(tvIcon);
              break;
            case TELKOPROJEKTOR:
              setIcon(telkoprojektorIcon);
              break;
            case SCREENCAST:
              setIcon(screencastIcon);
              break;
            default:
              break;
          }
        }

        else if (leaf) {
          log.info("nodeName1=" + nodeName);
          setIcon(leafIcon);
        }
        else if (expanded) {
          log.info("nodeName2=" + nodeName);
          setIcon(openIcon);
        }
        else {
          log.info(nodeName);
          log.info("nodeName3=" + nodeName);
          setIcon(closedIcon);
        }
      }
      return this;
    }
  }



  /**
   * Hänge ein neues Fenster in die Baumansicht.
   *
   * @param wm
   *            ein InternalFrame
   * @param der
   *            der Blattname in der Baumansicht sollte der Fenstertitel sein
   */
  public void addWindow(WM wm) {
    // Ist WM in Internal List
    for (WM tmp : internalFramelist) {
      if (tmp.getId() == wm.getId()) return;
    }
    internalFramelist.addElement(wm);
    // hier wird der Knotenname gesetzt
    node = new DefaultMutableTreeNode(wm);
    FontMetrics fm = getFontMetrics(getFont());
    metricwidth = fm.stringWidth(wm.getBaumansicht());

    node.setUserObject(wm.getBaumansicht());

    // finde Chatlist

    Optional<WM> launcherWM = internalFramelist.stream()
        .filter(launcher -> launcher.getType() == Frames.CHATROOMS).findFirst();

    if (wm.getType() == Frames.BESPRECHNUNGSRAUM) {
      DefaultMutableTreeNode gefunden = (DefaultMutableTreeNode) Util.findInEnumeration(
          root.preorderEnumeration(),
          chatlist -> chatlist.toString().equals(launcherWM.get().getBaumansicht())
      );
      model.insertNodeInto(node, gefunden, gefunden.getChildCount());
    }
    else if (wm.getType() == Frames.PAUSENRAUM) {
      DefaultMutableTreeNode gefunden = (DefaultMutableTreeNode) Util.findInEnumeration(
          root.preorderEnumeration(),
          chatlist -> chatlist.toString().equals(launcherWM.get().getBaumansicht())
      );
      model.insertNodeInto(node, gefunden, gefunden.getChildCount());
    }
    else if (wm.getType() == Frames.GRUPPENRAUM) {
      DefaultMutableTreeNode gefunden = (DefaultMutableTreeNode) Util.findInEnumeration(
          root.preorderEnumeration(),
          chatlist -> chatlist.toString().equals(launcherWM.get().getBaumansicht())
      );
      model.insertNodeInto(node, gefunden, gefunden.getChildCount());
    }
    else if (wm.getType() == Frames.PRIVATE_CHAT) {
      DefaultMutableTreeNode gefunden = (DefaultMutableTreeNode) Util.findInEnumeration(
          root.preorderEnumeration(),
          chatlist -> chatlist.toString().equals(launcherWM.get().getBaumansicht())
      );
      model.insertNodeInto(node, gefunden, gefunden.getChildCount());
    }
    else if (wm.getType() == Frames.FORUM) {
      DefaultMutableTreeNode gefunden = (DefaultMutableTreeNode) Util.findInEnumeration(
          root.preorderEnumeration(),
          chatlist -> chatlist.toString().equals(launcherWM.get().getBaumansicht())
      );
      model.insertNodeInto(node, gefunden, gefunden.getChildCount());
    }
    else {
      model.insertNodeInto(node, root, root.getChildCount());
    }

    tree.scrollPathToVisible(new TreePath(node.getPath()));
    scroll.getHorizontalScrollBar().setValue(0);
    record = wm.getBaumansicht();
  }



  public void addWMListener(PropertyChangeListener l) {
    changes.addPropertyChangeListener(l);
  }



  /**
   * Eine Liste aller Titelfenster.
   *
   * @return
   */
  public List<String> getFrameList() {
    List<String> titel = new ArrayList<>();
    for (WM wm : internalFramelist) {
      titel.add(wm.getFrameTitleId());
    }
    return titel;
  }



  /**
   * Die Hintergrundfarbe im Fensterbaum wird rot eingefärbt.
   * 
   * @deprecated das Fenster wird geschlossen
   */
  @Deprecated
  public void offline() {
    tree.setBackground(WMResource.JQUERY_RED);
  }



  public void removeAllListener() {

    for (PropertyChangeListener tmp : changes.getPropertyChangeListeners()) {
      removeWMListener(tmp);
    }

    for (MouseListener tmp : tree.getMouseListeners()) {
      tree.removeMouseListener(tmp);
    }

  }



  public void removeWM(WM wm) {
    Enumeration<TreeNode> enum15 = root.preorderEnumeration();
    for (; enum15.hasMoreElements();) {
      node = (DefaultMutableTreeNode) enum15.nextElement();
      if (node.toString().equals(wm.getBaumansicht())) {
        model.removeNodeFromParent(node);
        internalFramelist.removeElement(wm);
        break;
      }
    }
  }



  public void removeWMListener(PropertyChangeListener l) {
    changes.removePropertyChangeListener(l);
  }



  /**
   * Der alte Fenstername wird durch den neuen ersetzt.
   *
   * @param oldname
   *                nombre de la ventana antigua
   * @param newname
   *                nombre de la ventana nueva
   */
  public void replaceFramename(String oldname, String newname, long id) {
    if (oldname == null) throw new IllegalArgumentException("oldname ist null");
    if (oldname == "") return;
    if (oldname.equals(newname)) return;
    Enumeration<TreeNode> enum15 = root.preorderEnumeration();
    for (; enum15.hasMoreElements();) {
      node = (DefaultMutableTreeNode) enum15.nextElement();
      if (node.toString().equals(oldname)) {
        FontMetrics fm = getFontMetrics(getFont());
        metricwidth = fm.stringWidth(newname);
        node.setUserObject(newname);
        model.nodeChanged(node);
        break;
      }
    }
  }

  /**
   * In der Navigationsansicht wird das aktuelle Fenster farblich hervorgehoben.
   *
   * @param value
   *              der Fenstername in der Baumansicht
   */
  public void setActivNode(String value) {
    record = value;
  }

  /**
   * Zeichne den Hintergrundfarbverlauf von oben nach unten in einem
   * {@code JTree}.
   * 
   * @param top
   *               von Farbe
   * @param bottom
   *               nach Farbe
   */
  public void setBackgroundGradient(Color top, Color bottom) {
    treeUI.setBackgroundGradient(top, bottom);
  }



  /**
   * Der Menuschlüssel für die Textanzeige wird gesetzt.
   *
   * @param key
   *            der Zugriffschlüssel für JMenu, JMenuItem und JCheckBoxMenuItem in
   *            der Oberfläche
   *
   */
  public void setKey(KEY key) {
    setLanguage(key, language);
  }



  /**
   * Die Sprache wird gesetzt.
   *
   * @param language
   *                 die Sprache
   */
  @Override
  public void setLanguage(ISO639 language) {
    setLanguage(key, language);
  }



  /**
   * Die Sprache darf jederzeit geändert werden.
   *
   * @param key
   *                 der Schlüssel für das ausgewählte Kommando
   * @param language
   *                 der Text wird in dieser Sprache angezeigt
   */
  public void setLanguage(KEY key, ISO639 language) {
    if (key == null) throw new IllegalArgumentException("key ist null");
    if (language == null) throw new IllegalArgumentException("language ist null");
    this.language = language;
    this.key = key;
    COMMAND command = Multilingual.getInstance().getCommand(this.key);
    LANGUAGES languages = command.getLANGUAGES();
    if (languages == null) return;
    for (LANGUAGE tmp : languages.getLANGUAGE()) {
      if (tmp.getISO639() != language) continue;
      if (tmp.getTEXT() != null) {
        renderer.setText(tmp.getTEXT());
      }
      break;
    }

  }

}
