--  Copyright (2008-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
--
--  Buffer program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU Affero General Public License as published
--  by the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  Buffer program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--  GNU Affero General Public License for more details.

--  You should have received a copy of the GNU Affero General Public License
--  along with Buffer program. If not, see <http://www.gnu.org/licenses/>

with Nomo.Interpreter.Premises_Index;

with Nomo.Interpreter.Types_Directory.Relations;

with Nomo.Interpreter.Maximums_Register;

with Nomo.Internal_Messages.Plant;

with Nomo.Interpreter.Premises_Index.Arrays.Plant;

with Nomo.Interpreter.Premises_Index.Arrays.Setting;

with Nomo.Interpreter.External_Messages.Setting;

with Nomo.Interpreter.External_Messages.Premises.Plant;

with Nomo.Interpreter.External_Messages.Premises.Setting;

with Nomo.Interpreter.Plant.Operators;

with Nomo.Numerics.Reals;

with Nomo.Numerics.Informations;

package body Nomo.Interpreter.Storages.Plant is

   procedure Copy_Condition (This                 : not null access Storages.Full_Rule_Storage;
                             Source               : not null access constant Storages.Full_Rule_Storage;
                             Has_External_Premise : in Boolean) is
      use Premises_Index.Arrays.Setting;
      use External_Messages.Setting;
      use External_Messages.Premises.Setting;
   begin
      This.all.Internal_Premises_Number := Source.all.Internal_Premises_Number;
      Set (This.all.Internal_Condition, Source.all.Internal_Condition, This.all.Internal_Premises_Number);
      Set (This.all.Internal_Condition_Types, Source.all.Internal_Condition_Types, This.all.Internal_Premises_Number);
      if Has_External_Premise then
         This.all.External_Conclusion_Size := Source.all.External_Conclusion_Size;
         This.all.External_Condition_Type :=  Source.all.External_Condition_Type;
         Set (This.all.External_Condition, Source.all.External_Condition);
         Set (External_Message (This.all.External_Conclusion), Source.all.External_Conclusion, This.all.External_Conclusion_Size);
      end if;
   end Copy_Condition;

   function Internal_Condition_Is_Empty (This : not null access constant Storages.Full_Rule_Storage) return Boolean is
   begin
      return This.all.Internal_Premises_Number = 0;
   end Internal_Condition_Is_Empty;

   function Internal_Condition_Is_Full (This : not null access constant Storages.Full_Rule_Storage)  return Boolean is
      use Premises_Index;
   begin
      return This.all.Internal_Premises_Number = Premise_Index'Last;
   end Internal_Condition_Is_Full;

   function Has_Input_Premise (This : not null access constant Storages.Full_Rule_Storage;
                               Index  : in Perception_Type_Index) return Boolean is
      use Types_Directory.Relations;
      use Types_Index.Types_Index_Instance;
   begin
      return This.all.External_Condition_Type /= Get_Input_Type (Index);
   end Has_Input_Premise;

   procedure Reset (This : not null access Storages.Full_Rule_Storage) is
   begin
      This.all.Internal_Premises_Number := 0;
   end Reset;

   procedure Set_Internal_Conclusion (This                : in out Storages.Full_Rule_Storage;
                                      Index               : in Internal_Type_Index;
                                      Internal_Conclusion : in Internal_Message;
                                      Time_Span           : in Negative_Time_Interval;
                                      Operator            : in Positive;
                                      Times_Span          : in Premises_Time_Span;
                                      Properties          : in Premises_Property) is
      use Internal_Messages.Plant;
      use Premises_Index.Arrays.Plant;
      use External_Messages.Premises.Plant;
      use Interpreter.Plant.Operators;

      procedure Update_Property (I : in Time_Interval;
                                 P : in out Premise_Property);

      procedure Update_Property (I : in Time_Interval;
                                 P : in out Premise_Property) is
      begin
         if I < 0 then
            if P = Excitatory_Evidence then
               P := Excitatory_Intention;
            elsif P = Inhibitory_Evidence then
               P := Inhibitory_Intention;
            end if;
         else
            if P = Excitatory_Intention then
               P := Excitatory_Evidence;
            elsif P = Inhibitory_Intention then
               P := Inhibitory_Evidence;
            end if;
         end if;
      end Update_Property;

      Buffer_Properties : Premises_Property := Properties;

   begin
      if This.Internal_Premises_Number > 0 then
         Set_Times_Span (This.Internal_Condition,
                         This.Internal_Premises_Number,
                         Premises_Index.Arrays.Plant.Premises_Times_Span (Times_Span));
         for I in 1 .. Positive (This.Internal_Premises_Number) loop
            Update_Property (Times_Span (I), Buffer_Properties (I));
         end loop;
         Sort (This.Internal_Condition,
               This.Internal_Premises_Number,
               This.Internal_Condition_Types,
               This.Internal_Condition_Properties,
               Premises_Index.Arrays.Plant.Premises_Property (Buffer_Properties));
      end if;
      Set_Time_Span (Internal_Messages.Internal_Message (This.Internal_Conclusion), Time_Span);
      if Internal_Operators_With_Parameters(Operator).Information_Imposed then
         Set_Information (Internal_Messages.Internal_Message (This.Internal_Conclusion), Internal_Operators_With_Parameters(Operator).Information_Value);
      else
         Set_Information (Internal_Messages.Internal_Message (This.Internal_Conclusion), Get_Information (Internal_Conclusion));
      end if;
      This.Conclusion_Type := Index;
      This.Relevance := Internal_Operators_With_Parameters(Operator).Relevance;
      This.Id := Maximums_Register.Incrementation.Get_New_Rule_Id;
      This.Fitting_Nbr := Internal_Operators_With_Parameters(Operator).Fitting_Nbr;
      Adjust_Trend (This.Internal_Condition, This.Internal_Premises_Number, This.Fitting_Nbr);
      if Index in Perception_Type_Index then
         Adjust_Trend (This.External_Condition, This.Fitting_Nbr);
      end if;
   end Set_Internal_Conclusion;

   procedure Set_Internal_Condition (This               : not null access Storages.Full_Rule_Storage;
                                     Index              : in Internal_Type_Index;
                                     Internal_Condition : in Internal_Message;
                                     Operator           : in Positive) is
      use Internal_Messages.Plant;
      use Premises_Index.Arrays.Plant;
      use Premises_Index.Arrays.Setting;
      use Interpreter.Plant.Operators;
      Credibility : Numerics.Reals.Real_0_To_1;
      Information : Numerics.Informations.Positive_Integer;
   begin
      This.all.Internal_Premises_Number := This.all.Internal_Premises_Number + 1;
      if Internal_Operators_With_Parameters (Operator).Credibility_Imposed then
         Credibility := Internal_Operators_With_Parameters (Operator).Credibility_Value;
      else
         Credibility := Get_Credibility (Internal_Condition);
      end if;
      if Internal_Operators_With_Parameters (Operator).Information_Imposed then
         Information := Internal_Operators_With_Parameters (Operator).Information_Value;
      else
         Information := Get_Information (Internal_Condition);
      end if;
         Set (This.all.Internal_Condition,
              This.all.Internal_Premises_Number,
              Information,
              Internal_Operators_With_Parameters(Operator).Information_Tolerance,
              0,
              Internal_Operators_With_Parameters(Operator).Time_Span_Tolerance,
              Credibility,
              Internal_Operators_With_Parameters(Operator).Credibility_Tolerance);
      Set (This.all.Internal_Condition_Types, This.all.Internal_Premises_Number, Index);
   end Set_Internal_Condition;

   procedure Set_External_Condition (This               : not null access Storages.Full_Rule_Storage;
                                     Index              : in Input_Type_Index;
                                     External_Condition : in External_Message;
                                     Operator           : in Positive) is
      use External_Messages.Premises.Plant;
      use Types_Directory.Relations;
      use External_Messages.Setting;
      Size : Component_Index renames Get_Data_Size(Index);
   begin
      Set (This.all.External_Condition, External_Condition, Size, Operator);
      Set (This.all.External_Conclusion, External_Condition, Size);
      This.all.External_Conclusion_Size := Size;
      This.all.External_Condition_Type := Index;
   end Set_External_Condition;

end Nomo.Interpreter.Storages.Plant;
