with GNAT.Strings;
with GNATCOLL.Projects;          use GNATCOLL.Projects;
with GNATCOLL.VFS;               use GNATCOLL.VFS;
with GNATCOLL.Traces;            use GNATCOLL.Traces;
with GNATCOLL.VFS_Utils;         use GNATCOLL.VFS_Utils;

with Ada.Containers.Indefinite_Ordered_Sets;
with Ada.Containers.Indefinite_Vectors;

with Ada.Text_IO;

with GNAT.OS_Lib;             use GNAT.OS_Lib;
with Ada.Characters.Handling; use Ada.Characters.Handling;

procedure Main is
   Env                 : Project_Environment_Access;
   Source_Project_Tree : GNATCOLL.Projects.Project_Tree;

   P : Project_Type;

   package String_Set is new Ada.Containers.Indefinite_Ordered_Sets (String);
   use String_Set;

   package String_Vector is new
     Ada.Containers.Indefinite_Vectors (Positive, String);
   use String_Vector;

   Paths         : String_Set.Set;
   Ordered_Paths : String_Vector.Vector;

   procedure Has_Elem (Paths : String_Set.Set; Elem : String; Msg : String);
   procedure Has_Elem (Paths : String_Set.Set; Elem : String; Msg : String) is
   begin
      if Paths.Find (Elem) = String_Set.No_Element then
         Ada.Text_IO.Put_Line (Elem & Msg);
      end if;
   end Has_Elem;

   procedure Compare_Order
     (Ordered_Paths, Right_Order : String_Vector.Vector; Msg : String);
   procedure Compare_Order
     (Ordered_Paths, Right_Order : String_Vector.Vector; Msg : String)
   is
      Wrong_Order : Boolean := True;
   begin
      if Ordered_Paths = Right_Order then
         Wrong_Order := False;
      end if;

      if Wrong_Order then
         Ada.Text_IO.Put_Line (Msg);
         for I in Ordered_Paths.First_Index .. Ordered_Paths.Last_Index loop
            Ada.Text_IO.Put_Line (Ordered_Paths.Element (I));
         end loop;
         Ada.Text_IO.Put_Line ("/=");
         for I in Right_Order.First_Index .. Right_Order.Last_Index loop
            Ada.Text_IO.Put_Line (Right_Order.Element (I));
         end loop;
      end if;
   end Compare_Order;

   Cur_Dir : constant Virtual_File := Get_Current_Dir;
