/**
 * GUI Commands
 * Copyright 2004 Andrew Pietsch
 *
 * Licensed 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.
 *
 * $Id: MemberList.java,v 1.15 2006/02/26 00:59:06 pietschy Exp $
 */

package org.pietschy.command;

import org.pietschy.command.log.Logger;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

class
MemberList
implements CommandManagerListener
{
   static final String _ID_ = "$Id: MemberList.java,v 1.15 2006/02/26 00:59:06 pietschy Exp $";

   private Logger log;

   private CommandGroup group;
   private ArrayList groupMembers = new ArrayList();
   private WeakSet groupContainers = new WeakSet();
   private ExpansionGroupMember expansionPoint;
   private static final String CONTAINER_MANAGER_KEY = "org.pietschy.command.GroupContainerManager";


   public MemberList(CommandGroup parent)
   {
      log = CommandManager.getLogger(getClass());
      this.group = parent;
   }

   protected void
   bindMembers(JComponent container, MenuFactory factory, String faceId)
   {
      GroupContainerManager manager = createContainerManager(container);
      manager.initialise(container, factory, faceId);
      manager.rebuildPopupUsing(groupMembers);
      groupContainers.add(container);
   }

   protected void
   bindMembers(JComponent container, ButtonFactory factory, String faceId)
   {
      GroupContainerManager manager = createContainerManager(container);
      manager.configure(container, factory, faceId);
      manager.rebuildPopupUsing(groupMembers);
      groupContainers.add(container);
   }

   private GroupContainerManager createContainerManager(JComponent container)
   {
      GroupContainerManager manager = group.getMemberFactory().createContainerManager();
      // we just store the container manager in the containers client properties, this
      // means we don't have any hard references that prevent garbage collection.
      container.putClientProperty(CONTAINER_MANAGER_KEY, manager);
      return manager;
   }

   public void
   commandRegistered(CommandManagerEvent event)
   {
      if (isDependantOn(event.getCommand()))
         rebuildAllPopups();
   }

   protected void
   rebuildAllPopups()
   {
      log.enter("rebuildAllPopups");
      Iterator iter = groupContainers.iterator();
      while (iter.hasNext())
      {
         JComponent container = (JComponent) iter.next();
         GroupContainerManager builder = (GroupContainerManager) container.getClientProperty(CONTAINER_MANAGER_KEY);
         log.debug("builder: " + builder);
         if (builder != null)
            builder.rebuildPopupUsing(groupMembers);
      }

      log.exit("rebuildAllPopups");
   }

   /**
    * Checks if this group contains the specified command.  This method only checks the direct
    * chidren, it will only recurse into {@link CommandGroup} children if they have been added
    * inline.
    *
    * @param command the command to check
    * @return <tt>true</tt> if this groups children contain the command, or if a child group
    *         specified as inline contains this command.
    */
   public boolean
   containsDirectlyOrInline(Command command)
   {
      log.enter("containsDirectlyOrInline");
      for (Iterator iterator = groupMembers.iterator(); iterator.hasNext();)
      {
         GroupMember member = (GroupMember) iterator.next();
         if (member.isDependantOn(command))
         {
            log.exit("containsDirectlyOrInline()");
            log.returned(Boolean.TRUE);
            return true;
         }
      }

      log.returned(Boolean.FALSE);
      log.exit("containsDirectlyOrInline");
      return false;
   }

   /**
    * Checks if this group contains the specified command.  This method only checks the direct
    * chidren and not commands that are included as a result of inlining.
    *
    * @param command the command to check
    * @return <tt>true</tt> if this groups children contain the command, or if a child group
    *         specified as inline contains this command.
    */
   public boolean
   containsDirectMemberFor(Command command)
   {
      log.enter("containsDirectMemberFor");
      for (Iterator iterator = groupMembers.iterator(); iterator.hasNext();)
      {
         GroupMember member = (GroupMember) iterator.next();
         if (member.isMemberFor(command))
         {
            log.exit("containsDirectMemberFor");
            log.returned(Boolean.TRUE);
            return true;
         }
      }

      log.returned(Boolean.FALSE);
      log.exit("containsDirectMemberFor");
      return false;
   }

   public boolean
   isDependantOn(Command command)
   {
      for (int i = 0; i < groupMembers.size(); i++)
      {
         GroupMember member = (GroupMember) groupMembers.get(i);
         if (member.isDependantOn(command))
         {
            return true;
         }
      }

      return false;
   }


   public void
   add(GroupMember member)
   {
      if (groupMembers.add(member))
      {
         member.addNotify();
      }
   }


   public ExpansionGroupMember
   insertOrGetExpansionPoint()
   {
      if (expansionPoint == null)
      {
         expansionPoint = group.getMemberFactory().createExpansionMember("default");
         add(expansionPoint);
      }

      return expansionPoint;
   }

   public int
   size()
   {
      int size = groupMembers.size();

      if (expansionPoint != null)
      {
         // installFace the expasionpoints children, and remove itself from the count.
         size += expansionPoint.size() - 1;
      }

      return size;
   }

   public void
   acceptVisitor(GroupVisitor visitor)
   {
      for (Iterator iterator = groupMembers.iterator(); iterator.hasNext();)
      {
         GroupMember member = (GroupMember) iterator.next();
         member.acceptVisitor(visitor);
      }
   }

   public Iterator
   memberIterator()
   {
      return Collections.unmodifiableList(groupMembers).iterator();
   }


   public boolean isMember(Command group)
   {
      for (Iterator iterator = groupMembers.iterator(); iterator.hasNext();)
      {
         GroupMember member = (GroupMember) iterator.next();
         if (member.isMemberFor(group))
            return true;
      }

      return false;
   }

   public boolean
   isInlineMember(CommandGroup group)
   {
      for (Iterator iterator = groupMembers.iterator(); iterator.hasNext();)
      {
         GroupMember member = (GroupMember) iterator.next();
         if (member.isMemberFor(group))
            return (member instanceof InlineMember);
      }

      return false;
   }

}
