/**
 *  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.phone;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.net.URL;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
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 org.nexuswob.gui.swing.NimbusCellRenderer;
import net.javacomm.client.gui.JSliderVolumePanel;
import net.javacomm.client.resource.Resource;
import net.javacomm.multilingual.MultilingualButton;
import net.javacomm.multilingual.MultilingualLabel;
import net.javacomm.multilingual.MultilingualString;
import net.javacomm.multilingual.schema.ISO639;
import net.javacomm.multilingual.schema.KEY;
import net.javacomm.protocol.UserOnline;
import net.javacomm.window.manager.Control;
import net.javacomm.window.manager.Frames;
import net.javacomm.window.manager.WM;
import net.javacomm.window.manager.WMResource;
import net.javacomm.window.manager.WindowAnrufenEvent;
import net.javacomm.window.manager.WindowAnrufenListener;
import net.javacomm.window.manager.WindowManagerInternalFrame;



public class JAnrufenFrame extends WindowManagerInternalFrame implements WindowAnrufenListener {

  /**
   * Das Fenster wird geschlossen.
   * 
   * @author llange
   *
   */
  private class InternalAction extends InternalFrameAdapter {

    @Override
    public void internalFrameClosed(InternalFrameEvent event) {
      buttonSchliessen.removeActionListener(buttonCloseListener);
      knopfleiste.buttonHoererAbnehmen.removeActionListener(buttonHoererAbnehmenListener);
      knopfleiste.buttonHoererAuflegen.removeActionListener(buttonHoererAuflegenListener);
      knopfleiste.buttonRotary.removeActionListener(buttonRotaryListener);
      knopfleiste.buttonShare.removeActionListener(buttonShareListener);

      for (ActionListener listener : comboPersonen.getActionListeners()) {
        comboPersonen.removeActionListener(listener);
      }
      changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.CLOSE);
      windowManager.removeAllListener();
    }

  }

  /**
   * Die Knopfleiste mit Abnhmen, Wählen, Auflegen.
   */
  private class Knopfleiste extends JPanel {

    private static final long serialVersionUID = -572390491600165935L;
    private GridLayout grid = new GridLayout(1, 4, 9, 24);
    private MultilingualButton buttonHoererAbnehmen = new MultilingualButton(KEY.BUTTON_HOERER_ABNEHMEN);
    private MultilingualButton buttonHoererAuflegen = new MultilingualButton(KEY.BUTTON_HOERER_AUFLEGEN);
    private MultilingualButton buttonRotary = new MultilingualButton(KEY.BUTTON_DIAL);
    private MultilingualButton buttonShare = new MultilingualButton(KEY.BUTTON_SHARE);
    private Class<? extends Knopfleiste> resource2;

    private Knopfleiste() {
      resource2 = getClass();
      setLayout(grid);
      add(buttonHoererAbnehmen);
      buttonHoererAbnehmen.setIcon(new ImageIcon(resource2.getResource(Resource.ABNEHMEN)));

      add(buttonRotary);
      buttonRotary.setIcon(new ImageIcon(resource2.getResource(Resource.ROTARY)));

      add(buttonHoererAuflegen);
      buttonHoererAuflegen.setIcon(new ImageIcon(resource2.getResource(Resource.AUFLEGEN)));

      add(buttonShare);
      buttonShare.setIcon(new ImageIcon(resource2.getResource(Resource.DESKTOP_25x25)));

    }



    /**
     * Aktiviere und deaktiviere Knöpfe. Anschließend löse eine Abnehmen-Event aus.
     * 
     */
    private void hoererAbnehmen() {

      buttonHoererAbnehmen.removeActionListener(buttonHoererAbnehmenListener);
      buttonRotary.addActionListener(buttonRotaryListener);
      buttonHoererAuflegen.addActionListener(buttonHoererAuflegenListener);

      buttonDisabled(buttonHoererAbnehmen);
      buttonEnabled(buttonHoererAuflegen);
      buttonEnabled(buttonRotary);
    }



    /**
     * Der Telefonhörer wird aufgelegt. Die Methode wird auch aufgerufen, wenn die
     * Gegenseite Telefonate ablehnt.
     * 
     */
    public void hoererAuflegen() {
      buttonRotary.removeActionListener(buttonRotaryListener);
      buttonHoererAuflegen.removeActionListener(buttonHoererAuflegenListener);
      buttonShare.removeActionListener(buttonShareListener);
      buttonHoererAbnehmen.addActionListener(buttonHoererAbnehmenListener);

      buttonDisabled(buttonHoererAuflegen);
      buttonDisabled(buttonRotary);
      buttonDisabled(buttonShare);
      buttonEnabled(buttonHoererAbnehmen);

    }



    /**
     * Alle Knöpfe sind rot und ausgeschaltet. Die Listener für Abnehmen, Wählen,
     * Auflegen werden entfernt.
     * 
     */
    void noRecipients() {

      buttonHoererAbnehmen.removeActionListener(buttonHoererAbnehmenListener);
      buttonHoererAuflegen.removeActionListener(buttonHoererAuflegenListener);
      buttonRotary.removeActionListener(buttonRotaryListener);
      buttonShare.removeActionListener(buttonShareListener);

      buttonDisabled(buttonHoererAbnehmen);
      buttonDisabled(buttonHoererAuflegen);
      buttonDisabled(buttonRotary);
      buttonDisabled(buttonShare);

      for (ActionListener listener : buttonSchliessen.getActionListeners()) {
        buttonSchliessen.removeActionListener(listener);
      }
      buttonSchliessen.addActionListener(buttonCloseListener);
      buttonEnabled(buttonSchliessen);

    }



    /**
     * Mindestends eine Person ist online und kann angerufen werden. Die Listener
     * für Wählen und Auflegen werden deaktiviert und beide Knöpfe sind rot.
     * 
     */
    void starten() {

      buttonEnabled(buttonHoererAbnehmen);
      buttonDisabled(buttonHoererAuflegen);
      buttonDisabled(buttonRotary);
      buttonDisabled(buttonShare);

      if (buttonHoererAbnehmen.getActionListeners().length == 0)
        buttonHoererAbnehmen.addActionListener(buttonHoererAbnehmenListener);
      buttonHoererAuflegen.removeActionListener(buttonHoererAuflegenListener);
      buttonRotary.removeActionListener(buttonRotaryListener);
      buttonShare.removeActionListener(buttonShareListener);

    }

  }

  public final static int SCREEN_WIDTH = 750;
  public final static int SCREEN_HEIGHT = 360;

  private static final long serialVersionUID = -8852758676583100501L;
  private final static Logger log = LogManager.getLogger(JAnrufenFrame.class);
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private InternalAction internalAction = new InternalAction();
  private JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  private JPanel mainpanel = new JPanel();
  private MultilingualLabel labelPerson = new MultilingualLabel(KEY.LABEL_DIESE_PERSONEN_SIND_ONLINE);
  private JLabel labelPicture = new JLabel();
  private GridBagLayout gridbag = new GridBagLayout();
  private GridBagConstraints con = new GridBagConstraints();
  private JScrollPane scrollpane = new JScrollPane();
  private JComboBox<String> comboPersonen = new JComboBox<>();
  private Knopfleiste knopfleiste = new Knopfleiste();
  private JSeparator separator = new JSeparator();
  private MultilingualButton buttonSchliessen = new MultilingualButton(KEY.BUTTON_SCHLIESSEN);
  private String currentNickname;
  private ActionListener buttonRotaryListener;
  private ActionListener buttonHoererAbnehmenListener;
  private ActionListener buttonHoererAuflegenListener;
  private ActionListener buttonShareListener;
  private MultilingualString micofonNichtEingeschaltet = new MultilingualString(
      KEY.STRING_MIKROFON_IST_NICHT_EINGESCHALTET
  );
  private MultilingualString telefonieren = new MultilingualString(KEY.STRING_TELEFONIEREN);

  private JSliderVolumePanel volumepanel = new JSliderVolumePanel();

  private PropertyChangeListener anrufenListener;
  private boolean animated = false;

  private ActionListener buttonCloseListener = new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
      try {
        setClosed(true);
      }
      catch (PropertyVetoException event) {
        log.error(event.getMessage(), event.getCause());
      }
    }

  };

  public JAnrufenFrame() {

    URL url = getClass().getResource(Resource.CALL);
    setFrameIcon(new ImageIcon(url));
    setMaximizable(false);
    setResizable(true);
    setIconifiable(true);
    setClosable(false);
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    setFrameTitleId(getType().toString());

    setSize(SCREEN_WIDTH, SCREEN_HEIGHT); // +30

    addInternalFrameListener(internalAction);

    splitpane.setRightComponent(windowManager);
    splitpane.setLeftComponent(scrollpane);
    splitpane.setDividerLocation(SCREEN_WIDTH - 201);
    splitpane.setOneTouchExpandable(true);

    scrollpane.setViewportView(mainpanel);
    scrollpane.setBorder(null);
    scrollpane.getHorizontalScrollBar().setUnitIncrement(12);
    scrollpane.getVerticalScrollBar().setUnitIncrement(12);

    mainpanel.setMinimumSize(new Dimension(0, 0));
    mainpanel.setLayout(gridbag);

    con.gridx = 0;
    con.gridy = 0;
    con.gridwidth = 3;
    con.insets = new Insets(6, 6, 12, 0);
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(volumepanel, con);
    mainpanel.add(volumepanel);
    anrufenListener = new PropertyChangeListener() {

      @Override
      public void propertyChange(PropertyChangeEvent event) {
        changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.TELEFON_VOLUME);
      }

    };
    volumepanel.addVolumeListener(anrufenListener);

    con.gridx = 1;
    con.gridy = 1;
    con.gridwidth = 1;
    con.insets = new Insets(0, 6, 12, 0);
    con.fill = GridBagConstraints.NONE;
    gridbag.setConstraints(labelPicture, con);
    mainpanel.add(labelPicture);
    labelPicture.setIcon(new ImageIcon(getClass().getResource(Resource.TELEFON_PICTURE)));

    con.gridx = 0;
    con.gridy = 2;
    con.gridwidth = 1;
    con.insets = new Insets(0, 6, 12, 0);
    con.fill = GridBagConstraints.NONE;
    gridbag.setConstraints(labelPerson, con);
    mainpanel.add(labelPerson);

    con.gridx = 1;
    con.gridy = 2;
    con.gridwidth = 1;
    con.insets = new Insets(0, 6, 12, 0);
    con.fill = GridBagConstraints.NONE;
    gridbag.setConstraints(comboPersonen, con);
    mainpanel.add(comboPersonen);
    comboPersonen.setPreferredSize(new Dimension(180, 30));
    comboPersonen.setRenderer(new NimbusCellRenderer<>());

    con.gridx = 0;
    con.gridy = 3;
    con.gridwidth = 3;
    con.insets = new Insets(0, 6, 0, 6);
    con.fill = GridBagConstraints.NONE;
    gridbag.setConstraints(knopfleiste, con);
    mainpanel.add(knopfleiste);

    con.gridx = 0;
    con.gridy = 4;
    con.gridwidth = 3;
    con.insets = new Insets(24, 6, 0, 6);
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(separator, con);
    mainpanel.add(separator);

    con.gridx = 2;
    con.gridy = 5;
    con.gridwidth = 1;
    con.insets = new Insets(12, 0, 0, 6);
    con.fill = GridBagConstraints.NONE;
    con.anchor = GridBagConstraints.EAST;
    gridbag.setConstraints(buttonSchliessen, con);
    mainpanel.add(buttonSchliessen);

    buttonSchliessen.addActionListener(buttonCloseListener);

    windowManager.setMinimumSize(new Dimension(0, 0));
    windowManager.addWMListener(new PropertyChangeListener() {
      @Override
      public void propertyChange(PropertyChangeEvent event) {
        changes.firePropertyChange(event);

      }
    });

    buttonRotaryListener = new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (!TelefonUtil.isMicro()) {
          JOptionPane.showMessageDialog(
              JAnrufenFrame.this, "<html>" + micofonNichtEingeschaltet.toString() + "</html>",
              telefonieren.toString(), JOptionPane.ERROR_MESSAGE
          );
          return;
        }
        changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.TELEFON_WAEHLEN);
      }
    };
    buttonHoererAbnehmenListener = new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (!TelefonUtil.isMicro()) {
          JOptionPane.showMessageDialog(
              JAnrufenFrame.this, "<html>" + micofonNichtEingeschaltet.toString() + "</html>",
              telefonieren.toString(), JOptionPane.ERROR_MESSAGE
          );
          return;
        }
        changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.TELEFON_ABNEHMEN);
      }
    };
    buttonHoererAuflegenListener = new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent event) {

        for (ActionListener listener : buttonSchliessen.getActionListeners()) {
          buttonSchliessen.removeActionListener(listener);
        }

        buttonSchliessen.addActionListener(buttonCloseListener);
        buttonEnabled(buttonSchliessen);

        buttonDisabled(knopfleiste.buttonHoererAuflegen);
        buttonDisabled(knopfleiste.buttonRotary);
        buttonDisabled(knopfleiste.buttonShare);
        buttonEnabled(knopfleiste.buttonHoererAbnehmen);

        knopfleiste.buttonHoererAbnehmen.addActionListener(buttonHoererAbnehmenListener);
        knopfleiste.buttonHoererAuflegen.removeActionListener(buttonHoererAuflegenListener);
        knopfleiste.buttonRotary.removeActionListener(buttonRotaryListener);
        knopfleiste.buttonShare.removeActionListener(buttonShareListener);
        changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.TELEFON_AUFLEGEN);
      }

    };
    buttonShareListener = new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent event) {
        changes.firePropertyChange(JAnrufenFrame.class.getName(), Control.NULL, Control.SHARING);
      }

    };

    setContentPane(splitpane);
    knopfleiste.starten();
  }



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



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



  void buttonDisabled(JButton button) {
    button.setForeground(WMResource.DISABLED_BUTTON_FOREGROUND);
    button.setBackground(WMResource.DISABLED_BUTTON_BACKGROUND);
  }



  void buttonEnabled(JButton button) {
    button.setForeground(WMResource.ENABLED_BUTTON_FOREGROUND);
    button.setBackground(WMResource.ENABLED_BUTTON_BACKGROUND);
  }



  /**
   * Enables the Share button after the remote party answers the call.
   */
  public void enableShareButton() {
    for (ActionListener listener : knopfleiste.buttonShare.getActionListeners()) {
      knopfleiste.buttonShare.removeActionListener(listener);
    }
    knopfleiste.buttonShare.addActionListener(buttonShareListener);
    buttonEnabled(knopfleiste.buttonShare);
  }



  /**
   * Diese Person soll angerufen werden.
   * 
   * @return eine Person
   */
  public String getNickname() {
    return currentNickname;
  }



  @Override
  public Frames getType() {
    return Frames.ANRUFEN;
  }



  /**
   * Die Lautsprecherstärke wird vom Slider gelesen.
   * 
   * 
   * @return die Lautstärke
   */
  public int getVolume() {
    return volumepanel.getLoudness();
  }



  public void hoererAbnehmen() {
    knopfleiste.hoererAbnehmen();
  }



  public void hoererAbnehmenEinschalten() {
    buttonEnabled(knopfleiste.buttonHoererAbnehmen);
  }



  /**
   * Deaktiviere die Knöpfe WÄHLEN, und AUFLEGEN.
   * 
   */
  public void hoererAuflegenEinschalten() {
    knopfleiste.hoererAuflegen();
  }



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



  public boolean isSetAnimated() {
    return animated;
  }



  public void removeAllListener() {
    for (PropertyChangeListener tmp : changes.getPropertyChangeListeners()) {
      removeAnrufenListener(tmp);
    }
    volumepanel.removeVolumeListener(anrufenListener);
  }



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



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



  @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);
  }



  public void setAnimated(boolean state) {
    animated = state;
    knopfleiste.buttonShare.setIcon(
        !animated ? new ImageIcon(getClass().getResource(Resource.DESKTOP_25x25))
            : new ImageIcon(getClass().getResource(Resource.DESKTOP_ANIMATED_25x25))
    );
  }



  @Override
  public void setBackgroundGradient(Color top, Color bottom) {
    windowManager.setBackgroundGradient(top, bottom);
  }



  @Override
  public void setLanguage(ISO639 code) {
    knopfleiste.buttonShare.setLanguage(code);
    knopfleiste.buttonRotary.setLanguage(code);
    knopfleiste.buttonHoererAbnehmen.setLanguage(code);
    knopfleiste.buttonHoererAuflegen.setLanguage(code);
    buttonSchliessen.setLanguage(code);
    labelPerson.setLanguage(code);
    volumepanel.setLanguage(code);
    micofonNichtEingeschaltet.setLanguage(code);
    telefonieren.setLanguage(code);
    setLanguage(KEY.TELEFON_ANRUFEN, code, extText); // Frame
    treeView(code);
  }



  /**
   * Updates the rotary button to indicate that the call is waiting to be
   * answered.
   *
   * The button remains red to show that another call cannot be started until the
   * current call is answered or terminated.
   */
  public void setRotaryWaiting() {

    knopfleiste.buttonHoererAbnehmen.removeActionListener(buttonHoererAbnehmenListener);
    knopfleiste.buttonRotary.removeActionListener(buttonRotaryListener);

    buttonDisabled(knopfleiste.buttonHoererAbnehmen);

    buttonEnabled(knopfleiste.buttonHoererAuflegen);
    buttonSchliessen.setBackground(Resource.JQUERY_RED);
    buttonSchliessen.setForeground(Color.WHITE);
    buttonSchliessen.removeActionListener(buttonCloseListener);

    buttonDisabled(knopfleiste.buttonRotary);

  }



  /**
   * Die Telefonliste wird aktualisiert.
   * 
   * @param online
   *               diese Anwender sind online
   */
  public void setUserOnlinelist(List<UserOnline> online) {
    for (ActionListener listener : comboPersonen.getActionListeners()) {
      comboPersonen.removeActionListener(listener);
    }
    comboPersonen.removeAllItems();
    comboPersonen.setRenderer(new NimbusCellRenderer<>());
    for (UserOnline person : online) {
      comboPersonen.addItem(person.getNickname());
    }

    if (online.size() == 0) {
      knopfleiste.noRecipients();
    }
    else if (online.size() == 1) {
      currentNickname = comboPersonen.getItemAt(0);
      knopfleiste.starten();
    }
    else {
      if (currentNickname == null) currentNickname = comboPersonen.getSelectedItem().toString();
      comboPersonen.setSelectedItem(currentNickname);
    }

    comboPersonen.addActionListener(event -> {
      currentNickname = (String) comboPersonen.getSelectedItem();
    });
  }



  /**
   * Die Lautsprecherstärke wird in der GUI eingestellt, nicht am realen
   * Lautsprecher.
   * 
   * @param volume
   */
  public void setVolume(int volume) {
    volumepanel.setLoudness(volume);
  }

}
