package de.duehl.swing.ui.elements.box;

/*
 * Copyright 2017 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComboBox;

/**
 * Diese Klasse stellt eine Combobox mit Inhalten dar, die Beobachter über eine Auswahl des
 * Benutzers informiert.
 *
 * @version 1.01     2017-11-17
 * @author Christian Dühl
 */

public class ObservableComboBox<T> implements BoxSelectionChangeObservable<T> {

    private final JComboBox<T> box;
    private final ItemListener boxItemListener;
    private final List<BoxSelectionChangeObserver<T>> observers;

    public ObservableComboBox() {
        box = new JComboBox<>();

        boxItemListener = createBoxItemListener();
        observers = new ArrayList<>();
    }

    /**
     * Legt fest, ob der Benutzer auch eigenen Werte eingeben kann.
     *
     * Das macht nur wirklich Sinn bei Strings, wenn man sonst was einträgt, das nicht dem Typ
     * entspricht, knallt es beim Cast auf T.
     */
    public void setEditable(boolean editable) {
        box.setEditable(editable);
    }

    private ItemListener createBoxItemListener() {
        return new ItemListener () {
            @Override
            public void itemStateChanged(ItemEvent event) {
                if (event.getStateChange() == ItemEvent.SELECTED) {
                    reactOnBoxSelection();
                }
            }
        };
    }

    private void reactOnBoxSelection() {
        T selected = getSelected();
        for (BoxSelectionChangeObserver<T> observer : observers) {
            observer.boxSelectionChanged(selected);
        }
    }

    public T getSelected() {
        @SuppressWarnings("unchecked")
        T selected = (T) box.getSelectedItem();
        return selected;
    }

    /** Fügt einen Observer hinzu. */
    @Override
    public void addBoxSelectionChangeObserver(BoxSelectionChangeObserver<T> observer) {
        observers.add(observer);
    }

    /** Entfernt einen Observer. */
    @Override
    public void deleteBoxSelectionChangeObserver(BoxSelectionChangeObserver<T> observer) {
        observers.remove(observer);
    }

    /** Legt die Werte fest, die in der Box ausgewählt werden können. */
    public void setValues(List<T> values) {
        box.removeItemListener(boxItemListener);

        box.removeAllItems();
        for (T value : values) {
            box.addItem(value);
        }

        box.addItemListener(boxItemListener);
    }

    /** Legt den ausgewählten Werte fest, der in der Box angezeigt wird. */
    public void showValue(T value) {
        box.removeItemListener(boxItemListener);
        box.setSelectedItem(value);
        box.addItemListener(boxItemListener);
    }

    /** Getter für die Komponente zum Einfügen in die Gui. */
    public Component getBox() {
        return box;
    }

    public void validate() {
        box.validate();
    }

    public void setPreferredSize(Dimension preferredSize) {
        box.setPreferredSize(preferredSize);
    }

}
