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;

   Paths         : String_Set.Set;

   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;

   Cur_Dir : constant Virtual_File := Get_Current_Dir;

   Str : String_Access;
begin
   Parse_Config_File;
   Set_Active (Create ("Projects.Aggregate"), True);
--     Set_Active (Create ("Projects"), True);
   Initialize (Env);
   Env.Register_Default_Language_Extension ("C", ".h", ".c");
   Env.Set_Path_From_Gnatls ("gnatls", Str);
   Free (Str);
   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;

   --  Sources

   declare

      Source_Name_1 : constant Filesystem_String := "a1.adb";
      --  Not ambiguous base name.
      Expected_VF_1 : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("src_a" & Directory_Separator & "a1.adb")));

      Source_Name_2 : constant Filesystem_String := "c1.ads";
      --  Ambiguous base name.
      Expected_VF_2_a : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("src_a" & Directory_Separator & "c1.ads")));
      Expected_VF_2_b : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("src_b" & Directory_Separator & "c1.ads")));

      Source_Name_3 : constant Filesystem_String := "shared_src.ads";
      --  Belongs to several projects.
      Expected_VF_3 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c2" & Directory_Separator & "src_c2"
                 & Directory_Separator & "shared_src.ads")));

      Source_Name_4 : constant Filesystem_String := "foo.c";
      --  C file with ambigous base name.
      Expected_VF_4_c1 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c1" & Directory_Separator & "src_c"
                 & Directory_Separator & "foo.c")));
      Expected_VF_4_c2 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c2" & Directory_Separator & "src_c"
                 & Directory_Separator & "foo.c")));

      File     : Virtual_File;
      Info     : File_Info;
      Expected : Virtual_File;
      P, P2    : Project_Type;
   begin

      --  Project => No_Project

      ------------
      -- a1.adb --
      ------------

      File := Create (Source_Project_Tree, Source_Name_1);
      if Expected_VF_1 /= File then
         --  Base name not ambiguous, should get the file.

         Ada.Text_IO.Put_Line
           ("#01 "
            & File.Display_Full_Name
            & " /= " & Expected_VF_1.Display_Full_Name);
      end if;

      ------------
      -- c1.ads --
      ------------

      File := Create (Source_Project_Tree, Source_Name_2);
      if File /= No_File then
         --  Base name ambiguous, no project specified, should get No_File.

         Ada.Text_IO.Put_Line
           ("#02 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      --------------------
      -- shared_src.ads --
      --------------------

      File := Create (Source_Project_Tree, Source_Name_3);
      if File /= Expected_VF_3 then
         --  Belongs to several projects, but is same file, should get it.

         Ada.Text_IO.Put_Line
           ("#03 "
            & File.Display_Full_Name
            & " /= " & Expected_VF_3.Display_Full_Name);
      end if;

      -----------
      -- foo.c --
      -----------

      File := Create (Source_Project_Tree, Source_Name_4);
      if File /= No_File then
         --  Base name ambiguous, no project specified, should get No_File.

         Ada.Text_IO.Put_Line
           ("#04 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      --  Project specified

      ------------
      -- a1.adb --
      ------------

      P := Project_From_Path (Source_Project_Tree, Create (+"a.gpr"));
      File := Create (Source_Project_Tree, Source_Name_1, P);
      if Expected_VF_1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#05 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_1.Display_Full_Name);
      end if;

      ------------
      -- c1.ads --
      ------------

      File := Create (Source_Project_Tree, Source_Name_2, P);
      if Expected_VF_2_a /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#06 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_a.Display_Full_Name);
      end if;

      P := Project_From_Path (Source_Project_Tree, Create (+"b.gpr"));
      File := Create (Source_Project_Tree, Source_Name_2, P);
      if Expected_VF_2_b /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#07 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_b.Display_Full_Name);
      end if;

      ------------------
      -- whatever.ads --
      ------------------
      --  This file is visible through c1/c.gpr (withed by a.gpr), as well as
      --  directly through c2/c.gpr (aggregated by aggr.gpr)

      File := Source_Project_Tree.Create ("whatever.ads");
      if File /= No_File then
         Ada.Text_IO.Put_Line
            ("whatever.ads is ambiguous, but received "
             & File.Display_Full_Name);
      end if;

      P := Source_Project_Tree.Project_From_Name ("a");
      P2 := Source_Project_Tree.Project_From_Path (Create ("c1/c.gpr"));
      Info := P.Create_From_Project ("whatever.ads");
      Expected := Create (Normalize_Pathname (+"c1/src_c/whatever.ads"));

      if Info.File /= Expected then
         Ada.Text_IO.Put_Line
            ("whatever.ads, as seen from a.gpr, is unambiguous, but "
             & Info.File.Display_Full_Name & " /= "
             & Expected.Display_Full_Name);
      elsif Info.Project /= P2 then
         Ada.Text_IO.Put_Line
           ("as seen from a.gpr, whatever.ads has wrong project: "
            & Info.Project.Project_Path.Display_Full_Name & " /= "
            & P2.Project_Path.Display_Full_Name);
      end if;

      --------------------
      -- shared_src.ads --
      --------------------

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c1" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Source_Name_3, P);
      if Expected_VF_3 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#08 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_3.Display_Full_Name);
      end if;

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c2" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Source_Name_3, P);
      if Expected_VF_3 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#09 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_3.Display_Full_Name);
      end if;

      -----------
      -- foo.c --
      -----------

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c1" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Source_Name_4, P);
      if Expected_VF_4_c1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#10 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_4_c1.Display_Full_Name);
      end if;

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c2" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Source_Name_4, P);
      if Expected_VF_4_c2 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#11 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_4_c2.Display_Full_Name);
      end if;

      --  Search for predefined

      File := Create (Source_Project_Tree, "a-assert.adb");
      --  Checking that no exception occurs.
   end;

   --  ALI

   declare

      Object_Name_1 : constant Filesystem_String := "a1.ali";
      --  Not ambiguous base name.
      Expected_VF_1 : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("obj_a" & Directory_Separator & "a1.ali")));

      Object_Name_2 : constant Filesystem_String := "c1.ali";
      --  Ambiguous base name.
      Expected_VF_2_a : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("obj_a" & Directory_Separator & "c1.ali")));
      Expected_VF_2_b : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("obj_b" & Directory_Separator & "c1.ali")));

      Object_Name_3 : constant Filesystem_String := "shared_src.ali";
      --  Belongs to several projects.
      Expected_VF_3_c1 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c1" & Directory_Separator & "obj_c"
                 & Directory_Separator & "shared_src.ali")));
      Expected_VF_3_c2 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c2" & Directory_Separator & "obj_c"
                 & Directory_Separator & "shared_src.ali")));

      Object_Name_4 : constant Filesystem_String := "foo.c.gli";
      --  C file with ambigous base name.
      Expected_VF_4_c1 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c1" & Directory_Separator & "obj_c"
                 & Directory_Separator & "foo.c.gli")));
      Expected_VF_4_c2 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c2" & Directory_Separator & "obj_c"
                 & Directory_Separator & "foo.c.gli")));

      File : Virtual_File;
      P    : Project_Type;
   begin

      --  Project => No_Project

      ------------
      -- a1.ali --
      ------------

      File := Create (Source_Project_Tree, Object_Name_1);
      if Expected_VF_1 /= File then
         --  Base name not ambiguous, should get the file.

         Ada.Text_IO.Put_Line
           ("#12 "
            & File.Display_Full_Name
            & " /= " & Expected_VF_1.Display_Full_Name);
      end if;

      ------------
      -- c1.ali --
      ------------

      File := Create (Source_Project_Tree, Object_Name_2);
      if File /= No_File then
         --  Base name ambiguous, no project specified, should get No_File.

         Ada.Text_IO.Put_Line
           ("#13 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      --------------------
      -- shared_src.ali --
      --------------------

      File := Create (Source_Project_Tree, Object_Name_3);
      if File /= No_File then
         --  Belongs to several projects, thus multiple .ali,
         --  should get No_File.

         Ada.Text_IO.Put_Line
           ("#14 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      ---------------
      -- foo.c.gli --
      ---------------

      File := Create (Source_Project_Tree, Object_Name_4);
      if File /= No_File then
         --  Base name ambiguous, no project specified, should get No_File.

         Ada.Text_IO.Put_Line
           ("#15 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      --  Project specified

      ------------
      -- a1.ali --
      ------------

      P := Project_From_Path (Source_Project_Tree, Create (+"a.gpr"));
      File := Create (Source_Project_Tree, Object_Name_1, P);
      if Expected_VF_1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#16 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_1.Display_Full_Name);
      end if;

      ------------
      -- c1.ali --
      ------------

      File := Create (Source_Project_Tree, Object_Name_2, P);
      if Expected_VF_2_a /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#17 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_a.Display_Full_Name);
      end if;

      P := Project_From_Path (Source_Project_Tree, Create (+"b.gpr"));
      File := Create (Source_Project_Tree, Object_Name_2, P);
      if Expected_VF_2_b /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#18 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_b.Display_Full_Name);
      end if;

      --------------------
      -- shared_src.ali --
      --------------------

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c1" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Object_Name_3, P);
      if Expected_VF_3_c1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#19 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_3_c1.Display_Full_Name);
      end if;

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c2" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Object_Name_3, P);
      if Expected_VF_3_c2 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#20 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_3_c2.Display_Full_Name);
      end if;

      ---------------
      -- foo.c.gli --
      ---------------

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c1" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Object_Name_4, P);
      if Expected_VF_4_c1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#21 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_4_c1.Display_Full_Name);
      end if;

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c2" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Object_Name_4, P);
      if Expected_VF_4_c2 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#22 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_4_c2.Display_Full_Name);
      end if;

      --  Search for predefined

      File := Create (Source_Project_Tree, "a-assert.ali");
      --  Checking that no exception occurs.

   end;

   --  Projects

   declare
      Project_Name_1 : constant Filesystem_String := "a.gpr";
      --  Not ambiguous base name.
      Expected_VF_1 : constant Virtual_File :=
        Create
          (Normalize_Pathname (+("a.gpr")));

      Project_Name_2 : constant Filesystem_String := "c.gpr";
      --  Belongs to several projects.
      Expected_VF_2_c1 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c1" & Directory_Separator & "c.gpr")));
      Expected_VF_2_c2 : constant Virtual_File :=
        Create
          (Normalize_Pathname
             (+
                ("c2" & Directory_Separator & "c.gpr")));

      File : Virtual_File;
      P    : Project_Type;
   begin

      --  Project => No_Project

      -----------
      -- a.gpr --
      -----------

      File := Create (Source_Project_Tree, Project_Name_1);
      if Expected_VF_1 /= File then
         --  Base name not ambiguous, should get the file.

         Ada.Text_IO.Put_Line
           ("#23 "
            & File.Display_Full_Name
            & " /= " & Expected_VF_1.Display_Full_Name);
      end if;

      -----------
      -- c.gpr --
      -----------

      File := Create (Source_Project_Tree, Project_Name_2);
      if File /= No_File then
         --  Base name ambiguous, no project specified, should get No_File.

         Ada.Text_IO.Put_Line
           ("#24 "
            & File.Display_Full_Name & " /= No_File");
      end if;

      --  Project specified

      -----------
      -- a.gpr --
      -----------

      P := Project_From_Path (Source_Project_Tree, Create (+"a.gpr"));
      File := Create (Source_Project_Tree, Project_Name_1, P);
      if Expected_VF_1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#25 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_1.Display_Full_Name);
      end if;

      -----------
      -- c.gpr --
      -----------

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c1" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Project_Name_2, P);
      if Expected_VF_2_c1 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#26 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_c1.Display_Full_Name);
      end if;

      P := Project_From_Path
        (Source_Project_Tree,
         Create (+"c2" & Directory_Separator & "c.gpr"));
      File := Create (Source_Project_Tree, Project_Name_2, P);
      if Expected_VF_2_c2 /= File then
         --  Project specified, should get the file.

         Ada.Text_IO.Put_Line
           ("#27 "
            & File.Display_Full_Name
            & " /= " &  Expected_VF_2_c2.Display_Full_Name);
      end if;
   end;

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