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

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
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.text.DecimalFormat;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.plaf.synth.SynthFormattedTextFieldUI;
import javax.swing.text.JTextComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.javacomm.client.config.schema.Sorte;
import net.javacomm.client.gui.MultilingualServiceButton;
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.WindowAnrufenEvent;
import net.javacomm.window.manager.WindowAnrufenListener;
import net.javacomm.window.manager.WindowManagerInternalFrame;



/**
 * In der Menubar wird Bildschirmfreigabe > Teilen aufgerufen.
 */
public class TeilenInternalFrame extends WindowManagerInternalFrame implements WindowAnrufenListener {

  private final static Logger log = LogManager.getLogger(TeilenInternalFrame.class);
  private static final long serialVersionUID = -2992619214506326740L;
  public final static int SCREEN_WIDTH = 750;
  public final static int SCREEN_HEIGHT = 330;
  private ImageIcon image;
  private GridBagLayout gridbag = new GridBagLayout();
  private GridBagConstraints con = new GridBagConstraints();
  private MultilingualLabel labelAnWen = new MultilingualLabel(KEY.LABEL_AN_WEN_SOLL_DER);
  private JSeparator separator = new JSeparator();
  private JComboBox<String> benutzerliste = new JComboBox<>();
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private InternalFrameAdapter internalFrameAdapter = new InternalFrameAdapter() {
    @Override
    public void internalFrameClosing(InternalFrameEvent e) {
      changes.firePropertyChange(TeilenInternalFrame.class.getName(), Control.NULL, Control.CLOSE);
    }

  };
  private JSplitPane splitpaneHorizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  private JScrollPane leftpanel = new JScrollPane();
  private JPanel setuppanel = new JPanel();

  private MultilingualServiceButton serviceButton = new MultilingualServiceButton(
      KEY.BUTTON_STARTEN, KEY.BUTTON_ABBRECHEN
  );
  private MultilingualButton buttonClose = new MultilingualButton(KEY.BUTTON_SCHLIESSEN);
  private MultilingualLabel labelIntro = new MultilingualLabel(KEY.LABEL_BILDSCHIRMDATEN_AUFZEICHNEN);
  private JSpinner spinner = new JSpinner();
  private ImageIcon stopImage;
  private ImageIcon startImage;
  private MultilingualLabel labelStatus = new MultilingualLabel(KEY.LABEL_SERVERSTATUS);
  private JLabel labelBilderProSekunde = new JLabel();
  private final MultilingualString multiFps = new MultilingualString(KEY.LABEL_UEBERTRAGUNGSRATE);
//  DecimalFormat format;
  private final DecimalFormat df = new DecimalFormat("#0.00");
  private PropertyChangeListener sendListener;

  public TeilenInternalFrame() {
    Class<? extends TeilenInternalFrame> resource = getClass();
    stopImage = new ImageIcon(resource.getResource(Resource.SCREENLOCK_25x26));
    startImage = new ImageIcon(resource.getResource(Resource.SCREENOPEN_25x26));

    setMaximizable(false);
    setResizable(true);
    setIconifiable(true);
    setClosable(true);
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    URL url = getClass().getResource(Resource.SHARE_36x26);
    image = new ImageIcon(url);
    setFrameIcon(image);
    setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    addInternalFrameListener(internalFrameAdapter);
    sendListener = new PropertyChangeListener() {

      @Override
      public void propertyChange(PropertyChangeEvent event) {
        Double emaFps = (Double) event.getNewValue();
        clearFps(emaFps);
      }

    };
    init();
  }



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



  public synchronized void addTeilenFrameListener(PropertyChangeListener listener) {
    changes.addPropertyChangeListener(listener);
  }



  /**
   * Die Anzahl der Bilder pro Sekunde werden in FPS angezeigt.
   * 
   * @param value
   *              Frames pro Sekunde
   */
  public void clearFps(double value) {
    String text = multiFps.toString().replace("XXX", df.format(value));
    labelBilderProSekunde.setText(text);
  }



