package org.jacorb.poa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import org.jacorb.poa.AGM.TGTCWrapper;
import org.omg.PortableGroup.TagGroupTaggedComponent;


/**
 * Active Group Map Map groups with its members oids.
 *
 * @author Alysson Neves Bessani
 * @author Nick Cross
 * @version 1.0
 * @see GOA
 */
public class AGM
{
   private static final byte [][]EMPTYBYTE = new byte[0][];

   private HashMap<TGTCWrapper, ArrayList<byte[]>> groupTable = new HashMap<TGTCWrapper, ArrayList<byte[]>> ();


   /**
    * Adds an oid to a group represented by the TagGroupTaggedComponent
    *
    * @param tagGroupTaggedComponent
    * @param oid
    * @return the size of the group
    */
   synchronized int addToGroup (TagGroupTaggedComponent tagGroupTaggedComponent, byte[] oid)
   {
      TGTCWrapper t = new TGTCWrapper (tagGroupTaggedComponent);
      ArrayList members = (ArrayList)groupTable.get (t);

      if (members == null)
      {
         members = new ArrayList ();
         groupTable.put (t, members);
      }

      // If it does not contain the OID insert it
      if (!members.contains (oid))
      {
         members.add (oid);
      }
      return members.size ();
   }


   /**
    * Removes a stringfied oid to a group (represented by the stringfied group
    * key)
    *
    * @param tagGroupTaggedComponent
    * @param oid
    * @return the size of the group
    */
   synchronized int removeFromGroup (TagGroupTaggedComponent tagGroupTaggedComponent, byte[] oid)
   {
      TGTCWrapper t = new TGTCWrapper (tagGroupTaggedComponent);
      ArrayList members = (ArrayList)groupTable.get (t);

      if (members == null)
      {
         // no group exists in the table so return 0 for no members.
         return 0;
      }

      members.remove (oid);

      int nMembers = members.size ();

      // check if there members in this group (group empty), if empty
      // remove the entry from the group table.
      if (nMembers == 0)
      {
         groupTable.remove (t);
      }

      return nMembers;
   }


   /**
    * Returns the group members
    *
    * @param tagGroup
    * @return the group members oids as strings
    */
   final byte[][] getMembersFromGroup (TagGroupTaggedComponent tagGroup)
   {
      TGTCWrapper t = new TGTCWrapper (tagGroup);
      ArrayList members = (ArrayList)groupTable.get (t);

      return
      (
         members == null ?
         // no group in the table, has no members
         EMPTYBYTE
         :
         // group exists, return the array of members
         (byte[][])members.toArray (EMPTYBYTE)
      );
   }


   /**
    * Returns the groups in which the object is registered.
    *
    * @param oid
    * @return the groups that have this object as a member
    */
   final ArrayList<TGTCWrapper> getGroupsWithMember (byte[] oid)
   {
      ArrayList<TGTCWrapper> keyList = new ArrayList<TGTCWrapper> ();

      for (Iterator<TGTCWrapper> iterator = groupTable.keySet ().iterator (); iterator.hasNext ();)
      {
         TGTCWrapper t = (TGTCWrapper)iterator.next ();
         byte [][]oids = getMembersFromGroup (t.tcgtc);

         for (int i = 0; i < oids.length; i++)
         {
            if (Arrays.equals (oids[i], oid))
            {
               keyList.add (t);
               continue;
            }
         }
      }

      return keyList;
   }


   /**
    * TGTCWrapper is a simple wrapper around a TagGroupTaggedComponent. It
    * allows a TagGroupTaggedComponent to be used as a key in a hashmap as its
    * implementation of hashCode and equals actually is an implementation for
    * the component not the wrapper.
    */
   static class TGTCWrapper
   {
      TagGroupTaggedComponent tcgtc;


      public TGTCWrapper (TagGroupTaggedComponent tagGroupTaggedComponent)
      {
         tcgtc = tagGroupTaggedComponent;
      }


      /**
       * This implementation manually generates hashcode on the contained component
       * rather than delegating to its implementation (as its generated code it doesn't have one).
       *
       * @see java.lang.Object#hashCode()
       */
      public int hashCode ()
      {
         final int prime = 31;
         int result = 1;
         result = prime * result +
                  ((tcgtc.group_domain_id == null) ? 0 : tcgtc.group_domain_id.hashCode ());
         result = prime * result + ((tcgtc.group_version == null) ? 0 : tcgtc.group_version.major);
         result = prime * result + ((tcgtc.group_version == null) ? 0 : tcgtc.group_version.minor);
         result = prime * result + (int)(tcgtc.object_group_id ^ (tcgtc.object_group_id >>> 32));
         result = prime * result + tcgtc.object_group_ref_version;
         return result;
      }


      /**
       * This implementation manually checks for equality on the contained component
       * rather than delegating to its implementation (as its generated code it doesn't have one).
       *
       * @see java.lang.Object#equals(java.lang.Object)
       */
      public boolean equals (Object obj)
      {
         if (this == obj)
         {
            return true;
         }
         if (obj == null)
         {
            return false;
         }
         if (!(obj instanceof TGTCWrapper))
         {
            return false;
         }

         TagGroupTaggedComponent other = ((TGTCWrapper)obj).tcgtc;

         if (tcgtc == null || other == null)
         {
            return false;
         }

         if (tcgtc.group_domain_id == null)
         {
            if (other.group_domain_id != null)
            {
               return false;
            }
         }
         else if (!tcgtc.group_domain_id.equals (other.group_domain_id))
         {
            return false;
         }
         if (tcgtc.group_version == null)
         {
            if (other.group_version != null)
            {
               return false;
            }
         }
         else if (!(tcgtc.group_version.major == other.group_version.major && tcgtc.group_version.minor == other.group_version.minor))
         {
            return false;
         }
         if (tcgtc.object_group_id != other.object_group_id)
         {
            return false;
         }
         if (tcgtc.object_group_ref_version != other.object_group_ref_version)
         {
            return false;
         }
         return true;
      }
   }
}
