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

import com.github.lgooddatepicker.components.DatePicker;
import com.github.lgooddatepicker.components.DatePickerSettings;
import com.github.lgooddatepicker.components.DatePickerSettings.DateArea;
import com.github.lgooddatepicker.components.DateTimePicker;
import com.github.lgooddatepicker.components.TimePicker;
import com.github.lgooddatepicker.components.TimePickerSettings;
import com.github.lgooddatepicker.optionalusertools.DateTimeChangeListener;
import com.github.lgooddatepicker.optionalusertools.DateVetoPolicy;
import com.github.lgooddatepicker.optionalusertools.TimeVetoPolicy;
import com.github.lgooddatepicker.zinternaltools.DateTimeChangeEvent;
import com.privatejgoodies.forms.layout.ConstantSize;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nexuswob.util.Util;
import net.javacomm.client.resource.Resource;
import net.javacomm.multilingual.Babelfish;
import net.javacomm.multilingual.MultilingualButton;
import net.javacomm.multilingual.MultilingualLabel;
import net.javacomm.multilingual.schema.ISO639;
import net.javacomm.multilingual.schema.KEY;
import net.javacomm.window.manager.Control;
import net.javacomm.window.manager.WMResource;



public class Verfallsdatum extends JPanel implements Babelfish {

  private final static Logger log = LogManager.getLogger(Verfallsdatum.class);
  private static final long serialVersionUID = 5119672081770798320L;
  private final int pixels = 12;