  @Override
  public Dimension getMinimumSize() {
    return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
  }



  /**
   * An diesen Benutzer soll gesendet werden.
   * 
   * @return der ausgewählte Benutzernickname oder {@code null}, wenn es keinen
   *         Benutzer gibt
   */
  public String getNickname() {
    return (String) benutzerliste.getSelectedItem();
  }



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



  private void init() {
    setContentPane(splitpaneHorizontal);

    leftpanel.setViewportView(setuppanel);
    leftpanel.getVerticalScrollBar().setUnitIncrement(12);
    leftpanel.getHorizontalScrollBar().setUnitIncrement(12);
    leftpanel.setBorder(null);
    splitpaneHorizontal.setLeftComponent(leftpanel);
    splitpaneHorizontal.setRightComponent(windowManager);
    splitpaneHorizontal.setDividerLocation(SCREEN_WIDTH - WindowManagerInternalFrame.SPLITWIDTH);
    splitpaneHorizontal.setOneTouchExpandable(true);
    windowManager.setMinimumSize(new Dimension(0, 0));
    windowManager.addWMListener(new PropertyChangeListener() {
      @Override
      public void propertyChange(PropertyChangeEvent event) {
        changes.firePropertyChange(event);
      }
    });

    setuppanel.setLayout(gridbag);

    // 1. Reihe

    con.gridx = 0;
    con.gridy++;
    con.gridwidth = 2;
    con.fill = GridBagConstraints.NONE;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(12, 0, 0, 0);
    con.anchor = GridBagConstraints.WEST;
    gridbag.setConstraints(labelAnWen, con);
    setuppanel.add(labelAnWen);

    // 2. Reihe
    con.gridx = 0;
    con.gridy++;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.NONE;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(12, 0, 0, 24);
    con.anchor = GridBagConstraints.WEST;
    gridbag.setConstraints(benutzerliste, con);
    setuppanel.add(benutzerliste);
    benutzerliste.setMaximumRowCount(7);
    benutzerliste.setMinimumSize(new Dimension(90, 30));
    benutzerliste.setPreferredSize(new Dimension(180, 30));

    con.gridx = 1;
//    con.gridy++;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.NONE;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(12, 0, 0, 0);
    con.anchor = GridBagConstraints.WEST;
    gridbag.setConstraints(serviceButton, con);
    setuppanel.add(serviceButton);
    serviceButton.addButtonListener(event -> {
      Control value = (Control) event.getNewValue();
      switch(value) {
        case BUTTON_1:
          changes.firePropertyChange(TeilenInternalFrame.class.getName(), Control.NULL, Control.START);
          break;
        case BUTTON_2:
          changes.firePropertyChange(TeilenInternalFrame.class.getName(), Control.NULL, Control.CANCEL);
          break;
        default:
          break;

      }
    });

    // 3. Reihe
    con.gridx = 0;
    con.gridy++;
    con.gridwidth = 2;
    con.fill = GridBagConstraints.NONE;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(12, 0, 0, 0);
    con.anchor = GridBagConstraints.WEST;
    gridbag.setConstraints(labelBilderProSekunde, con);
    setuppanel.add(labelBilderProSekunde);

    // 4. Reihe
    con.gridx = 0;
    con.gridy++;
    con.gridwidth = 2;
    con.fill = GridBagConstraints.HORIZONTAL;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(24, 0, 0, 0);
    con.anchor = GridBagConstraints.CENTER;
    gridbag.setConstraints(separator, con);
    setuppanel.add(separator);

    // 5. Reihe
    con.gridx = 1;
    con.gridy++;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.NONE;
    con.weightx = 0.0;
    con.weighty = 0.0;
    con.insets = new Insets(12, 0, 0, 0);
    con.anchor = GridBagConstraints.EAST;
    gridbag.setConstraints(buttonClose, con);
    setuppanel.add(buttonClose);
    buttonClose.addActionListener(event -> {
      try {
        setClosed(true);
      }
      catch (PropertyVetoException e) {}
    });
    ImageCaster.getInstance().addSendListener(sendListener);
  }



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



