/**
 *  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.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.client.config.schema.Sorte;
import net.javacomm.client.resource.Resource;
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.protocol.ChatUser;
import net.javacomm.protocol.RecordInterface;
import net.javacomm.share.Constants;
import net.javacomm.window.manager.Control;
import net.javacomm.window.manager.Frames;
import net.javacomm.window.manager.WM;
import net.javacomm.window.manager.WindowAnrufenEvent;
import net.javacomm.window.manager.WindowAnrufenListener;
import net.javacomm.window.manager.WindowManagerInternalFrame;



/**
 *
 * Ein Private-, Public-, Temporary-Room.
 *
 * @author llange
 *
 */
public class JChatFrame extends WindowManagerInternalFrame implements WindowAnrufenListener {

  /*****************************************************************************/
  /*                                                                           */
  /* Class InternalAction */
  /*                                                                           */
  /*****************************************************************************/
  class InternalAction extends InternalFrameAdapter {

    @Override
    public void internalFrameActivated(InternalFrameEvent event) {
      centerPane.setFocus();
    }



    @Override
    public void internalFrameClosing(InternalFrameEvent event) {
      changes.firePropertyChange(JChatFrame.this.getClass().getName(), Control.NULL, Control.QUIT);
    }

  }

  private final static Logger log = LogManager.getLogger(JChatFrame.class);
  private static final long serialVersionUID = -6959551542128286854L;
  private final static Dimension MIN_NORMAL = new Dimension(670, 330);
  private InternalAction internalAction = new InternalAction();
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private JChatuserOnline chatuserOnline = new JChatuserOnline();
  private JChatPane centerPane;
  private GridBagLayout gridbag = new GridBagLayout();
  private GridBagConstraints con = new GridBagConstraints();
  private JMenuBar menubar = new JMenuBar();
  private JMenu menuChat;
  private MultilingualMenuItem itemBye = new MultilingualMenuItem(KEY.AUF_WIEDERSEHEN);
  private boolean isPrivate = false; // der Nickname erscheint nur bei false im Chatfenster
  private JPanel chatPanel = new JPanel();

  private Frames type = Frames.FORUM;
  private Class<? extends JChatFrame> resource;
  private String remotePseudoid;

  private JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  private MultilingualString largeDatei = new MultilingualString(KEY.SERVER_DIE_DATEI_IST);

  private MultilingualString anhang = new MultilingualString(KEY.STRING_ANHANG);

