/**
 *  Copyright © 2020-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.
 *
 *  In short:
 *  - This file may be used, modified, and distributed under MPL 2.0 only.
 *  - It may NOT be relicensed under GPL, LGPL, AGPL, or any other Secondary License.
 *
 *  Rationale:
 *  - Ensures that the code remains MPL-2.0.
 *  - Avoids legal conflicts with GPL-licensed libraries (e.g., VideoLAN).
 *  - Maximizes usability for commercial and security-critical applications.
 *
 */
package net.javacomm.client.chat;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
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.io.IOException;
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JToolTip;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.MatteBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nexuswob.gui.ArrowTooltip;
import org.nexuswob.gui.InputDocument;
import org.nexuswob.gui.JToolTipBorder;
import net.javacomm.client.environment.GUI;
import net.javacomm.client.resource.Resource;
import net.javacomm.multilingual.Babelfish;
import net.javacomm.multilingual.MultilingualCheckBoxMenuItem;
import net.javacomm.multilingual.MultilingualMenuItem;
import net.javacomm.multilingual.MultilingualString;
import net.javacomm.multilingual.schema.ISO639;
import net.javacomm.multilingual.schema.KEY;
import net.javacomm.share.Constants;
import net.javacomm.window.manager.Control;



public class JInput extends JPanel implements Babelfish {

  private final static Logger log = LogManager.getLogger(JInput.class);

  private static final long serialVersionUID = -5494262362709859216L;
  private JScrollPane scrollText = new JScrollPane();
  private JTextArea textArea = new JTextArea() {
    private static final long serialVersionUID = 1628367315511920662L;

    @Override
    public JToolTip createToolTip() {
      ArrowTooltip arrow = new ArrowTooltip(Resource.JQUERY_BLAU);
      arrow.setComponent(textArea);
      arrow.setTextAttributes(GUI.regularFont13, Resource.JQUERY_HELLBLAU, Resource.JQUERY_BLAU);
      arrow.setBorder(new JToolTipBorder(7, Resource.JQUERY_YELLOW, Resource.JQUERY_BLAU));
      return arrow;
    }

  };
  private MatteBorder matte;
  private InputDocument doc = new InputDocument(Constants.LEN_MESSAGE);
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private JPopupMenu popup = new JPopupMenu();
  private MultilingualMenuItem itemPaste = new MultilingualMenuItem(KEY.POPUP_PASTE);
  private MultilingualMenuItem itemZeilenumbruch = new MultilingualMenuItem(KEY.POPUP_ZEILENUMBRUCH);
  private MultilingualCheckBoxMenuItem itemTooltip = new MultilingualCheckBoxMenuItem(KEY.POPUP_TOOLTIP);
  private MultilingualString eingabeZuLang = new MultilingualString(KEY.STRING_EINGABE_ZU_LANG);
  private MultilingualString eingabeBegrenzung = new MultilingualString(KEY.STRING_EINGABE_BEGRENZUNG);
  private MultilingualString tooltip = new MultilingualString(KEY.TOOLTIP_INPUT);