begin
   Parse_Config_File;
   Set_Active (Create ("Projects.Aggregate"), True);
   Initialize (Env);
   Source_Project_Tree.Load (GNATCOLL.VFS.Create (+"aggr.gpr"), Env);

   if Source_Project_Tree.Root_Project = No_Project then
      Ada.Text_IO.Put_Line ("project not loaded");
   end if;

   P := Project_From_Path
     (Source_Project_Tree,
      Create (+"c1/c.gpr"));
   if P = No_Project then
      Ada.Text_IO.Put_Line
        ("can't get c1" & Directory_Separator & "c by path");
   end if;

   P := Project_From_Name (Source_Project_Tree, "a");
   if P = No_Project then
      Ada.Text_IO.Put_Line ("can't get a by name");
   end if;

   --  Source_Dirs for a.gpr
   declare
      Dirs : File_Array := P.Source_Dirs (Recursive => True);
   begin
      for I in Dirs'Range loop
         Paths.Include (+Relative_Path (Dirs (I), Cur_Dir));
      end loop;

      Has_Elem
        (Paths,
         "c1" & Directory_Separator & "src_c" & Directory_Separator,
         " not sourse dir of a.gpr");
      Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c2" & Directory_Separator,
         " not sourse dir of a.gpr");
      Has_Elem
        (Paths,
         "src_a" & Directory_Separator,
         " not sourse dir of a.gpr");

      Paths.Clear;
   end;

   --  Projects for a.gpr
   declare
      Iter : Project_Iterator := Start (P, Recursive => True);
      Project : Project_Type;
   begin
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Paths.Include (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Has_Elem
        (Paths,
         "c1" & Directory_Separator & "c.gpr",
         "c1" & Directory_Separator & "c.gpr not part of a.gpr");
      Has_Elem (Paths, "a.gpr", " not part of a.gpr");

      Paths.Clear;
   end;

   P := Source_Project_Tree.Project_From_Path
     (Create (+"c1/c.gpr"));
   if P = No_Project then
      Ada.Text_IO.Put_Line
        ("can't get c1" & Directory_Separator & "c by path");
   end if;

   --  Projects importing c1/c.gpr
   declare
      Iter    : Project_Iterator :=
        Find_All_Projects_Importing (P);
      Project : Project_Type;
   begin
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Paths.Include (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Has_Elem
        (Paths,
         "b.gpr",
         " not importing c1" & Directory_Separator & "c.gpr");
      Has_Elem
        (Paths,
         "a.gpr",
         " not importing c1" & Directory_Separator & "c.gpr");

      Paths.Clear;
   end;

   P := Source_Project_Tree.Project_From_Path (Create (+"aggr.gpr"));
   if P = No_Project then
      Ada.Text_IO.Put_Line ("can't get aggr by path");
   end if;

   --  Source_Dirs for aggr.gpr
   declare
      Dirs : File_Array := P.Source_Dirs (Recursive => True);
   begin
      for I in Dirs'Range loop
         Paths.Include (+Relative_Path (Dirs (I), Cur_Dir));
      end loop;

      Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c" & Directory_Separator,
         " not sourse dir of aggr.gpr");
      Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c2" & Directory_Separator,
         " not sourse dir of aggr.gpr");
      Has_Elem
        (Paths,
         "c1" & Directory_Separator & "src_c" & Directory_Separator,
         " not sourse dir of aggr.gpr");
      Has_Elem
        (Paths,
         "src_b" & Directory_Separator,
         " not sourse dir of aggr.gpr");
      Has_Elem
        (Paths,
         "src_a" & Directory_Separator,
         " not sourse dir of aggr.gpr");

      Paths.Clear;
   end;

   --  Projects for aggr.gpr
   declare
      Iter    : Project_Iterator;
      Project : Project_Type;

      Right_Order : String_Vector.Vector;

      C1_Str : constant String := "c1" & Directory_Separator & "c.gpr";
      C2_Str : constant String := "c2" & Directory_Separator & "c.gpr";
   begin
      --  Normal order all
      Iter := Start (P, Recursive => True, Direct_Only => False);
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Ordered_Paths.Append (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Right_Order.Append (C2_Str);
      Right_Order.Append (C1_Str);
      Right_Order.Append ("b.gpr");
      Right_Order.Append ("a.gpr");
      Right_Order.Append ("aggr.gpr");

      Compare_Order
        (Ordered_Paths,
         Right_Order,
         "wrong normal order of projecs imported by aggr.gpr with "
         & "Direct_Only => False");

      Ordered_Paths.Clear;
      Right_Order.Clear;

      --  Reversed order all
      Iter := Start_Reversed (P, Recursive => True, Direct_Only => False);
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Ordered_Paths.Append (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Right_Order.Append ("aggr.gpr");
      Right_Order.Append (C2_Str);
      Right_Order.Append ("b.gpr");
      Right_Order.Append (C1_Str);
      Right_Order.Append ("a.gpr");

      Compare_Order
        (Ordered_Paths,
         Right_Order,
         "wrong reversed order of projecs imported by aggr.gpr with "
         & "Direct_Only => False");

      Ordered_Paths.Clear;
      Right_Order.Clear;

      --  Normal order direct only
      Iter := Start (P, Recursive => True, Direct_Only => True);
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Ordered_Paths.Append (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Right_Order.Append (C2_Str);
      Right_Order.Append ("b.gpr");
      Right_Order.Append ("a.gpr");
      Right_Order.Append ("aggr.gpr");

      Compare_Order
        (Ordered_Paths,
         Right_Order,
         "wrong normal order of projecs imported by aggr.gpr with "
         & "Direct_Only => True");

      Ordered_Paths.Clear;
      Right_Order.Clear;

      --  Reversed order direct only
      Iter := Start_Reversed (P, Recursive => True, Direct_Only => True);
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Ordered_Paths.Append (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Right_Order.Append ("aggr.gpr");
      Right_Order.Append (C2_Str);
      Right_Order.Append ("b.gpr");
      Right_Order.Append ("a.gpr");

      Compare_Order
        (Ordered_Paths,
         Right_Order,
         "wrong reversed order of projecs imported by aggr.gpr with "
         & "Direct_Only => True");

      Ordered_Paths.Clear;
      Right_Order.Clear;

      --  Importing aggr (should be empty)
      Iter := Find_All_Projects_Importing (P);
      loop
         Project := Current (Iter);
         exit when Project = No_Project;

         Ordered_Paths.Append (+Relative_Path (Project.Project_Path, Cur_Dir));

         Next (Iter);
      end loop;

      Compare_Order
        (Ordered_Paths,
         Right_Order,
         "aggr.gpr should not be imported by any project");

      Ordered_Paths.Clear;
   end;

   P := Project_From_Path
     (Source_Project_Tree,
      Create (+("aggr.gpr")));
   if P = No_Project then
      Ada.Text_IO.Put_Line ("can't get aggr by path");
   end if;

   --  Source_Files for aggr.gpr
   declare
      Files : File_Array_Access := P.Source_Files (Recursive => True);
   begin
      for I in Files'Range loop
         Paths.Include (+Relative_Path (Files (I), Cur_Dir));
      end loop;
   end;

   Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c"
         & Directory_Separator & "whatever.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c"
         & Directory_Separator & "whatever2.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "c2" & Directory_Separator & "src_c2"
         & Directory_Separator & "shared_src.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "c1" & Directory_Separator & "src_c"
         & Directory_Separator & "whatever.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "c1" & Directory_Separator & "src_c"
         & Directory_Separator & "whatever1.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_b" & Directory_Separator & "b1.adb",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_b" & Directory_Separator & "b1.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_b" & Directory_Separator & "c1.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_a" & Directory_Separator & "a1.adb",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_a" & Directory_Separator & "a1.ads",
         " not sourse dir of aggr.gpr");
   Has_Elem
        (Paths,
         "src_a" & Directory_Separator & "c1.ads",
         " not sourse dir of aggr.gpr");

   Paths.Clear;

   Source_Project_Tree.Unload;
   Free (Env);
end Main;
