/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/


package com.sun.star.wizards.common;

import java.util.*;

import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;


import org.w3c.dom.*;

/**
 *
 * @author  rpiterman
 */
public class ConfigSet implements ConfigNode, XMLProvider, ListModel
{

    private Class childClass;
    private Map childrenMap = new HashMap();
    private List childrenList = new Vector();
    public Object root;
    /**
     * After reading the configuration set items,
     * the ConfigSet checks this field.
     * If it is true, it will remove any nulls from
     * the vector.
     * subclasses can change this field in the constructor
     * to avoid this "deletion" of nulls.
     */
    protected boolean noNulls = true;
    /** Utility field used by event firing mechanism. */
    private javax.swing.event.EventListenerList listenerList = null;

    public ConfigSet(Class childType)
    {
        childClass = childType;
    }

    public void add(String name, Object child)
    {
        childrenMap.put(name, child);
        try
        {
            int i = ((Indexable) child).getIndex();
            int oldSize = getSize();
            while (getSize() <= i)
            {
                childrenList.add(null);
            }
            childrenList.set(i, child);
            if (oldSize > i)
            {
                oldSize = i;
            }
            fireListDataListenerIntervalAdded(oldSize, i);
        }
        catch (ClassCastException cce)
        {
            childrenList.add(child);
            fireListDataListenerIntervalAdded(getSize() - 1, getSize() - 1);
        }
    }

    public void add(int i, Object o)
    {
        int name = i;
        while (getElement(PropertyNames.EMPTY_STRING + name) != null)
        {
            name++;
        }
        childrenMap.put(PropertyNames.EMPTY_STRING + name, o);
        childrenList.add(i, o);

        fireListDataListenerIntervalAdded(i, i);
    }

    protected Object createChild() throws InstantiationException, IllegalAccessException
    {
        return childClass.newInstance();
    }