  public JInput() {
    super(true);
    setLayout(new BorderLayout());
    itemPaste.addActionListener(event -> {
      Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
      Transferable transferable = clipboard.getContents(this);
      if (transferable == null) return;
      try {
        String text = (String) transferable.getTransferData(DataFlavor.stringFlavor);
        textArea.setText(textArea.getText().concat(text));
      }
      catch (UnsupportedFlavorException | IOException e) {
        log.error(e.getMessage(), e);
      }
    });
    popup.add(itemPaste);
    itemZeilenumbruch.addActionListener(event -> {
      shiftDownEnter();
    });
    popup.add(itemZeilenumbruch);
    scrollText.setViewportView(textArea);
    scrollText.setBorder(null);
    textArea.addFocusListener(new FocusAdapter() {
      @Override
      public void focusGained(FocusEvent event) {
        changes.firePropertyChange(JInput.class.getName(), Control.NULL, Control.INPUT_FOCUS);
      }

    });
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    textArea.setDocument(doc);
    doc.setDocumentFilter(new ChatmessageFilter());
    textArea.getInputMap()
        .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK), "SHIFT_ENTER");
    textArea.getActionMap().put("SHIFT_ENTER", new AbstractAction() {

      private static final long serialVersionUID = -6909033642696710886L;

      @Override
      public void actionPerformed(ActionEvent event) {
        shiftDownEnter();
      }
    });

    textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER");
    textArea.getActionMap().put("ENTER", new AbstractAction() {
      private static final long serialVersionUID = 7348629681452971214L;

      @Override
      public void actionPerformed(ActionEvent event) {
        changes.firePropertyChange("JInput", Control.NULL, Control.MESSAGE);
      }
    });

    textArea.add(popup);
    textArea.addMouseListener(new MouseAdapter() {

      @Override
      public void mousePressed(MouseEvent event) {
        if (event.isPopupTrigger()) {
          pasteFromClipboard(event);
        }
      }



      @Override
      public void mouseReleased(MouseEvent event) {
        if (event.isPopupTrigger()) {
          pasteFromClipboard(event);
        }
      }

    });
    popup.addSeparator();
    popup.add(itemTooltip);
    itemTooltip.addActionListener(event -> {
      tooltip();
      changes.firePropertyChange(JInput.class.getName(), itemTooltip.isSelected(), Control.CHATTOOLTIP);
    });
    add(BorderLayout.CENTER, scrollText);
  }



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



  void chatTooltip(boolean chattooltip) {
    itemTooltip.setSelected(chattooltip);
    tooltip();
  }



  public void clear() {
    textArea.setText(new String());
    setFocus();
  }



  public void disableInput() {
    textArea.setEditable(false);
  }



  public String getMessage() {
    return textArea.getText().replace('\n', Message.EOT.charAt(0));
  }



  @Override
  public Dimension getPreferredSize() {
    FontMetrics fm;
    int height;

    fm = textArea.getFontMetrics(getFont());
    height = fm.getAscent() + fm.getDescent() + fm.getLeading();
    return new Dimension(super.getPreferredSize().width, height * 4);
  }



  void pasteFromClipboard(MouseEvent event) {
    popup.show(textArea, event.getX(), event.getY());
  }



  public void removeAllListener() {
    for (PropertyChangeListener listener : changes.getPropertyChangeListeners()) {
      removeInputListener(listener);
    }
    for (MouseListener ml : textArea.getMouseListeners()) {
      textArea.removeMouseListener(ml);
    }
    for (KeyListener kl : textArea.getKeyListeners()) {
      textArea.removeKeyListener(kl);
    }

    for (ActionListener al : itemPaste.getActionListeners()) {
      itemPaste.removeActionListener(al);
    }

    for (ActionListener al : itemZeilenumbruch.getActionListeners()) {
      itemZeilenumbruch.removeActionListener(al);
    }

    for (FocusListener fl : textArea.getFocusListeners()) {
      textArea.removeFocusListener(fl);
    }

    for (ActionListener al : itemTooltip.getActionListeners()) {
      itemTooltip.removeActionListener(al);
    }

  }



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



  public void setBg(Color c) {
    textArea.setBackground(c);
  }



  public void setFg(Color c) {
    textArea.setForeground(c);
    textArea.setCaretColor(c);
    matte = new MatteBorder(
        JChatPane.thickness, JChatPane.thickness, JChatPane.thickness, JChatPane.thickness,
        textArea.getForeground()
    );
    setBorder(matte);
  }



  void setFocus() {
    textArea.requestFocus();
  }



  @Override
  public void setLanguage(ISO639 code) {
    itemPaste.setLanguage(code);
    itemZeilenumbruch.setLanguage(code);
    itemTooltip.setLanguage(code);
    tooltip.setLanguage(code);
    eingabeZuLang.setLanguage(code);
    eingabeBegrenzung.setLanguage(code);
    tooltip();
  }



  private void shiftDownEnter() {
    StringBuilder text = new StringBuilder(textArea.getText());
    int caretPosition = textArea.getCaretPosition();
    text.insert(caretPosition, "\n");
    textArea.setText(text.toString());
    textArea.setCaretPosition(caretPosition + 1);
  }



  private void tooltip() {
    textArea.setToolTipText(itemTooltip.isSelected() ? null : "<html>" + tooltip.toString() + "</html>");
  }

  /**
   * Eine Chatnachnachricht wird auf 500 Zeichenbeschränkt.
   */
  class ChatmessageFilter extends DocumentFilter {

    ChatmessageFilter() {}



    @Override
    public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
        throws BadLocationException {
      if (string == null) return;
      validateAndApply(fb, offset, 0, string, attr);
    }



    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
        throws BadLocationException {
      if (text == null) return;
      validateAndApply(fb, offset, length, text, attrs);
    }



    private void showWarning() {

      String neu = eingabeBegrenzung.toString().replace("XXX", String.valueOf(Constants.LEN_MESSAGE));

      JTextPane textpane = new JTextPane();
      textpane.setContentType("text/html");
      textpane.setEditable(false);
      textpane.setText(neu);

      SwingUtilities.invokeLater(() -> {
        JOptionPane.showMessageDialog(null, textpane, eingabeZuLang.toString(), JOptionPane.ERROR_MESSAGE);
      });
    }



    private void validateAndApply(FilterBypass fb, int offset, int lengthToRemove, String text,
        AttributeSet attrs) throws BadLocationException {

      int currentLength = fb.getDocument().getLength();
      // Berechnung: Was wäre die Länge nach der Operation?
      int futureLength = currentLength - lengthToRemove + text.length();

      if (futureLength <= Constants.LEN_MESSAGE) {
        // Alles im grünen Bereich -> Einfügen/Ersetzen erlauben
        fb.replace(offset, lengthToRemove, text, attrs);
      }
      else {
        // Limit überschritten -> Aktion komplett verwerfen und warnen
        showWarning();
      }
    }

  }

}
