with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO;      use Ada.Text_IO;
with GNAT.OS_Lib;      use GNAT.OS_Lib;
with GNAT.Directory_Operations; use GNAT.Directory_Operations;

with Projects.Registry;    use Projects, Projects.Registry;
with Src_Info;             use Src_Info;
with Src_Info.Queries;     use Src_Info.Queries;

with Language_Handlers;       use Language_Handlers;
with Language_Handlers.Glide; use Language_Handlers.Glide;
with Test_Utils;              use Test_Utils;

procedure Find_Ref is

   Tree : Scope_Tree;

   procedure Callback (Node : Scope_Tree_Node; Is_Renaming : Boolean);
   procedure Print_Entity
     (Node : Scope_Tree_Node; First : Boolean; Is_Renaming : Boolean);

   procedure Dump (Entity : Entity_Information);

   ----------
   -- Dump --
   ----------

   procedure Dump (Entity : Entity_Information) is
   begin
      Put (Get_Name (Entity)
           & " ("
           & Get_Declaration_File_Of (Entity)
           & Get_Declaration_Line_Of (Entity)'Img
           & Get_Declaration_Column_Of (Entity)'Img & ")");
   end Dump;

   ------------------
   -- Print_Entity --
   ------------------

   procedure Print_Entity
     (Node : Scope_Tree_Node; First : Boolean; Is_Renaming : Boolean)
   is
      Entity : Entity_Information;
   begin
      Entity := Get_Entity (Node);

      if Is_Renaming then
         Put ("  <Renamed by> ");
      end if;

      if not Is_Renaming
        and then Get_Parent (Node) /= Null_Scope_Tree_Node
      then
         Print_Entity (Get_Parent (Node), False, Is_Renaming);
         Put (" -> ");
         Dump (Entity);
      else
         Dump (Entity);
      end if;

      if First then
         New_Line;
      end if;

      Destroy (Entity);
   end Print_Entity;

   --------------
   -- Callback --
   --------------

   procedure Callback (Node : Scope_Tree_Node; Is_Renaming : Boolean) is
   begin
      Print_Entity (Node, First => True, Is_Renaming => Is_Renaming);
   end Callback;


   Prj_Filename     : String_Access;
   Source_Filename  : String_Access;
   Registry         : aliased Project_Registry;
   Project          : Project_Type;
   Source_Info_List : Src_Info.LI_File_List;
   LI_Unit          : LI_File_Ptr;
   Entity_Name      : String_Access;
   Line, Column     : Natural := 0;
   Status           : Find_Decl_Or_Body_Query_Status;
   Iter             : Entity_Reference_Iterator;
   Print_Scope      : Boolean;
   Parse_All        : Boolean;
   Info             : Entity_Information;
   Handler          : Language_Handler;
   Rename           : Entity_Information;
   Is_Renaming      : Boolean;
   LI_Handle        : LI_Handler;

begin
   Handler := Create_Lang_Handler (Registry'Unrestricted_Access);

   if Argument_Count < 5 or else Argument_Count > 7 then
      Put_Line ("invalid number of arguments");
      Put_Line ("find_ref prj_file src_file entity_name line column [scope]"
                & " [parse_all]");
      Put_Line ("  where scope should be ""true"" if you want to print the");
      Put_Line ("  list of subprograms that reference the entity instead of");
      Put_Line ("  just the line/column");
      return;
   end if;

   Prj_Filename := new String'(Argument (1));
   if not Is_Regular_File (Prj_Filename.all) then
      Put_Line ("No such file: '" & Prj_Filename.all & "'");
      return;
   end if;

   Source_Filename := new String'(Argument (2));
   if not Is_Regular_File (Source_Filename.all) then
      Put_Line ("No such file: '" & Source_Filename.all & "'");
      return;
   end if;

   Entity_Name := new String'(Argument (3));
   Line := Natural'Value (Argument (4));
   Column := Natural'Value (Argument (5));

   Print_Scope := Argument_Count >= 6 and then Argument (6) = "true";
   Parse_All   := Argument_Count >= 7 and then Argument (7) = "true";

   Reset (Source_Info_List);
   Load_Project (Prj_Filename.all, Handler, Registry, Project);

   if Parse_All then
      for L in 1 .. Languages_Count (Glide_Language_Handler (Handler)) loop
         LI_Handle := Get_Nth_Handler (Glide_Language_Handler (Handler), L);
         if LI_Handle /= null then
            Parse_All_LI_Information
              (LI_Handle,
               Source_Info_List,
               Get_Current_Dir,
               Project);
         end if;
      end loop;
   end if;

   Load_LI_File
     (Source_Info_List, Handler, Registry, Source_Filename.all, LI_Unit);

   if LI_Unit /= No_LI_File then
      Find_Declaration
        (Lib_Info           => LI_Unit,
         File_Name          => Source_Filename.all,
         Entity_Name        => Entity_Name.all,
         Line               => Line,
         Column             => Column,
         Entity             => Info,
         Status             => Status);

      if Status = Src_Info.Queries.Overloaded_Entity_Found then
         Put_Line ("Entity " & Entity_Name.all & " is overloaded.");

      elsif Status /= Src_Info.Queries.Success
        and then Status /= Src_Info.Queries.Fuzzy_Match
      then
         Put_Line ("Declaration not found for " & Entity_Name.all);

      else
         if Status = Src_Info.Queries.Fuzzy_Match then
            Put_Line ("Entity not found, approximate match");
         end if;

         Renaming_Of (Source_Info_List, Info, Is_Renaming, Rename);
         if Is_Renaming and then Rename /= No_Entity_Information then
            Put (" <is renaming of>: ");
            Dump (Rename);
            New_Line;
            Destroy (Rename);
         elsif Is_Renaming then
            Put_Line (" <is a renaming of an unknown entity>");
         end if;

         Find_All_References
           (Project, Handler, Info, Source_Info_List, Iter, Project,
            LI_Once => Print_Scope);

         while Get (Iter) /= No_Reference loop
            if not Print_Scope then
               Put_Line ("Ref="
                         & Get_File (Get_Location (Get (Iter)))
                         & Get_Line (Get_Location (Get (Iter)))'Img
                         & Get_Column (Get_Location (Get (Iter)))'Img);
            else
               Tree := Create_Tree (Get_LI (Iter));
               Find_Entity_References
                 (Tree, Info, Callback'Unrestricted_Access);
               Free (Tree);
            end if;

            Next (Handler, Iter, Source_Info_List);
         end loop;

         declare
            E        : Entity_Information;
            Decl_LI  : LI_File_Ptr;
            Sub_Iter : Subprogram_Iterator;
         begin
            Load_LI_File
              (Source_Info_List, Handler, Registry,
               Get_Declaration_File_Of (Info), Decl_LI);

            if Decl_LI = No_LI_File then
               Put_Line ("No LI file for " & Get_Declaration_File_Of (Info));
            else
               Sub_Iter := Get_Subprogram_Parameters (Decl_LI, Info);

               E := Get (Sub_Iter);
               if E /= No_Entity_Information then
                  Put_Line ("Parameters: ");

                  while E /= No_Entity_Information loop
                     Put_Line ("   Param="
                               & Get_Name (E)
                               & " at "
                               & Get_Declaration_File_Of (E)
                               & ':'
                               & Get_Declaration_Line_Of (E)'Img
                               & ':'
                               & Get_Declaration_Column_Of (E)'Img);
                     Destroy (E);
                     Next (Sub_Iter);

                     E := Get (Sub_Iter);
                  end loop;
               end if;
            end if;
         end;

         Destroy (Info);

      end if;
   else
      Put_Line ("LI file not found");
   end if;
end Find_Ref;
