/* ConjecturingComboBoxModel.java
 * =========================================================================
 * This file is part of the GrInvIn project - http://www.grinvin.org
 * 
 * Copyright (C) 2005-2008 Universiteit Gent
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License can be found in the file
 * LICENSE.txt provided with the source distribution of this program (see
 * the META-INF directory in the source jar). This license can also be
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 * 
 * If you did not receive a copy of the GNU General Public License along
 * with this program, contact the lead developer, or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.grinvin.worksheet;

import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import org.grinvin.conjecture.engine.Engine;
import org.grinvin.invariants.Invariant;
import org.grinvin.list.invariants.InvariantListModel;
import org.grinvin.list.invariants.InvariantListModelListener;

/**
 * Combo box model for the conjecturing window. Keeps track of an
 * ordered list of invariants, and only shows those that are accepted
 * by the associated engine.
 */
public class ConjecturingComboBoxModel extends AbstractListModel
        implements ComboBoxModel, InvariantListModelListener {
    
    //
    private final Engine engine;
    
    //
    private final WorksheetModel worksheetModel;
    
    //
    private final InvariantListModel model;
    
    //
    private final List<Invariant> list;
    
    public ConjecturingComboBoxModel(Engine engine, WorksheetModel worksheetModel) {
        this.engine = engine;
        this.worksheetModel = worksheetModel;
        this.model = worksheetModel.getInvariantListModel();
        this.list = new ArrayList<Invariant> ();
        
        // fill with current contents of model and make sure
        // that combobox is synchronised with later updates
        rebuild();
        setSelectedItem(list.size() > 0 ? list.get(0) : null);
        model.addInvariantListModelListener(this);
        addListDataListener(new InvariantSelectionListener());
    }
    
    public int getSize() {
        return list.size();
    }
    
    public Invariant getElementAt(int index) {
        return list.get(index);
    }
    
    //
    private Invariant selected;
    
    public Invariant getSelectedItem() {
        return selected;
    }
    
    public Engine getEngine() {
        return engine;
    }
    
    /**
     * Returns the internal index of the selected item.
     * <p><b>Note:<b> This is a slow operation, use of 
     * {@link #getSelectedItem} is to be preferred
     */
    public int getSelectedIndex() {
        return indexOf(selected);
    }
    
    /**
     * Return the index of the given invariant in the internal list.
     */
    public int indexOf(Invariant invariant) {
        if (invariant == null)
            return -1;
        int pos = insertPosition(invariant);
        if (pos == list.size() || list.get(pos) != invariant)
            return -1;
        else
            return pos;
        
    }
    
    
    @SuppressWarnings("PMD.CompareObjectsWithEquals")
    public void setSelectedItem(Object anItem) {
        if (selected == anItem)
            return;
        Invariant invariant = null;
        if (anItem instanceof Invariant) {
            invariant = (Invariant)anItem;
            if (! engine.allows(invariant)) {
                invariant = null;
            }
        }
        if (selected != invariant) {
            this.selected = invariant;
            fireContentsChanged(this, -1, -1);
        }
    }
    
    //
    private int insertPosition(Invariant invariant) {
        String id = invariant.getId();
        int pos = list.size() - 1;
        while (pos >= 0 && list.get(pos).getId().compareTo(id) >= 0)
            pos --;
        return pos + 1;
    }
    
    /**
     * Add an invariant to the combobox. Only adds the invariant when this is
     * allowed by the engine.
     */
    public void add(Invariant invariant) {
        if (engine.allows(invariant)) {
            int pos = insertPosition(invariant);
            int len = list.size();
            list.add(pos, invariant);
            fireIntervalAdded(this, pos, len);
        }
    }
    
    @SuppressWarnings("PMD.CompareObjectsWithEquals")
    private void rebuild() {
        // rebuild the entire list
        list.clear();
        for (int i=model.getSize()-1; i >= 0; i--)
            add(model.get(i));
        
        // check whether selection still valid
        if (selected != null && indexOf(selected) < 0) {
            this.selected = null;
            fireContentsChanged(this, -1, -1);
        }
    }
    
    public void intervalRemoved(ListDataEvent e) {
        rebuild();
    }
    
    public void intervalAdded(ListDataEvent e) {
        int from = e.getIndex0();
        int upto = e.getIndex1();
        for (int i=from; i<= upto; i++)
            add(model.get(i));
    }
    
    public void contentsChanged(ListDataEvent e) {
        rebuild();
    }
    
    private class InvariantSelectionListener implements ListDataListener {
        public void intervalAdded(ListDataEvent e) {
            worksheetModel.fireEngineConfigurationChanged();
        }

        public void intervalRemoved(ListDataEvent e) {
            worksheetModel.fireEngineConfigurationChanged();
        }

        public void contentsChanged(ListDataEvent e) {
            worksheetModel.fireEngineConfigurationChanged();
        }
        
    }
}