  private ComponentAdapter componentAdapter = new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e) {
      Rectangle rectangle = JChatFrame.this.getBounds();
      int breite = (int) rectangle.getSize().getWidth() - WindowManagerInternalFrame.SPLITWIDTH;
      setHorizontalDivider(breite);
    }
  };

  /**
   * Erzeuge einen Chatraum.
   * 
   * @param record
   *               der Raumname
   */
  public JChatFrame(String record) {
    super(record);
    centerPane = new JChatPane(record);
    init();
    addComponentListener(componentAdapter);
    setMinimumSize(MIN_NORMAL);
  }



  @Override
  public void activated() {
    try {
      setSelected(true);
      windowManager.setActivNode(title);
    }
    catch (PropertyVetoException e) {}
  }



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



  /**
   * Wenn das Farbschema geändert wird, muss diese Methode aufgerufen werden,
   * damit die Spaltenüberschriften wieder zentriert sind.
   *
   */
  public void centerHeader() {
    centerPane.centerHeader();
  }



  /**
   * Soll ein Tooltip eingeblendet werden?
   *
   * @param chattooltip
   *                    {@code true}, Tooltip wird angezeigt
   */
  public void chatTooltip(boolean chattooltip) {
    centerPane.chatTooltip(chattooltip);
  }



  public void clearMessages() {
    centerPane.clearMessages();
  }



  /**
   * Der Gegenüber in einem Private Chat.
   * 
   * @return der Gegenüber ist eine RemotePseudoid
   */
  public String getRemotePseudoid() {
    return remotePseudoid;
  }



  @Override
  public Frames getType() {
    return type;
  }



  private void init() {
    resource = getClass();
    addInternalFrameListener(internalAction);
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    setMaximizable(true);
    setResizable(true);
    setIconifiable(true);
    setClosable(true);
    JChatFrame.this.setDoubleBuffered(true);
    setJMenuBar(menubar);

    menuChat = new JMenu("Chat");
    menuChat.setMnemonic(KeyEvent.VK_C);
    menubar.add(menuChat);

    itemBye.addActionListener(event -> {
      try {
        setClosed(true);
      }
      catch (PropertyVetoException e) {
        log.error(e.getMessage(), e);
      }
    });
    itemBye.setIcon(new ImageIcon(resource.getResource(Resource.ADIOS_28x28)));
    menuChat.add(itemBye);

    chatPanel.setLayout(gridbag);

    con.fill = GridBagConstraints.BOTH;
    con.weighty = 1.0;
    gridbag.setConstraints(chatuserOnline, con);
    chatPanel.add(chatuserOnline);
    chatuserOnline.addChatInfoListener(new PropertyChangeListener() {

      @Override
      public void propertyChange(PropertyChangeEvent event) {
        changes.firePropertyChange(event);
      }
    });

    con.weightx = 1.0;
    gridbag.setConstraints(centerPane, con);
    chatPanel.add(centerPane);

    windowManager.addWMListener(propertyEvent -> {
      changes.firePropertyChange(propertyEvent);
    });

    setLayout(new BorderLayout());
    add(splitpane, BorderLayout.CENTER);

    splitpane.setLeftComponent(chatPanel);
    splitpane.setRightComponent(windowManager);
    splitpane.setOneTouchExpandable(true);
    windowManager.setMinimumSize(new Dimension(0, 0));
    chatPanel.setMinimumSize(new Dimension(0, 0));

    centerPane.addCenterListener(event -> {
      // welcher Frame hat das Ereignis ausgelöst?
      changes.firePropertyChange(event);
    });

  }



  @Override
  public void insertWM(WM value) {
    windowManager.addWindow(value);
  }



  public boolean isPrivate() {
    return isPrivate;
  }



  // zunächst nur für private chat, falls die Gegenseite die Verbindung abbaut.
  /**
   * Die Gegenseite baut die Verbindung ab. Alles offline.
   */
  public void offline() {
    setInputBackground(Resource.JQUERY_RED.getRGB());
//    wm.offline();
    centerPane.offline();
  }



  public void removeAllListener() {
    windowManager.removeAllListener();
    centerPane.removeAllListener();
    for (PropertyChangeListener listener : changes.getPropertyChangeListeners()) {
      changes.removePropertyChangeListener(listener);
    }
    removeComponentListener(componentAdapter);
    for (ActionListener al : itemBye.getActionListeners()) {
      itemBye.removeActionListener(al);
    }
  }



  /**
   * Die Anlage wird entfernt.
   *
   */
  public void removeAnlage() {
    centerPane.removeAnlage();
  }



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



  @Override
  public void removeWM(WM record) {
    windowManager.removeWM(record);
  }



  @Override
  public void ringRing(WindowAnrufenEvent event) {
    extText = event.getNickname();
    getMultiAbnehmen().setLanguage(KEY.FRAME_ABNEHMEN, event.getCode(), extText);
    getMultiAnrufen().setLanguage(KEY.TELEFON_ANRUFEN, event.getCode(), extText);
  }



  /**
   * Der Anwender hat eine Datei als Anlage ausgewählt.
   *
   * @param absoluteFilename
   *                         ein absoluter Dateiname
   */
  public void setAnlage(String absoluteFilename) {
    try {
      if (Files.size(Paths.get(absoluteFilename)) > Constants.MAX_FILESIZE) {
        String neu = largeDatei.toString()
            .replace("XXXMB", "<b>" + String.valueOf(Constants.MAX_FILESIZE / 1024 / 1024) + "MB</b>");

        JOptionPane.showMessageDialog(
            JChatFrame.this,
            "<html>" + "<b>"
                + "<span style=\"color:red\">"
                + absoluteFilename
                + "</span>"
                + "</b>"
                + neu
                + "</html>",
            anhang.toString(), JOptionPane.ERROR_MESSAGE
        );
        return;
      }
      centerPane.setAnlage(absoluteFilename);
    }
    catch (IOException e) {
      log.warn(e.getMessage());
    }
  }



  @Override
  public void setBackgroundGradient(Color color1, Color color2) {
    windowManager.setBackgroundGradient(color1, color2);
  }



  /**
   * Ein Anlage wird in dieses Verzeichnis kopiert.
   *
   * @param dir
   *            ein Verzeichnis
   */
  public void setDownloadDir(String dir) {
    centerPane.setDownloadDir(dir);
  }



  @Override
  public void setFrameIcon(Icon icon) {
    super.setFrameIcon(icon);
  }



  @Override
  public void setFrameTitleId(String str) {}



  /**
   * Die Trennleiste wird in horizontaler Richtung positioniert. Die Angabe
   * erfolgt in Pixel. 0 ist ganz links und {@code SCREEN_WIDTH} ist ganz rechts.
   *
   *
   * @param pixel
   *              die X-Position
   */
  public void setHorizontalDivider(int pixel) {
    splitpane.setDividerLocation(pixel);
  }



  /**
   *
   * @param bg
   */
  public void setInputBackground(int bg) {
    centerPane.setInputBackground(bg);
  }



  public void setInputBackground(String nickname, int bg) {
    centerPane.setInputBackground(bg);
    chatuserOnline.updateBackground(nickname, new Color(bg));
    centerPane.updateBackground(nickname, bg); // Ticket 13 Private Chat
  }



  /**
   *
   * @param fg
   */
  public void setInputForeground(int fg) {
    centerPane.setInputForeground(fg);
  }



  public void setInputForeground(String nickname, int fg) {
    centerPane.setInputForeground(fg);
    chatuserOnline.updateForeground(nickname, new Color(fg));
    centerPane.updateForeground(nickname, fg); // Ticket 13 Private Chat
  }



  @Override
  public void setLanguage(ISO639 code) {
    largeDatei.setLanguage(code);
    anhang.setLanguage(code);
    centerPane.setLanguage(code);
    chatuserOnline.setLanguage(code);
    itemBye.setLanguage(code);

    treeView(code);
  }



  public void setMessage(RecordInterface chatmessage) throws MessageException {
    // log.info("zur Anzeige");
    centerPane.setMessage(chatmessage);
  }



  /**
   * Wird von außen aufgerufen, wenn ein Update Nockname vorgenommen wurde. Der
   * Raum wurde unter diesem Nickname geöffnet.
   * 
   * @param nickname
   */
  public void setUpdateNickname(String nickname) {
    chatuserOnline.setUpdateNickname(nickname);
  }



  /**
   * Der Chatraum wird auf PrivateChat gesetzt.
   * 
   * @param remotePseudoid
   *                       an den Anwender mit dieser Session werden die
   *                       Nachrichten gesendet
   */
  public void setPrivate(String remotePseudoid) {
    isPrivate = true;
    this.remotePseudoid = remotePseudoid;
    type = Frames.PRIVATE_CHAT;
    centerPane.setRoomtype(type);
    for (PropertyChangeListener listener : chatuserOnline.getPropertyChangeListeners()) {
      chatuserOnline.removePropertyChangeListener(listener);
    }
    chatPanel.remove(chatuserOnline);
  }



  /**
   * Die Hintergrundfarbe im Nachrichteneingabefeld.
   *
   * @param eis
   *            eine Eissorte
   */
  public void setTickerBackground(Sorte eis) {
    centerPane.setTickerBackground(eis);
  }



  @Override
  public void setType(Frames value) {
    type = value;
    chatuserOnline.setImage(value);
    centerPane.setRoomtype(value);
  }



  /**
   * Dieser Anwender hat den Raum geöffnet.
   * 
   * @param userid
   */
  public void setUserID(String userid) {
    chatuserOnline.setUserID(userid);
  }



  public void setUserList(ChatUser value[]) {
    chatuserOnline.setUserList(value);
  }

}