    public void writeConfiguration(Object configView, Object param)
    {
        Object[] names = childrenMap.keySet().toArray();

        if (ConfigNode.class.isAssignableFrom(childClass))
        {
            //first I remove all the children from the configuration.
            String children[] = Configuration.getChildrenNames(configView);
            for (int i = 0; i < children.length; i++)
            {
                try
                {
                    Configuration.removeNode(configView, children[i]);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }            // and add them new.
            }
            for (int i = 0; i < names.length; i++)
            {
                try
                {
                    ConfigNode child = (ConfigNode) getElement(names[i]);
                    Object childView = Configuration.addConfigNode(configView, (String) names[i]);
                    child.writeConfiguration(childView, param);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        }
        //for a set of primitive / String type.
        else
        {
            throw new IllegalArgumentException("Unable to write primitive sets to configuration (not implemented)");
        }
    }

    public void readConfiguration(Object configurationView, Object param)
    {
        String[] names = Configuration.getChildrenNames(configurationView);

        if (ConfigNode.class.isAssignableFrom(childClass))
        {

            for (int i = 0; i < names.length; i++)
            {
                try
                {
                    ConfigNode child = (ConfigNode) createChild();
                    child.setRoot(root);
                    child.readConfiguration(Configuration.getNode(names[i], configurationView), param);
                    add(names[i], child);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
            //remove any nulls from the list
            if (noNulls)
            {
                for (int i = 0; i < childrenList.size(); i++)
                {
                    if (childrenList.get(i) == null)
                    {
                        childrenList.remove(i--);
                    }
                }
            }
        }
        //for a set of primitive / String type.
        else
        {
            for (int i = 0; i < names.length; i++)
            {
                try
                {
                    Object child = Configuration.getNode(names[i], configurationView);
                    add(names[i], child);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        }
    }

    public void remove(Object obj)
    {
        Object key = getKey(obj);
        childrenMap.remove(key);
        int i = childrenList.indexOf(obj);
        childrenList.remove(obj);
        fireListDataListenerIntervalRemoved(i, i);
    }

    public void remove(int i)
    {
        Object o = getElementAt(i);
        remove(o);
    }

    public void clear()
    {
        childrenMap.clear();
        childrenList.clear();
    }

    public void update(int i)
    {
        fireListDataListenerContentsChanged(i, i);
    }

    public Node createDOM(Node parent)
    {

        Object[] items = items();

        for (int i = 0; i < items.length; i++)
        {
            Object item = items[i];
            if (item instanceof XMLProvider)
            {
                ((XMLProvider) item).createDOM(parent);
            }
        }
        return parent;
    }

    public Object[] items()
    {
        return childrenList.toArray();
    }

    public Object getKey(Object object)
    {
        for (Iterator i = childrenMap.entrySet().iterator(); i.hasNext();)
        {

            Map.Entry me = (Map.Entry) i.next();
            if (me.getValue() == object)
            {
                return me.getKey();
            }
        }
        return null;
    }

    public Object getKey(int i)
    {
        int c = 0;
        while (i > -1)
        {
            if (getElementAt(c) != null)
            {
                i--;
            }
            c++;
        }
        if (c == 0)
        {
            return null;
        }
        else
        {
            return getKey(getElementAt(c - 1));
        }
    }

    public void setRoot(Object newRoot)
    {
        root = newRoot;
    }

    /** Registers ListDataListener to receive events.
     * @param listener The listener to register.
     *
     */
    public synchronized void addListDataListener(javax.swing.event.ListDataListener listener)
    {
        if (listenerList == null)
        {
            listenerList = new javax.swing.event.EventListenerList();
        }
        listenerList.add(javax.swing.event.ListDataListener.class, listener);
    }

    /** Removes ListDataListener from the list of listeners.
     * @param listener The listener to remove.
     *
     */
    public synchronized void removeListDataListener(javax.swing.event.ListDataListener listener)
    {
        listenerList.remove(javax.swing.event.ListDataListener.class, listener);
    }

    /** Notifies all registered listeners about the event.
     *
     * @param event The event to be fired
     *
     */
    private void fireListDataListenerIntervalAdded(int i0, int i1)
    {
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, i0, i1);
        if (listenerList == null)
        {
            return;
        }
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2)
        {
            if (listeners[i] == javax.swing.event.ListDataListener.class)
            {
                ((javax.swing.event.ListDataListener) listeners[i + 1]).intervalAdded(event);
            }
        }
    }

    /** Notifies all registered listeners about the event.
     *
     * @param event The event to be fired
     *
     */
    private void fireListDataListenerIntervalRemoved(int i0, int i1)
    {
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, i0, i1);
        if (listenerList == null)
        {
            return;
        }
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2)
        {
            if (listeners[i] == javax.swing.event.ListDataListener.class)
            {
                ((javax.swing.event.ListDataListener) listeners[i + 1]).intervalRemoved(event);
            }
        }
    }

    /** Notifies all registered listeners about the event.
     *
     * @param event The event to be fired
     *
     */
    private void fireListDataListenerContentsChanged(int i0, int i1)
    {
        ListDataEvent event = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, i0, i1);
        if (listenerList == null)
        {
            return;
        }
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2)
        {
            if (listeners[i] == javax.swing.event.ListDataListener.class)
            {
                ((javax.swing.event.ListDataListener) listeners[i + 1]).contentsChanged(event);
            }
        }
    }

    public Object getElementAt(int i)
    {
        return childrenList.get(i);
    }

    public Object getElement(Object o)
    {
        return childrenMap.get(o);
    }

    public int getSize()
    {
        return childrenList.size();
    }

    public Set keys()
    {
        return childrenMap.keySet();
    }

    public int getIndexOf(Object item)
    {
        return childrenList.indexOf(item);
    }

    /**
     * Set members might include a property
     * which orders them.
     * This method reindexes the given member to be 
     * the index number 0
     * Do not forget to call commit() after calling this method.
     * @param confView
     * @param memebrName
     */
    public void reindexSet(Object confView, String memberName, String indexPropertyName) throws Exception
    {
        /*
         * First I read all memebrs of the set,
         * except the one that should be number 0
         * to a vector, ordered by there index property
         */
        String[] names = Configuration.getChildrenNames(confView);
        Vector v = new Vector(names.length);
        Object member = null;
        int index = 0;
        for (int i = 0; i < names.length; i++)
        {
            if (!names[i].equals(memberName))
            {
                member = Configuration.getConfigurationNode(names[i], confView);
                index = Configuration.getInt(indexPropertyName, member);
                while (index >= v.size())
                {
                    v.add(null);
                }
                v.setElementAt(member, index);

            }
        /**
         * Now I reindex them
         */
        }
        index = 1;
        for (int i = 0; i < v.size(); i++)
        {
            member = v.get(i);
            if (member != null)
            {
                Configuration.set(index++, indexPropertyName, member);
            }
        }

    }

    public void sort(Comparator comparator)
    {
        Collections.sort(this.childrenList, comparator);
    }
}
