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

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nexuswob.gui.swing.NimbusCellRenderer;
import net.javacomm.client.config.schema.Sorte;
import net.javacomm.client.resource.Resource;
import net.javacomm.multilingual.Babelfish;
import net.javacomm.multilingual.MultilingualButton;
import net.javacomm.multilingual.MultilingualDialog;
import net.javacomm.multilingual.MultilingualLabel;
import net.javacomm.multilingual.schema.ISO639;
import net.javacomm.multilingual.schema.KEY;
import net.javacomm.window.manager.Control;



/**
 * JLogon ist ein MultilingualDialog.
 */
public class JLogon extends MultilingualDialog implements Babelfish {

  private static final long serialVersionUID = 3394092024360327599L;
  private final static Logger log = LogManager.getLogger(MultilingualDialog.class);

  /**
   * Für das automatische Update eines nichtregistrierten Users
   */
  final static String UID = "";
  /**
   * Für das automatische Update eines nichtregistrierten Users
   */
  final static String PASSWORD = "";

  public final static int SECOND = 90;
  public final static int x = 480;
  public final static int y = 550;
  private STATUS status;
  private Frame parent;
  private BorderLayout border = new BorderLayout(0, 0);
  private JPanel panSouth = new JPanel(new FlowLayout(FlowLayout.CENTER, 12, 12));
  private JPanel panNorth = new JPanel(new FlowLayout(FlowLayout.CENTER, 12, 12));
  private MultilingualButton buttonAbbrechen = new MultilingualButton(KEY.BUTTON_ABBRECHEN);
  private MultilingualButton buttonOk = new MultilingualButton(KEY.BUTTON_ANMELDEN);
  private JPanel mainPanel = new JPanel();
  private JPanel panCenter = new JPanel();
  private GridBagLayout gridbag = new GridBagLayout();
  private GridBagConstraints con = new GridBagConstraints();

  private MultilingualLabel labelSprache = new MultilingualLabel();
  private Map<ISO639, Icon> mapSprache = new HashMap<>();
  private JComboBox<ISO639> comboSprache = new JComboBox<>(new ISO639[] {ISO639.DE, ISO639.EN, ISO639.ES});
  private MultilingualLabel labelDomain = new MultilingualLabel();
  private MultilingualLabel labelUserID = new MultilingualLabel();
  private MultilingualLabel labelPassword = new MultilingualLabel();
  private MultilingualLabel labelTimeout = new MultilingualLabel();
  private JLabel labelTime = new JLabel(String.valueOf(SECOND));
  // private JLabel labelRSA = new JLabel ("Keep your online communication
  private MultilingualLabel labelRSA = new MultilingualLabel();

  private JTextField textFieldUser = new JTextField();
  private JPasswordField password = new JPasswordField();
  private JPanel panButton = new JPanel(new FlowLayout(FlowLayout.CENTER, 48, 0));
  private ISO639 language;

