with Data_Maps;  use Data_Maps;
with Ada.Text_IO;  use Ada.Text_IO;

package body Relation_Maps is

   function Compare_Dates (L_Name, R_Name : String) return Boolean is

      LC : Data_Maps.Cursor := Find (Dates, L_Name);
      RC : Data_Maps.Cursor := Find (Dates, R_Name);

   begin

      if Has_Element (LC) then
         if Has_Element (RC) then
            return Element (LC) < Element (RC);
         end if;

         return False;
      end if;

      if Has_Element (RC) then
         return True;
      end if;

      return False;

   end Compare_Dates;


   function Length (Container : Set) return Ada.Containers.Count_Type is
      Result : Ada.Containers.Count_Type;

      procedure Process (K : String; E : String_Sets.Set) is
      begin
         Result := String_Sets.Length (E);
      end;
   begin
      Map_Types.Query_Element (Map_Types.Cursor (Container), Process'Access);
      return Result;
   end;



   procedure Insert
     (Container : Map;
      Key       : String) is

      C : Map_Types.Cursor;
      B : Boolean;
   begin
      Map_Types.Insert
        (Container => Container.all,
         Key       => Key,
         New_Item  => String_Sets.Empty_Set,
         Position  => C,
         Inserted  => B);
   end;


   function Insert
     (Container : Map;
      Key       : String) return Set is

      C : Map_Types.Cursor;
      B : Boolean;
   begin
      Map_Types.Insert
        (Container => Container.all,
         Key       => Key,
         New_Item  => String_Sets.Empty_Set,
         Position  => C,
         Inserted  => B);

      return Set (C);
   end;


   function Contains
     (Container : Map;
      Key       : String) return Boolean is
   begin
      return Map_Types.Contains (Container.all, Key);
   end;


   function Element
     (Container : Map;
      Key       : String) return Set is

      C : constant Map_Types.Cursor :=
        Map_Types.Find (Container.all, Key);

      pragma Assert (Map_Types.Has_Element (C));
   begin
      return Set (C);
   end Element;


   procedure Iterate
     (Container : Map;
      Key       : String;
      Process   : not null access procedure (Name : in String)) is

      procedure Set_Iterate_Process (C : String_Sets.Cursor) is
      begin
         String_Sets.Query_Element (C, Process);
      end;

      procedure Map_Query_Process (K : String; E : String_Sets.Set) is
      begin
         String_Sets.Iterate (E, Set_Iterate_Process'Access);
      end;

      C : constant Map_Types.Cursor := Map_Types.Find (Container.all, Key);

   begin

      if Map_Types.Has_Element (C) then
         Map_Types.Query_Element (C, Map_Query_Process'Access);
      end if;

   end Iterate;



   procedure Insert
     (Container : Map;
      Key       : String;
      New_Name  : String) is

      procedure Process
        (K : in     String;
         E : in out String_Sets.Set) is
      begin
         Insert (E, New_Name);
      end;

      C : Map_Types.Cursor;
      B : Boolean;

   begin

      Map_Types.Insert
        (Container => Container.all,
         Key       => Key,
         New_Item  => String_Sets.Empty_Set,
         Position  => C,
         Inserted  => B);

      Map_Types.Update_Element (C, Process'Access);

   end Insert;


   procedure Insert
     (Container : Set;
      New_Name  : String) is

      procedure Process
        (K : in     String;
         E : in out String_Sets.Set) is
      begin
         String_Sets.Insert (E, New_Name);
      end;

      C : constant Map_Types.Cursor := Map_Types.Cursor (Container);

   begin

      Map_Types.Update_Element (C, Process'Access);

   end Insert;


   procedure Iterate
     (Container : in Map;
      Process   : not null access procedure (Name : in String; Elements : in Set)) is

      procedure Iterate_Process (C : Map_Types.Cursor) is

         procedure Query_Process (K : String; E : String_Sets.Set) is
         begin
            Process (K, Set (C));
         end;

      begin
         Map_Types.Query_Element (C, Query_Process'Access);
      end;

   begin
      Map_Types.Iterate (Container.all, Iterate_Process'Access);
   end;


   procedure Iterate
     (Container : in Set;
      Process   : not null access procedure (Name : in String)) is

      procedure Iterate_Process (C : String_Sets.Cursor) is
      begin
         Query_Element (C, Process);
      end;

      procedure Query_Process (K : in String; E : String_Sets.Set) is
      begin
         String_Sets.Iterate (E, Iterate_Process'Access);
      end;
   begin
      Map_Types.Query_Element (Map_Types.Cursor (Container), Query_Process'Access);
   end;


   function First (Container : Set) return Set_Cursor is
      Result : String_Sets.Cursor;

      procedure Process (K : String; E : String_Sets.Set) is
      begin
         Result := First (E);
      end;
   begin
      Map_Types.Query_Element (Map_Types.Cursor (Container), Process'Access);
      return Set_Cursor (Result);
   end;


   function First_Element (Container : Set) return String is
      Result : String_Sets.Cursor;

      procedure Process (K : String; E : String_Sets.Set) is
      begin
         Result := First (E);
      end;
   begin
      Map_Types.Query_Element (Map_Types.Cursor (Container), Process'Access);
      return String_Sets.Element (Result);
   end;


   procedure Next (Position : in out Set_Cursor) is
   begin
      String_Sets.Next (String_Sets.Cursor (Position));
   end;


   function Has_Element (Position : Set_Cursor) return Boolean is
      C : constant String_Sets.Cursor := String_Sets.Cursor (Position);
   begin
      return String_Sets.Has_Element (C);
   end;


   function Element (Position : Set_Cursor) return String is
      C : constant String_Sets.Cursor := String_Sets.Cursor (Position);
   begin
      return String_Sets.Element (C);
   end;


   function Contains
     (Container : Set;
      Name      : String) return Boolean is

      Result : Boolean;

      procedure Process (K : String; E : String_Sets.Set) is
      begin
         Result := String_Sets.Contains (E, Name);
      end;
   begin
      Map_Types.Query_Element (Map_Types.Cursor (Container), Process'Access);
      return Result;
   end;


   Students_Map : aliased Map_Types.Map;
   Advisors_Map : aliased Map_Types.Map;

   function Students return Map is
   begin
      return Students_Map'Access;
   end;

   function Advisors return Map is
   begin
      return Advisors_Map'Access;
   end;


end Relation_Maps;

