--
--  Copyright (C) 2022-2024, AdaCore
--
--  SPDX-License-Identifier: Apache-2.0 WITH LLVM-Exception
--

--  Source iterator for View DataBase.
--
--  The goal here is to have an iterator rather than just a function returning
--  the list, because this list can be very large so the function call could,
--  if not inlined, cause unnecessary copies of a large object (the more
--  sources, the larger.
--
--  Also, this allows to have sorted/unsorted iteration while still maintening
--  the list as a hashed container for efficiency of source loading.

with Ada.Iterator_Interfaces;
with Ada.Containers.Indefinite_Holders;
with Ada.Finalization;

with GPR2.Build.View_Db;

private with Ada.Containers.Indefinite_Ordered_Maps;
private with GPR2.Build.View_Tables;

package GPR2.Build.Source.Sets is

   type Object is tagged private
     with Constant_Indexing => Constant_Reference,
          Default_Iterator  => Iterate,
          Iterator_Element  => Source.Object;
   --  Represents the list of sources contained in a single View_Db. This
   --  Set cannot be used as a generic holder for sources. See
   --  GPR2.Build.Source.Lists for such container.

   Empty_Set : constant Object;

   type Source_Set_Option is
     (Unsorted,
      --  Iterate over the view db's hashed map: result is fast but unsorted
      Sorted,
      --  Use a local sorted container: more memory footprint but result is
      --  sorted
      Recurse
      --  Lookup the view's sources together with the view's visibility
      --  closure. Has to use a local container, so the result will be also
      --  sorted alphabetically.
     );

   type Filter_Data is interface;

   type Dummy_Filter_Data is new Filter_Data with null record;
   No_Data : Dummy_Filter_Data;

   type Filter_Function is access
     function (View   : GPR2.Project.View.Object;
               Source : GPR2.Build.Source_Base.Object'Class;
               Data   : Filter_Data'Class) return Boolean;
   --  Function that can be used to filter sources from the set.
   --  Must return True if the source is to be kept, false otherwise.
   --  The filter_data interface can be used to pass parameters to the
   --  filter.

   function Create
     (Db     : Build.View_Db.Object;
      Option : Source_Set_Option := Unsorted;
      Filter : Filter_Function := null;
      F_Data : Filter_Data'Class := No_Data) return Object;
   --  Create a source iterator set representing the sources stored in the
   --  view db object.
   --  If sorted is set, the iterated list is sorted alphabetically (but doing
   --  so is slower).
   --  If filter is set, the iterated list will only contain the sources that
   --  the filter allows.
   --  Important note: the Filter parameter is used by the returned object, so
   --  cannot be a nested subprogram, as filter may be called out of its
   --  nested context.

   --  Iterator

   type Cursor is private;

   No_Element : constant Cursor;

   function Element (Position : Cursor) return Source.Object;

   function Has_Element (Position : Cursor) return Boolean;

   package Source_Iterators is
     new Ada.Iterator_Interfaces (Cursor, Has_Element);

   function Constant_Reference
     (Self     : aliased Object;
      Position : Cursor) return Source.Object;

   function Is_Empty (Self : Object) return Boolean;

   function Iterate
     (Self : Object) return Source_Iterators.Forward_Iterator'Class;

private

   use GPR2.Build.View_Tables;

   package Path_Source_Maps is new Ada.Containers.Indefinite_Ordered_Maps
     (Simple_Name,
      GPR2.Build.View_Tables.Source_Proxy,
      "<" => GPR2."<",
      "=" => GPR2.Build.View_Tables."=");

   type Cursor (From_View_Db : Boolean := False) is record
      Db : Build.View_Db.Object;

      case From_View_Db is
         when True =>
            --  we iterate directly the view db's "Sources" list
            Current_Src   : Basename_Source_Maps.Cursor;
         when False =>
            --  we iterate the list in the iterator
            Current_Path  : Path_Source_Maps.Cursor;
      end case;
   end record;

   No_Element : constant Cursor := (others => <>);

   package Filter_Data_Holders is new Ada.Containers.Indefinite_Holders
     (Filter_Data'Class);

   type Object is tagged record
      Db     : Build.View_Db.Object;
      Option : Source_Set_Option := Unsorted;
      Filter : Filter_Function;
      F_Data : Filter_Data_Holders.Holder;
   end record;

   Empty_Set : constant Object := (others => <>);

   type Source_Iterator (From_View_Db : Boolean) is
     new Ada.Finalization.Controlled and Source_Iterators.Forward_Iterator
   with record
      --  we keep a reference to the view db for faster retrieval of
      --  the source items
      Db : Build.View_Db.Object;

      case From_View_Db is
         when False =>
            --  we have our own list of source proxy
            Paths  : Path_Source_Maps.Map;
         when True =>
            Filter : Filter_Function;
      end case;
   end record;

   overriding function First (Self : Source_Iterator) return Cursor;
   overriding function Next
     (Self     : Source_Iterator;
      Position : Cursor) return Cursor;
   overriding procedure Finalize (Self : in out Source_Iterator);

end GPR2.Build.Source.Sets;
