package nxm.ucl.libg;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/** A class that is used by Date Picker for month selection
    @author crs
 */
@SuppressWarnings({"rawtypes","unchecked"}) // <-- NOTE: JDK7 has some unchecked & rawtypes Xlint warnings that CANNOT be fixed since we still support JDK6
public class MJMonthPicker extends JPanel implements ItemListener, ChangeListener {
  /** NOTE: this should be changed if the structure/fields of this class changes. */
  private static final long serialVersionUID = 2012122806331L; // for Serializable interface

  /** The Month value for the selected month */
  private int month;
  /** The previous Month value on the spinner when the selected Month has been changed */
  private int oldSpinnerValue;

  /** GDatePicker Year Object */
  MJYearPicker  yearPicker;
  /** GDatePicker Day Object */
  MJDayPicker dayPicker;

  /** Date Variables */
  private Locale locale;

  /** Combo Box Swing Component for Month Selection */
  private JComboBox comboBox;
  /** Spinner Swing Component for Month Selection */
  private JSpinner spinner;

  /** Local initialization used when initializing the names of the months */
  private boolean localInitialize;
  /** Flag for whether or not MJMonthPicker is initialized */
  private boolean initialized;

  /**
   * Default constructor.
   */
  public MJMonthPicker () {
    setName("MJMonthPicker");

    setLayout(new BorderLayout());

    comboBox = new JComboBox();
    comboBox.addItemListener(this);

    // Get the default locale
    locale = Locale.getDefault();

    // Initialize the months
    initNames();

    // Create a new JSpinner to allow interaction with the month selection
    spinner = new JSpinner() {
      private static final long serialVersionUID = 1L;

      private JTextField textField = new JTextField();

      public Dimension getPreferredSize () {
        Dimension size = super.getPreferredSize();
        return new Dimension(size.width, textField.getPreferredSize().height);
      }
    };

    spinner.addChangeListener(this);
    spinner.setForeground(super.getForeground());
    spinner.setBackground(super.getBackground());
    spinner.setBorder(BorderFactory.createEmptyBorder());
    spinner.setEditor(comboBox);
    comboBox.setLightWeightPopupEnabled(false);
    comboBox.setForeground(super.getForeground());
    comboBox.setBackground(super.getBackground());
    comboBox.setBorder(BorderFactory.createEmptyBorder());

    // Add the spinner to the JPanel
    add(spinner, BorderLayout.WEST);

    initialized = true;
    setMonth(Calendar.getInstance().get(Calendar.MONTH));

    updateUI();
  }

  /**
   * Initializes the months to be displayed in the combo box.
   */
  private void initNames () {
    localInitialize = true;

    DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(locale);
    String[] monthNames = dateFormatSymbols.getMonths();

    if (comboBox.getItemCount() == 12) {
      comboBox.removeAllItems();
    }

    for (int i = 0; i < 12; i++) {
      comboBox.addItem(monthNames[i]);
    }

    localInitialize = false;
    comboBox.setSelectedIndex(month);
  }

  // ** Swing Event Methods **

  /**
   * Increase/Decrease the Month value and fire an event.
   */
  @Override
  public void stateChanged (ChangeEvent e) {

    SpinnerNumberModel model = (SpinnerNumberModel) ((JSpinner)
            e.getSource()).getModel();

    int value = model.getNumber().intValue();
    boolean increase = (value > oldSpinnerValue) ? true : false;
    oldSpinnerValue = value;

    int month = getMonth();

    // Month increases
    if (increase) {
      month += 1;

      if (month == 12) {
        month = 0;
      }
    }
    // Month decreases
    else {
      month -= 1;

      if (month == -1) {
        month = 11;
      }
    }

    // Set the new month value
    setMonth(month);
  }

  @Override
  public void itemStateChanged (ItemEvent e) {
    if (e.getStateChange() == ItemEvent.SELECTED) {
      int index = comboBox.getSelectedIndex();

      if ((index >= 0) && (index != month)) {
        setMonth(index, false);
      }
    }
  }

  private void setMonth (int newMonth, boolean select) {
    if (!initialized || localInitialize) {
      return;
    }

    int oldMonth = month;
    month = newMonth;

    if (select)
      comboBox.setSelectedIndex(month);

    if (dayPicker != null) {
      dayPicker.setMonth(month);
    }
    firePropertyChange("month", oldMonth, month);
  }

  /**
   * Sets the month. This is a bound property. Values are valid between 0 (January) and 11 (December).
   * A {@code value < 0} will be treated as 0, a {@code value > 11} will be treated as 11.
   * @param newMonth the new month value
   */
  public void setMonth (int newMonth) {
    if (newMonth < 0 || newMonth == Integer.MIN_VALUE) {
      setMonth(0, true);
    } else if (newMonth > 11) {
      setMonth(11, true);
    } else {
      setMonth(newMonth, true);
    }
  }

  /** Get the month
   *  @return the month value
   */
  public int getMonth () {
    return month;
  }

  // ** Purely Graphical Methods. **

  @Override
  public void setFont (Font font) {
    if (comboBox != null) {
      comboBox.setFont(font);
    }
    super.setFont(font);
  }

  public void updateUI () {

    if (spinner != null) {
      spinner.setBorder(new EmptyBorder(0, 0, 0, 0));
    }
  }

  // ** Accessory Methods **

  /**
   * Sets the local instance YearPicker object.
   * @param yearPicker The YearPicker object to reference.
   */
  public void setYearPicker (MJYearPicker yearPicker) {
    this.yearPicker = yearPicker;
  }

  /**
   * Sets the local instance DayPicker object.
   * @param dayPicker The DayPicker object to reference.
   */
  public void setDayPicker (MJDayPicker dayPicker) {
    this.dayPicker = dayPicker;
  }

}