with Alire.Conditional;
with Alire.Conditional_Trees.TOML_Load;
with Alire.Crates;
with Alire.Properties.Actions;
with Alire.Properties.Bool;
with Alire.Properties.Configurations;
with Alire.Properties.Environment;
with Alire.Properties.Build_Profiles;
with Alire.Properties.Build_Switches;
with Alire.Properties.Future;
with Alire.Properties.Labeled;
with Alire.Properties.Licenses;
with Alire.Properties.Scenarios;
with Alire.Properties.Tests;
with Alire.TOML_Adapters;

package Alire.Properties.From_TOML is

   package Prop_Loader is new Conditional.For_Properties.TOML_Load;

   subtype Property_Loader is Prop_Loader.Static_Loader;

   type Property_Keys is (Actions,
                          Authors,
                          Auto_GPR_With,
                          Build_Profiles,
                          Build_Switches,
                          Configuration,
                          Description,
                          Environment,
                          Executables,
                          GPR_Externals,
                          GPR_Set_Externals,
                          Hint,
                          Licenses,
                          Long_Description,
                          Maintainers,
                          Maintainers_Logins,
                          Name,
                          Notes,
                          Project_Files,
                          Tags,
                          Version,
                          Website,
                          --  Reserved for possible future use
                          Alire_Version,
                          Future,
                          Index_Version,
                          Manifest_Version,
                          Metadata_Version,
                          Mirror,
                          Test -- In 3.0
                          );
   --  These enum values must match the toml key they represent with '-' => '_'

   --  The following array describes which properties are mandatory, depending
   --  on what we are loading.

   Mandatory : array (Crates.Sections, Property_Keys) of Boolean :=

                 (Crates.External_Private_Section =>
                    (others => False),

                  Crates.External_Shared_Section  =>
                    (Description        |
                     Maintainers        |
                     Name   => True,
                     others => False),

                  Crates.Index_Release           =>
                    (Description        |
                     Maintainers        |
                     Name               |
                     Version => True,
                     others  => False),

                  Crates.Local_Release           =>
                    (Description        |
                     Name               |
                     Version => True,
                     others  => False)
                 );

   Recommended : array (Property_Keys) of Boolean :=
                   (Authors  |
                    Licenses |
                    Tags     |
                    Website => True,
                    others  => False);

   --  Cardinalities say if a property must be a single value or an array.
   --  Since we store properties always as elements in a list, we lose this
   --  information after load so we use this not only during loading to enforce
   --  index correctness, but also during exporting to ensure the proper type
   --  (atom/array) is created.

   type Cardinalities is
     (Unique,
      --  Must be a single value
      Multiple
      --  We accept either table or array , but we will always export an array
     );

   Cardinality : constant array (Property_Keys) of Cardinalities :=
                   (Actions            => Multiple,
                    Authors            => Multiple,
                    Auto_GPR_With      => Unique,
                    Build_Profiles     => Unique,
                    Build_Switches     => Unique,
                    Configuration      => Unique,
                    Description        => Unique,
                    Environment        => Unique,
                    Executables        => Multiple,
                    GPR_Externals      => Unique,
                    GPR_Set_Externals  => Unique,
                    Hint               => Unique,
                    Licenses           => Unique,
                    Long_Description   => Unique,
                    Maintainers        => Multiple,
                    Maintainers_Logins => Multiple,
                    Name               => Unique,
                    Notes              => Unique,
                    Project_Files      => Multiple,
                    Version            => Unique,
                    Website            => Unique,
                    Tags               => Multiple,
                    --  Reserved for future use
                    Alire_Version      => Unique,
                    Future             => Multiple,
                    Index_Version      => Unique,
                    Manifest_Version   => Unique,
                    Metadata_Version   => Unique,
                    Mirror             => Multiple,
                    Test               => Multiple);

   type Loader_Array is array (Property_Keys range <>) of Property_Loader;

   --  We use the following arrays to determine which properties may appear
   --  in a manifest section. These loaders will always receive a table of the
   --  form prop_name = prop_value. Dynamic expressions are resolved prior to
   --  dispatching to these loaders, so they need not to care.

   External_Private_Loaders : constant Loader_Array (Property_Keys) :=
                                (Hint   => Labeled.From_TOML'Access,
                                 others => null);
   --  This loader is used for properties common to all external classes, found
   --  in their private [[external]] sections

   External_Shared_Loaders : constant Loader_Array (Property_Keys) :=
                               (Authors            |
                                Description => Labeled.From_TOML'Access,
                                Licenses    =>
                                  Properties.Licenses.From_TOML'Access,
                                Long_Description   |
                                Maintainers        |
                                Maintainers_Logins |
                                Name               |
                                Tags               |
                                Website       => Labeled.From_TOML'Access,
                                others  => null);
   --  This loader is used for properties a manifest containing externals may
   --  provide, shared by all external definitions found therein

   Release_Loaders : constant Loader_Array (Property_Keys) :=
     (Actions        => Properties.Actions.From_TOML'Access,
      Authors        => Labeled.From_TOML'Access,
      Auto_GPR_With  => Bool.From_TOML'Access,
      Build_Profiles => Properties.Build_Profiles.From_TOML'Access,
      Build_Switches => Properties.Build_Switches.From_TOML'Access,
      Description    => Labeled.From_TOML'Access,
      Configuration  =>
        Properties.Configurations.Config_Entry_From_TOML'Access,
      Environment    =>
        Properties.Environment.From_TOML'Access,
      Executables    => Labeled.From_TOML'Access,
      GPR_Externals |
      GPR_Set_Externals
                     => Scenarios.From_TOML'Access,
      Hint           => null,
      Licenses       => Properties.Licenses.From_TOML'Access,
      Long_Description   |
      Maintainers        |
      Maintainers_Logins |
      Name               |
      Notes              |
      Project_Files      |
      Tags               |
      Version            |
      Website        => Labeled.From_TOML'Access,
      Test           => Tests.From_TOML'Access,
      --  Reserved for future use
      Alire_Version    |
      Future           |
      Index_Version    |
      Manifest_Version |
      Metadata_Version |
      Mirror           => Properties.Future.From_TOML'Access
     );
   --  This loader applies to a normal release manifest

   --  The following array determines which properties accept dynamic
   --  expressions, per index semantics. All other properties must be static.

   Is_Dynamic : constant array (Property_Keys) of Boolean
     := (Actions           |
         Build_Profiles    |
         Configuration     |
         Environment       |
         Executables       |
         Future            |
         GPR_Set_Externals |
         Hint              |
         Project_Files     |
         Test              => True,
         others            => False);

   function Loader (From    : TOML_Adapters.Key_Queue;
                    Loaders : Loader_Array;
                    Section : Crates.Sections;
                    Strict  : Boolean)
                    return Conditional.Properties;
   --  Takes a table of mixed properties and dispatches to each concrete
   --  property loader. Takes into account dynamic properties. Indirectly
   --  called from Alire.TOML_Load to load each individual property, with
   --  the appropriate static loaders for the section.

   --  Following functions are wrappers on Loader that conform to the signature
   --  expected by the dynamic expression loader. Merely used to associate the
   --  appropriate section to the Loader.

   use all type Crates.Sections;

   function External_Private_Loader (From   : TOML_Adapters.Key_Queue;
                                     Strict : Boolean)
                                     return Conditional.Properties is
     (Loader
        (From, External_Private_Loaders, External_Private_Section, Strict));

   function External_Shared_Loader (From   : TOML_Adapters.Key_Queue;
                                    Strict : Boolean)
                            return Conditional.Properties is
     (Loader (From, External_Shared_Loaders, External_Shared_Section, Strict));

   function Index_Release_Loader (From   : TOML_Adapters.Key_Queue;
                                  Strict : Boolean)
                                  return Conditional.Properties is
     (Loader (From, Release_Loaders, Index_Release, Strict));

   function Local_Release_Loader (From   : TOML_Adapters.Key_Queue;
                                  Strict : Boolean)
                                  return Conditional.Properties is
     (Loader (From, Release_Loaders, Local_Release, Strict));

   Section_Loaders : constant
     array (Crates.Sections) of access
     function (From : TOML_Adapters.Key_Queue; Strict : Boolean)
     return Conditional.Properties
     := (External_Private_Section => External_Private_Loader'Access,
         External_Shared_Section  => External_Shared_Loader'Access,
         Index_Release            => Index_Release_Loader'Access,
         Local_Release            => Local_Release_Loader'Access);

end Alire.Properties.From_TOML;