  private String username = null;
  private int second = SECOND;
  private KeyAction keyAction = new KeyAction();
  private JComboBox<String> comboboxDomain = new JComboBox<>();
  private Class<? extends JLogon> resource;
  private ImageIcon icon;
  private JLabel labelDuke;
  private Timer swingtimer;
  private Color background;
  private ListCellRenderer<? super ISO639> sprachRenderer = new DefaultListCellRenderer() {
    private static final long serialVersionUID = 4752522651793422756L;

    @Override
    public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
        boolean cellHasFocus) {

      JLabel label = (JLabel) super.getListCellRendererComponent(
          list, value, index, isSelected, cellHasFocus
      );
      switch((ISO639) value) {
        case DE:
          label.setText("Deutsch");
          break;
        case EN:
          label.setText("English");
          break;
        case ES:
          label.setText("Español");
          break;
        case FR:
          break;
        case IT:
          break;
        case PT:
          break;
        case RU:
          break;
        case TR:
          break;
        default:
          break;

      }
      // Get icon to use for the list item value
      Icon icon = mapSprache.get(value);
      // Set icon to display for value
      label.setIcon(icon);
      return label;
    }
  };
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private ActionListener sprachActionListener = new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
      changes
          .firePropertyChange(JLogon.class.getName(), Control.NULL, (ISO639) comboSprache.getSelectedItem());
    }

  };
  private WindowAdapter adapter = new WindowAdapter() {
    @Override
    public void windowOpened(java.awt.event.WindowEvent e) {
      textFieldUser.requestFocusInWindow();
      textFieldUser.setCaretPosition(0);
    }
  };

  /**
   * Der Anwender meldet sich über diesen Dialog an.
   *
   * @param parent
   *                    der Dialog ist an diesen Frame gebunden
   * @param title
   *                    die Überschrift in der Dialogtitelleiste
   * @param letzeDomain
   *                    die letzte Anmeldung lief über diese Domane; die Domäne
   *                    darf {@code null} sein
   * @param domains
   *                    über eine dieser Domänen erfolgt die aktuelle Anmeldung
   * @param eissorte
   *                    die Farbgebung in den Dialogfeldern
   * @param password
   *                    das Anwenderpasswort
   * @param email
   *                    EMail-Adresse vom Anwender
   * @param userid
   *                    UserID vom Anwender
   */
  public JLogon(Frame parent, String title, String letzeDomain, List<String> domains, Sorte eissorte, String password, String email, String userid) {
    super(parent, title, true);
    this.parent = parent;
    resource = getClass();
    URL url = resource.getResource(Resource.DUKEPLUG);
    icon = new ImageIcon(url);
    labelDuke = new JLabel(icon);
    this.password.setText(password);
    setUserid(userid != null ? userid : email);
    mapSprache.put(ISO639.DE, new ImageIcon(resource.getResource(Resource.DEUTSCH_39x26)));
    mapSprache.put(ISO639.EN, new ImageIcon(resource.getResource(Resource.ENGLISH_39x26)));
    mapSprache.put(ISO639.ES, new ImageIcon(resource.getResource(Resource.SPAIN_39x26)));
    switch(eissorte) {
      case BLAUBEERE:
        background = Resource.JQUERY_HELLBLAU;
        break;
      case JOGHURT:
        background = Color.WHITE;
        break;
      case MOKKA:
        background = Resource.JQUERY_MOKKA;
        break;
      case VANILLE:
        background = new Color(255, 249, 185);
        break;
      case ERDBEERE:
        background = new Color(255, 157, 179);
        break;
      case ZITRONE:
        background = Resource.JQUERY_LEMON;
        break;
      default:
        log.warn(eissorte.toString() + " - diese Eissorte wird nicht unterstützt");
        break;
    }
    setDomains(domains);
    wasLastDomain(letzeDomain);
  }



  private void check() {
    /* Validieren */

    boolean isGuest = (username.equals(UID) || username.equals("nexus")) && getPassword().equals(PASSWORD);
    if (isGuest) {
      status = STATUS.GUEST;
    }
    else {
      status = STATUS.CONNECTED;
    }
    setClose();
  }



  private void confirm() {
    username = textFieldUser.getText();
    check();
  }



  public final String getPassword() {
    return String.valueOf(password.getPassword());
  }



  public STATUS getStatus() {
    return status;
  }



  public String getUrl() {
    return comboboxDomain.getSelectedItem() == null ? new String()
        : (String) comboboxDomain.getSelectedItem();
  }



  /**
   * Der Benutzername ist die Userid oder die EMailadresse
   * 
   * @return diese Kennung
   */
  public String getUsername() {
    return username;
  }



  /**
   * Die Methode muss nach JLogon aufgerufen werden.
   */
  public void init() {
    setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

    setContentPane(mainPanel);
    mainPanel.setLayout(border);
    panCenter.setLayout(gridbag);

    con.gridx = 0;
    con.gridy = 0;
    con.insets = new Insets(0, 12, 12, 0);
    con.weighty = 1.0;
    con.gridwidth = 2;
    gridbag.setConstraints(labelDuke, con);
    panCenter.add(labelDuke);

    con.weightx = 0.0;
    con.gridx = 0;
    con.gridy = 1;
    con.gridwidth = 1;
    con.anchor = GridBagConstraints.WEST;
    con.insets = new Insets(12, 12, 0, 12);
    gridbag.setConstraints(labelSprache, con);
    panCenter.add(labelSprache);

    con.weightx = 1.0;
    con.gridx = 1;
    con.gridy = 1;
    con.gridwidth = 1;
    con.insets = new Insets(12, 12, 0, 12);
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(comboSprache, con);
    panCenter.add(comboSprache);
    comboSprache.setRenderer(new NimbusCellRenderer<ISO639>());
    comboSprache.setRenderer(sprachRenderer);
    comboSprache.addActionListener(sprachActionListener);

//
    con.weightx = 0.0;
    con.gridx = 0;
    con.gridy = 2;
    con.gridwidth = 1;
    con.anchor = GridBagConstraints.WEST;
    con.insets = new Insets(12, 12, 0, 12);
    gridbag.setConstraints(labelDomain, con);
    panCenter.add(labelDomain);

    con.weightx = 1.0;
    con.gridx = 1;
    con.gridy = 2;
    con.gridwidth = 1;
    con.insets = new Insets(12, 12, 0, 12);
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(comboboxDomain, con);
    panCenter.add(comboboxDomain);
    comboboxDomain.setRenderer(new NimbusCellRenderer<String>());
//
    con.weightx = 0.0;
    con.gridx = 0;
    con.gridy = 3;
    con.gridwidth = 1;
    con.anchor = GridBagConstraints.WEST;
    con.insets = new Insets(12, 12, 0, 12);
    gridbag.setConstraints(labelUserID, con);
    panCenter.add(labelUserID);

    textFieldUser.setText(username);
    textFieldUser.selectAll();
    textFieldUser.addKeyListener(keyAction);
    con.insets = new Insets(12, 12, 0, 12);
    con.weightx = 1.0;
    con.gridx = 1;
    con.gridy = 3;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(textFieldUser, con);
    panCenter.add(textFieldUser);
    textFieldUser.setBackground(background);
//
    con.weightx = 0.0;
    con.insets = new Insets(12, 12, 0, 12);
    con.gridx = 0;
    con.gridy = 4;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.NONE;
    gridbag.setConstraints(labelPassword, con);
    panCenter.add(labelPassword);

    con.weightx = 1.0;
    con.insets = new Insets(12, 12, 0, 12);
    con.gridx = 1;
    con.gridy = 4;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.HORIZONTAL;
    gridbag.setConstraints(password, con);
    panCenter.add(password);
    password.addKeyListener(keyAction);
    password.setBackground(background);
//
    con.weightx = 0.0;
    labelRSA.setIcon(new ImageIcon(resource.getResource(Resource.CRYPTO_32x32)));
    labelRSA.setHorizontalTextPosition(SwingConstants.LEFT);
    labelRSA.setIconTextGap(24);
    con.insets = new Insets(12, 12, 0, 12);
    con.gridx = 0;
    con.gridy = 5;
    con.gridwidth = 1;
    con.fill = GridBagConstraints.NONE;
    con.gridwidth = 2;
    con.anchor = GridBagConstraints.CENTER;
    gridbag.setConstraints(labelRSA, con);
    panCenter.add(labelRSA);
//
    buttonOk.setKey(KEY.BUTTON_ANMELDEN);
    buttonOk.setIcon(new ImageIcon(resource.getResource(Resource.GREENGO_50x23)));
    buttonOk.addActionListener((event) -> {
      confirm();
    });
    panButton.add(buttonOk);

    buttonAbbrechen.setIcon(new ImageIcon(resource.getResource(Resource.DISMISS_25X25)));
    buttonAbbrechen.addActionListener((event) -> {
      status = STATUS.DISMISSED;
      setClose();
    });
    panButton.add(buttonAbbrechen);
    panSouth.add(panButton);
    panNorth.add(labelTimeout);
    panNorth.add(labelTime);
    mainPanel.add("North", panNorth);
    mainPanel.add("Center", panCenter);
    mainPanel.add("South", panSouth);
    setBounds(
        parent.getBounds().x + (parent.getBounds().width - x) / 2,
        parent.getBounds().y + (parent.getBounds().height - y) / 2, x, y
    );

    swingtimer = new Timer(1000, new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        if (second > 0) {
          labelTime.setText(String.valueOf(second--));
        }
        else {
          status = STATUS.ELAPSED;
          setClose();
        }
      }
    });
    swingtimer.start();
    addWindowListener(adapter);

  }



  /**
   * Das Logonfenster wird geschlossen. Alle Listener werden entfernt.
   *
   */
  public void setClose() {
    swingtimer.stop();
    for (ActionListener listener : swingtimer.getActionListeners()) {
      swingtimer.removeActionListener(listener);
    }
    this.removeAllListener();
    dispose();
  }



  /**
   * Die Domänenauswahl im Login-Fenster wird erstellt.
   *
   *
   * @param domains
   *                die verfügbaren Domänen
   */
  public void setDomains(List<String> domains) {
    comboboxDomain.removeAllItems();
    if (domains == null) {
      return;
    }
    for (String domain : domains) {
      comboboxDomain.addItem(domain);
    }
  }



  public final void setPassword(String text) {
    password.setText(text);
  }



  public void setUserid(String value) {
    username = value;
  }



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



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



  public void removeAllListener() {
    for (ActionListener l : buttonOk.getActionListeners()) {
      buttonOk.removeActionListener(l);
    }
    comboSprache.removeActionListener(sprachActionListener);
    password.removeKeyListener(keyAction);
    for (PropertyChangeListener tmp : changes.getPropertyChangeListeners()) {
      removeLanguageListener(tmp);
    }
    removeWindowListener(adapter);
  }



  /**
   * Die letzte Anmeldung lief über diese Domäne. Wenn die alte Domäne immer noch
   * die aktuelle ist, wird sie in der ComboBox ausgewählt.
   *
   *
   *
   * @param value
   *              eine Domäne
   * @return {@code true}, wenn die übergebene Domäne in der Domänenliste immer
   *         noch vorhanden ist
   */
  public boolean wasLastDomain(String value) {
    if (value == null) return false;
    boolean found = false;
    for (int index = 0; index < comboboxDomain.getItemCount(); index++) {
      found = found || comboboxDomain.getItemAt(index).equals(value);
    }
    if (found) comboboxDomain.setSelectedItem(value);
    return found;
  }

  /*****************************************************************************/
  /*                                                                           */
  /* Innerclass KeyAction */
  /*                                                                           */
  /*****************************************************************************/

  class KeyAction extends KeyAdapter {

    @Override
    public void keyReleased(KeyEvent event) {
      Object obj;

      obj = event.getSource();
      if (obj.equals(textFieldUser)) {
        if (event.getKeyCode() == KeyEvent.VK_ENTER) {
          textFieldUser.transferFocus();
        }
      }
      else if (obj.equals(password)) {
        if (event.getKeyCode() == KeyEvent.VK_ENTER) {
          confirm();
        }
      }
    }
  }

  /**
   * Nach dem Logon ist einer der vier Status {@link #DISMISSED},
   * {@link #ELAPSED}, {@link #GUEST}, {@link #CONNECTED},
   * {@link #WITHOUT_REGISTRATION} aktiv.
   */
  public enum STATUS {
    /**
     * Die Anmeldung wurde abgebrochen
     */
    DISMISSED,
    /**
     * Die Anmeldezeit ist abgelaufen
     */
    ELAPSED,
    /**
     * Der Anwender ist unbekannt und wird als Gast eingestuft
     */
    GUEST,
    /**
     * Der Anwender wird vorläufig als regulärer Anwender eingestuft
     */
    CONNECTED,
    /**
     * Der Anwender wurde nicht registriert
     */
    WITHOUT_REGISTRATION
  }

  /**
   * Die aktuelle Spreche im Logon-Fenster.
   * 
   * @return die Sprache in der Benutzeroberfläche
   */
  public ISO639 getLanguage() {
    return language;
  }



  @Override
  public void setLanguage(ISO639 code) {
    language = code;
    buttonOk.setLanguage(KEY.BUTTON_ANMELDEN, code);
    buttonAbbrechen.setLanguage(KEY.BUTTON_ABBRECHEN, code);
    labelDomain.setLanguage(KEY.LABEL_DOMAENE, code);
    labelSprache.setLanguage(KEY.LABEL_SPRACHE, code);
    labelPassword.setLanguage(KEY.LABEL_PASSWORT, code);
    labelUserID.setLanguage(KEY.LABEL_EMAIL, code);
    labelTimeout.setLanguage(KEY.LABEL_TIMEOUT, code);
    labelRSA.setLanguage(KEY.LABEL_RSA, code);
    comboSprache.setSelectedItem(code);
    super.setLanguage(code);
  }

}