  private GridBagLayout gridbag = new GridBagLayout();
  private GridBagConstraints con = new GridBagConstraints();
  private MultilingualLabel labelHeader = new MultilingualLabel(KEY.LABEL_VERFALLSDATUM);
  private MultilingualLabel labelVerfallsdatum = new MultilingualLabel(KEY.LABEL_ABLAUFDATUM);
  private DateTimePicker datetimepicker = new DateTimePicker();
  private DatePickerSettings datepickerSettings = new DatePickerSettings();
  private MultilingualButton confirmButton = new MultilingualButton(KEY.BUTTON_CONFIRM);
  private JButton timerButton;
  private JButton aprilButton;
  private DatePicker datepicker;
  private TimePicker timepicker;
  private TimePickerSettings timepickerSettings;
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  private Class<? extends Verfallsdatum> resource;
  private ActionListener confirmListener = new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
      changes.firePropertyChange(
          Verfallsdatum.class.getName(),
          ZonedDateTime.of(datetimepicker.getDateTimeStrict(), ZoneId.systemDefault()),
          Control.CONFIRM
      );
    }

  };


  /**
   * Die maximale Ablaufzeit beträgt 1-Monat.
   *
   * @author llange
   *
   */
  private class DateVetoPolicyImpl implements DateVetoPolicy {

    private LocalDate sperrdatum;

    public DateVetoPolicyImpl(LocalDate value) {
      sperrdatum = value;
    }



    @Override
    public boolean isDateAllowed(LocalDate date) {
      if (date.isBefore(sperrdatum)) {
        return false;
      }
      LocalDate a = sperrdatum.plusMonths(1);
      if (date.isAfter(a)) return false;
      return true;
    }
  }

  private class TimeVetoPoilicyImpl implements TimeVetoPolicy {

    private LocalDateTime sperrzeit;

    /**
     * Alles was nach der Sperrzeit liegt, wird angezeigt.
     *
     * @param value
     *              Sperrzeit
     */
    public TimeVetoPoilicyImpl(LocalDateTime value) {
      sperrzeit = value;
    }



    @Override
    public boolean isTimeAllowed(LocalTime time) {
      if (datepicker.getDate().isAfter(sperrzeit.toLocalDate())) return true;
      LocalDateTime help = LocalDateTime.of(sperrzeit.toLocalDate(), time);
      return sperrzeit.isBefore(help);
    }

  }




  public Verfallsdatum() {
    setLayout(gridbag);

    resource = getClass();

    con.gridx = 0;
    con.gridy = 0;
    con.gridwidth = 3;
    con.gridheight = 1;
    con.insets = new Insets(0, 0, 6, 0);
    con.fill = GridBagConstraints.HORIZONTAL;
    con.anchor = GridBagConstraints.CENTER;
    con.weightx = 1.0;
    con.weighty = 0.0;
    gridbag.setConstraints(labelHeader, con);
    add(labelHeader);

    con.gridx = 0;
    con.gridy++;
    con.gridwidth = 1;
    con.insets = new Insets(0, 0, 0, 6);
    con.fill = GridBagConstraints.NONE;
    con.anchor = GridBagConstraints.CENTER;
    con.weightx = 0.0;
    con.weighty = 0.0;
    gridbag.setConstraints(labelVerfallsdatum, con);
    add(labelVerfallsdatum);

    con.gridx++;
    con.gridwidth = 1;
    con.insets = new Insets(0, 0, 0, 0);
    con.fill = GridBagConstraints.NONE;
    con.anchor = GridBagConstraints.CENTER;
    con.weightx = 0.0;
    con.weighty = 0.0;
    gridbag.setConstraints(datetimepicker, con);
    add(datetimepicker);
    datepicker = datetimepicker.getDatePicker();
    datepicker.setSettings(datepickerSettings);

    timepicker = datetimepicker.getTimePicker();
    // timepicker.getSettings() unebedingt vor
    // datetimepicker.addDateTimeChangeListener setzen, damit es
    // nicht zu NullPointerException im Listener kommt.
    datepickerSettings.setAllowEmptyDates(false); // dies ist auch ein Auslöser, DateTimeListener

    timepickerSettings = timepicker.getSettings();
    timepickerSettings.setAllowEmptyTimes(false);
    timepickerSettings.setGapBeforeButtonPixels(pixels);

    datetimepicker.setGapSize(15, ConstantSize.PIXEL);
    datetimepicker.addDateTimeChangeListener(new DateTimeChangeListener() {

      @Override
      public void dateOrTimeChanged(DateTimeChangeEvent event) {
        LocalDateTime minimumDatum = Util.ahora(LocalDateTime.now());
        timepickerSettings.setVetoPolicy(new TimeVetoPoilicyImpl(minimumDatum));
        if (event.getNewDateTimeStrict().isBefore(minimumDatum)) {
          datetimepicker.setDateTimeStrict(minimumDatum.plusMinutes(30)); // in die Zukunft
        }
        else {
          datetimepicker.setDateTimeStrict(event.getNewDateTimeStrict());
          // Knopf aktivieren
          changes.firePropertyChange(
              Verfallsdatum.class.getName(),
              ZonedDateTime.of(datetimepicker.getDateTimeStrict(), ZoneId.systemDefault()),
              Control.EXPIRY_DATE
          );

        }
      }

    });


    datepickerSettings.setColor(DateArea.TextTodayLabel, Color.BLACK); // ok
    datepickerSettings.setColor(DateArea.TextCalendarPanelLabelsOnHover, Color.BLACK);
    datepickerSettings.setColor(DateArea.TextMonthAndYearNavigationButtons, Resource.JQUERY_ORANGE); // ok
    datepickerSettings.setColor(DateArea.TextMonthAndYearMenuLabels, Color.BLACK); // OK
    datepickerSettings.setGapBeforeButtonPixels(pixels);

    aprilButton = datepicker.getComponentToggleCalendarButton();
    aprilButton.setIcon(new ImageIcon(resource.getResource(Resource.DATETIME_25X25)));
    aprilButton.setText(new String());


    timerButton = timepicker.getComponentToggleTimeMenuButton();
    timerButton.setIcon(new ImageIcon(resource.getResource(Resource.POPUP_SCHRITTWEITE)));
    timerButton.setText(new String());


    datepickerSettings.setVisiblePreviousYearButton(false);
    datepickerSettings.setVisibleNextYearButton(false);
    datepickerSettings.setVisibleClearButton(false);

    con.gridx++;
    con.gridwidth = 1;
    con.insets = new Insets(0, 18, 0, 0);
    con.fill = GridBagConstraints.NONE;
    con.anchor = GridBagConstraints.WEST;
    con.weightx = 0.0;
    con.weighty = 0.0;
    gridbag.setConstraints(confirmButton, con);
    add(confirmButton);
    confirmButton.setForeground(WMResource.DISABLED_BUTTON_FOREGROUND);
    confirmButton.setBackground(WMResource.DISABLED_BUTTON_BACKGROUND);

  }



  /**
   * Das Ablaufdatum einer Konferenz wird übernommen. Das Ablaufdatum kommt aus
   * dem Backend.
   *
   * @param datetime
   *                 Datum und Uhrzeit
   */
  public void setVerfallsdatum(ZonedDateTime datetime) {
    datepickerSettings.setVetoPolicy(new DateVetoPolicyImpl(ZonedDateTime.now().toLocalDate()));
    datetimepicker.setDateTimeStrict(datetime.toLocalDateTime()); // ausgelöst DateTimeListener
  }



  /**
   * Die Knöpfe für das Datum und die Zeit werden dekativiert. Dies ist der Fall,
   * wenn es keine Konferenzen gibt.
   *
   */
  public void setDisableVerfallsdatum() {
    datepicker.setEnabled(false);
    timepicker.setEnabled(false);
  }



  /**
   * Der Knopf wird deaktiviert oder aktiviert.
   *
   * @param disabled
   *                 {@code true}, der Knopf ist deaktiviert und rot eingefärbt
   */
  public void setDisabledConfirmButton(boolean disabled) {
    if (disabled) {
      confirmButton.setForeground(WMResource.DISABLED_BUTTON_FOREGROUND);
      confirmButton.setBackground(WMResource.DISABLED_BUTTON_BACKGROUND);
      confirmButton.removeActionListener(confirmListener);
    }
    else {
      confirmButton.setForeground(WMResource.ENABLED_BUTTON_FOREGROUND);
      confirmButton.setBackground(WMResource.ENABLED_BUTTON_BACKGROUND);
      if (confirmButton.getActionListeners().length == 0) confirmButton.addActionListener(confirmListener);
    }
  }



  /**
   * Andere Objekte können sich registrieren, um eine Datumsänderung abhören.
   *
   * @param listener
   *                 ein Listener zum Abhören
   */
  public synchronized void addVerfallsdatumListener(PropertyChangeListener listener) {
    changes.addPropertyChangeListener(listener);
  }



  /**
   * Ein Objekt möchte keine Änderungen mehr empfangen.
   *
   * @param listener
   *                 ein Listener zum Abhören
   */
  public synchronized void removeVerfallsdatumListener(PropertyChangeListener listener) {
    changes.removePropertyChangeListener(listener);
  }



  /**
   * Entferne alle Listener auf einen Schlag.
   *
   */
  public synchronized void removeAllListener() {
    for (PropertyChangeListener listener : changes.getPropertyChangeListeners()) {
      removeVerfallsdatumListener(listener);
    }

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

  }



  @Override
  public void setLanguage(ISO639 code) {
    labelVerfallsdatum.setLanguage(code);
    confirmButton.setLanguage(code);
    confirmButton.setMnemonic(-1);
    labelHeader.setLanguage(code);
  }

}