  /**
   * Entferne alle Listener aus TeilenInternalFrame.
   */
  public void removeAllListener() {
    for (PropertyChangeListener listener : changes.getPropertyChangeListeners()) {
      removeScreensaverListener(listener);
    }
    windowManager.removeAllListener();
    serviceButton.removeAllListener();
    for (ActionListener al : buttonClose.getActionListeners()) {
      buttonClose.removeActionListener(al);
    }
    ImageCaster.getInstance().removeSendListener(sendListener);

  }



  public synchronized void removeScreensaverListener(PropertyChangeListener listener) {
    changes.removePropertyChangeListener(listener);
  }



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



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



  /**
   * Die Hintergrundfarbe für den Spinner.
   * 
   * @param sorte
   *              die Hintergrundfarbe
   */
  public void setEissorte(Sorte sorte) {
    Color color;
    switch(sorte) {
      case BLAUBEERE:
        color = Resource.JQUERY_HELLBLAU;
        break;
      case ERDBEERE:
        color = Resource.JQUERY_ERDBEERE;
        break;
      case JOGHURT:
        color = Color.WHITE;
        break;
      case MOKKA:
        color = Resource.JQUERY_MOKKA;
        break;
      case VANILLE:
        color = Resource.JQUERY_VANILLE;
        break;
      case ZITRONE:
        color = Resource.JQUERY_LEMON;
        break;
      default:
        color = Color.WHITE;
        break;
    }

    final JComponent editor = spinner.getEditor();
    int c = editor.getComponentCount();
    for (int i = 0; i < c; i++) {
      final Component comp = editor.getComponent(i);
      if (comp instanceof JTextComponent) {
        ((JTextComponent) comp).setUI(new SynthFormattedTextFieldUI() {
          @Override
          protected void paint(javax.swing.plaf.synth.SynthContext context, java.awt.Graphics g) {
            g.setColor(color);
            g.fillRect(3, 3, getComponent().getWidth() - 3, getComponent().getHeight() - 6);
            super.paint(context, g);
          }
        });
      }
    }

  }



  @Override
  public void setFrameTitleId(String id) {
    setTitle(id);
  }



  @Override
  public void setLanguage(ISO639 code) {
    buttonClose.setLanguage(code);
    serviceButton.setLanguage(KEY.BUTTON_STARTEN, KEY.BUTTON_ABBRECHEN, code);
    serviceButton.setMnemonic(-1, -1);
    labelAnWen.setLanguage(code);
    multiFps.setLanguage(code);
    clearFps(0d);
    labelIntro.setLanguage(code);
    labelStatus.setLanguage(code);
    setLanguage(KEY.FRAME_BILDSCHIRMEMPFANG, code); // der neue Name wird gesetzt
    treeView(code);
  }



  @Override
  public void setType(Frames value) {
    log.warn("Der Typ wurde mit Frames.VIDEOREKORDER statisch vorgegeben.");
  }



  /**
   * Eine Benutzerliste wird übergeben. Die Benutzerliste enthält Nicknames. Nach
   * dem Aufruf enthält die Combobox nur die Nicknames aus der übergebenen Liste.
   * Falls die Benutzerliste leer ist, wird der STARTEN - Knopf rot eingefärbt.
   * 
   * 
   * @param liste
   *              eine Liste von Nicknames
   */
  public void setUserOnlinelist(List<UserOnline> liste, String userid) {
    benutzerliste.removeAllItems();
    liste.forEach(online -> {
      benutzerliste.addItem(online.getNickname());
    });
    benutzerliste.addItem("Test");

  }



  /**
   * Der Startbutton wird deaktiviert
   * 
   * @param value
   *              true, der Button wird rot und deaktiviert
   */
  public void startStop(boolean value) {
    if (value) {
      labelStatus.setIcon(startImage);
      serviceButton.setDisabledButton1();
    }
    else {
      labelStatus.setIcon(stopImage);
      serviceButton.setEnabledButton1();
    }
  }

}
