--This program was adapted from the example in Chapter 15 of
--   STL Tutorial and Reference Guide, 2nd ed.
--   Musser, Derge, and Saini
--
--The dictionary file for this program can be found at Musser's web site:
--
--  <http://www.cs.rpi.edu/~musser/stl-book/source/>
--
--The file name is "diction" or "diction.txt".
--

with Anagram_Types;  use Anagram_Types;
with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Integer_Text_IO;  use Ada.Integer_Text_IO;
with Ada.Command_Line;  use Ada.Command_Line;
with Ada.Characters.Handling;  use Ada.Characters.Handling;
with Ada.Containers.Generic_Array_Sort;

procedure Anagram is

   M : Map_Types.Map;
   V : Cursor_Vectors.Vector;

   use Map_Types;
   use String_Vectors;

   function Less_Case_Insensitive (I, J : Character) return Boolean is
   begin
      return To_Lower (I) < To_Lower (J);
   end;

   procedure Sort is
      new Ada.Containers.Generic_Array_Sort
        (Positive,
         Character,
         String,
         "<" => Less_Case_Insensitive);

   --use Charles.Algorithms;
   use Ada.Containers;

begin

   if Argument_Count = 0 then
      Put_Line ("no dictionary specified");
      return;
   end if;

   declare
      File : File_Type;

      Line : String (1 .. 81);
      Last : Natural;
   begin
      Open (File, In_File, Argument (1));

      while not End_Of_File (File) loop

         Get_Line (File, Line, Last);
         pragma Assert (Last > 0);
         pragma Assert (Last < Line'Last);

         declare
            E : String renames Line (Line'First .. Last);
            K : String := E;
            C : Map_Types.Cursor;
            B : Boolean;
         begin
            Sort (K);
            Insert (M, K, String_Vectors.Empty_Vector, C, B);

            declare
               procedure Process (K : in String; ME : in out String_Vectors.Vector) is
                  use Cursor_Vectors;
               begin
                  if Length (ME) = 1 then
                     Append (V, New_Item => C);
                  end if;

                  Append (ME, New_Item => E);
               end Process;
            begin
               Map_Types.Update_Element (C, Process'Access);
            end;
         end;

      end loop;

      Close (File);
   end;

   declare
      function "<" (L, R : Map_Types.Cursor) return Boolean is
         Result : Boolean;

         procedure Process_L (LK : String; LV : String_Vectors.Vector) is
            procedure Process_R (RK : String; RV : String_Vectors.Vector) is
               use String_Vectors;
            begin
               Result := Length (LV) < Length (RV);
            end Process_R;
         begin
            Query_Element (R, Process_R'Access);
         end Process_L;
      begin
         Query_Element (L, Process_L'Access);
         return Result;
      end "<";

      procedure Sort is
         new Cursor_Vectors.Generic_Sort;
   begin
      Sort (V);
   end;

   declare

      procedure Print (C : Map_Types.Cursor) is
         procedure Process (K : String; E : String_Vectors.Vector) is
            procedure Process (SVC : String_Vectors.Cursor) is
            begin
               Put (Element (SVC));
               Put (' ');
            end;
         begin
            Put ("   ");
            Iterate (E, Process'Access);
            New_Line;
         end Process;
      begin
         Query_Element (C, Process'Access);
      end Print;

      use Cursor_Vectors;

      I : Positive := First_Index (V);
      J : constant Positive := Last_Index (V) + 1;
      K : Positive;

      N : Count_Type;

   begin

      New_Line;

      while I /= J loop

         declare
            procedure Process (K : String; E : String_Vectors.Vector) is
            begin
               N := Length (E);
            end;

            C : constant Map_Types.Cursor := Element (V, I);
         begin
            Query_Element (C, Process'Access);
         end;

         K := I + 1;

         while K /= J loop

            declare
               Done : Boolean;

               procedure Process (K : String; E : String_Vectors.Vector) is
               begin
                  Done := Length (E) /= N;
               end;

               C : constant Map_Types.Cursor := Element (V, K);
            begin
               Query_Element (C, Process'Access);
               exit when Done;
            end;

            K := K + 1;

         end loop;

         Put ("There are ");
         Put (Positive'(K - I), Width => 0);
         Put (" anagram group(s) of size ");
         Put (Count_Type'Image (N));
         Put_Line (".");
         New_Line;

         while I /= K loop
            Print (C => Element (V, I));
            I := I + 1;
         end loop;

         New_Line;

      end loop;

   end;


end Anagram;

