package users.swing;

import javax.swing.*;

import java.util.*;

import users.*;
import users.events.*;

import util.*;

/**
  * A ListModel modelling the set or a subset of users managed by a UserManager.
  *
  * @see UserManager
  * @see User
  *
  * @author Steffen Zschaler
  * @version 2.0 05/05/1999
  * @since v2.0
  */
public class UserListModel extends AbstractListModel implements UserDataListener, HelpableListener {

  /**
    * A local copy of the list of users to provide easy and consistent access via an
    * index.
    *
    * This model is kept up to date by registering as a listener with the UserManager.
    *
    * @serial
    */
  protected List m_lUsers;

  /**
    * A Comparator that orders the users. By default, users are ordered by their name.
    *
    * @serial
    */
  protected Comparator m_cmpComparator = new SerializableComparator() {
    public int compare (Object o1, Object o2) {
      return ((User) o1).getName().compareTo (((User) o2).getName());
    }
  };

  /**
    * A filter that defines the subset of users that are displayed. If <code>null</code> no
    * filtering will occur.
    *
    * @serial
    */
  protected UserFilter m_ufFilter;

  /**
    * The UserManager that is being modelled.
    *
    * @serial
    */
  protected UserManager m_um;

  /**
    * Create a new UserListModel modelling the global UserManager. All Users will be displayed and they will be
    * sorted by their names.
    */
  public UserListModel() {
    this (UserManager.getGlobalUM());
  }

  /**
    * Create a new UserListModel modelling the global UserManager.
    *
    * @param uf a filter that defines the set of users to be displayed. If <code>null</code>, no filtering will
    * occur.
    * @param cmp a Comparator that defines the order of the users to be displayed. The objects to be compared
    * by this comparator will be Users. If <code>null</code>, users will be ordered by their names.
    */
  public UserListModel (UserFilter uf,
                        Comparator cmp) {
    this (UserManager.getGlobalUM(),
          uf,
          cmp);
  }

  /**
    * Create a new UserListModel modelling a given UserManager. All Users will be displayed and they will be
    * sorted by their names.
    *
    * @param um the UserManager to be modelled.
    */
  public UserListModel (UserManager um) {
    this (um, null, null);
  }

  /**
    * Create a new UserListModel modelling a given UserManager.
    *
    * @param um the UserManager to be modelled.
    * @param uf a filter that defines the set of users to be displayed. If <code>null</code>, no filtering will
    * occur.
    * @param cmp a Comparator that defines the order of the users to be displayed. The objects to be compared
    * by this comparator will be Users. If <code>null</code>, users will be ordered by their names.
    */
  public UserListModel (UserManager um,
                        UserFilter uf,
                        Comparator cmp) {
    super();

    // replace listener list for special support
    listenerList = new ListenerHelper (this);

    m_um = um;
    m_ufFilter = uf;

    if (cmp != null) {
      m_cmpComparator = cmp;
    }

    updateModel();
  }

  // List Model methods

  /**
    * Return the number of users in the model.
    *
    * @return the number of users in the model.
    *
    * @override Never
    */
  public int getSize() {
    // make sure internal model is up to date.
    ((ListenerHelper) listenerList).needModelUpdate();

    return m_lUsers.size();
  }

  /**
    * Get a user by index.
    *
    * @param nIndex the index of the user to be returned.
    *
    * @return the user associated with the given index.
    *
    * @override Never
    */
  public Object getElementAt (int nIndex) {
    // make sure internal model is up to date.
    ((ListenerHelper) listenerList).needModelUpdate();

    return m_lUsers.get (nIndex);
  }

  // UserDataListener methods

  /**
    * Respond to the <code>userAdded</code> event by updating the internal model
    * and forwarding a translated version of the event to anyone who listens to us.
    *
    * @param e the event object describing the event.
    *
    * @override Sometimes
    */
  public void userAdded (UserDataEvent e) {
    updateModel();

    int nPos = m_lUsers.indexOf (e.getUser());

    if (nPos > -1) {
      fireIntervalAdded (this, nPos, nPos);
    }
  }

  /**
    * Respond to the <code>userDeleted</code> event by updating the internal model
    * and forwarding a translated version of the event to anyone who listens to us.
    *
    * @param e the event object describing the event.
    *
    * @override Sometimes
    */
  public void userDeleted (UserDataEvent e) {
    int nPos = m_lUsers.indexOf (e.getUser());

    updateModel();

    if (nPos > -1) {
      fireIntervalRemoved (this, nPos, nPos);
    }
  }

  // HelpableListener methods
  /**
    * Update the internal model.
    *
    * @override Sometimes
    */
  public synchronized void updateModel() {
    List lUsers = new LinkedList (m_um.getUsers());

    if (m_ufFilter != null) {
      for (Iterator i = lUsers.iterator(); i.hasNext();) {
        if (!m_ufFilter.match ((User) i.next())) {
          i.remove();
        }
      }
    }

    Collections.sort (lUsers, m_cmpComparator);

    m_lUsers = lUsers;
  }

  /**
    * Subscribe to the UserManager to be informed of any changes in its set of users.
    *
    * @override Never
    */
  public void subscribe() {
    m_um.addUserDataListener (this);
  }

  /**
    * Unsubscribe from the UserManager as there is no need to listen to it anymore, as
    * we are not listened to anymore. From now on we are working in "poll-mode" until
    * any listener indicates an interest in us again.
    *
    * @override Never
    */
  public void unsubscribe() {
    m_um.removeUserDataListener (this);
  }
}