--  Copyright (2008-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
--
--  This 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.
--
--  This 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 this program. If not, see <http://www.gnu.org/licenses/>

with Nomo.Numerics.Reals.Elementary_Functions;

with Nomo.Premise_Properties;

with Nomo.Interpreter.Event_Memory.Internal.Discernment;

package body Nomo.Interpreter.Conditions_Base.Evaluation is

   Distance_Max : constant Positive_Real := 90.0; -- Exp(-90.0) = 0.0

   procedure Evaluate_Internal_Exciting_Evidences (This                : in out Condition_Base;
                                                   Inhibitory_Score    : in out Positive_Real;
                                                   Excitatory_Distance : in out Positive_Real) is
      use Event_Memory.Internal.Discernment;
      use Premises_Index.Focus;
      Type_Range  : constant not null access constant Types_Focus := This.Directory.Get_Types_Range (Premise_Properties.Excitatory_Evidence);
      Distance    : Positive_Real;
   begin
      for I in Type_Range.all.First .. Type_Range.all.Last loop
         declare
            Designation : Premises_Focus renames This.Directory.Get_Internal_Premises_Range (I).all;
         begin
            Discern_Exciting_Evidences (Designation.Get_Type_Index,
                                        This.Matchmaker_Ptr,
                                        This.Premises'Access,
                                        Designation,
                                        Distance);
         end;
         if Distance > Distance_Max then
            Inhibitory_Score := 1.0;
            exit;
         else
            Excitatory_Distance := Excitatory_Distance + Distance;
         end if;
      end loop;
   end Evaluate_Internal_Exciting_Evidences;

   procedure Evaluate_Internal_Exciting_Intentions (This                : in out Condition_Base;
                                                    Inhibitory_Score    : in out Positive_Real;
                                                    Excitatory_Distance : in out Positive_Real) is
      use Event_Memory.Internal.Discernment;
      use Premises_Index.Focus;
      Type_Range  : constant not null access constant Types_Focus := This.Directory.Get_Types_Range (Premise_Properties.Excitatory_Intention);
      Distance    : Positive_Real;
   begin
      for I in Type_Range.all.First .. Type_Range.all.Last loop
         declare
            Designation : Premises_Focus renames This.Directory.Get_Internal_Premises_Range (I).all;
         begin
            Discern_Exciting_Intentions (Designation.Get_Type_Index,
                                         This.Matchmaker_Ptr,
                                         This.Premises'Access,
                                         Designation,
                                         Distance);
         end;
         if Distance > Distance_Max then
            Inhibitory_Score := 1.0;
            exit;
         else
            Excitatory_Distance := Excitatory_Distance + Distance;
         end if;
      end loop;
   end Evaluate_Internal_Exciting_Intentions;

   procedure Evaluate_Internal_Inhibiting_Evidences (This             : in Condition_Base;
                                                     Inhibitory_Score : in out Positive_Real) is
      use Event_Memory.Internal.Discernment;
      use Premises_Index.Focus;
      Type_Range  : constant not null access constant Types_Focus := This.Directory.Get_Types_Range (Premise_Properties.Inhibitory_Evidence);
      Score       : Positive_Real;
   begin
      for I in Type_Range.all.First .. Type_Range.all.Last loop
         declare
            Designation : Premises_Focus renames This.Directory.Get_Internal_Premises_Range (I).all;
         begin
            Discern_Inhibiting_Evidences (Designation.Get_Type_Index,
                                          This.Matchmaker_Ptr,
                                          This.Premises'Access,
                                          Designation,
                                          Score);
         end;
         Inhibitory_Score := Inhibitory_Score + Score;
         exit when Inhibitory_Score >= 1.0;
      end loop;
   end Evaluate_Internal_Inhibiting_Evidences;

   procedure Evaluate_Internal_Inhibiting_Intentions (This             : in Condition_Base;
                                                      Inhibitory_Score : in out Positive_Real) is
      use Event_Memory.Internal.Discernment;
      use Premises_Index.Focus;
      Type_Range  : constant not null access constant Types_Focus := This.Directory.Get_Types_Range (Premise_Properties.Inhibitory_Intention);
      Score       : Positive_Real;
   begin
      for I in Type_Range.all.First .. Type_Range.all.Last loop
         declare
            Designation : Premises_Focus renames This.Directory.Get_Internal_Premises_Range (I).all;
         begin
            Discern_Inhibiting_Intentions (Designation.Get_Type_Index,
                                           This.Matchmaker_Ptr,
                                           This.Premises'Access,
                                           Designation,
                                           Score);
         end;
         Inhibitory_Score := Inhibitory_Score + Score;
         exit when Inhibitory_Score >= 1.0;
      end loop;
   end Evaluate_Internal_Inhibiting_Intentions;

   procedure Reset_Score (This : out Condition_Base) is
   begin
      This.Score := 0.0;
      This.Credibility := 0.0;
   end Reset_Score;

   procedure Subtract (This                : in out Condition_Base;
                       Excitatory_Distance : in Positive_Real;
                       Inhibitory_Score    : in Positive_Real) is
      use Numerics.Reals.Elementary_Functions;
      Total_Score : constant Real := Exp (-Excitatory_Distance) - Inhibitory_Score;
   begin
      if Total_Score > Strictly_Positive_Real'First then
         This.Credibility := Total_Score;
         This.Score := Total_Score;
      end if;
      pragma Assert (Total_Score > Strictly_Positive_Real'First or else This.Score = 0.0);
   end Subtract;

   procedure Subtract (This                : in out Condition_Base;
                       Excitatory_Distance : in Positive_Real;
                       Inhibitory_Score    : in Positive_Real;
                       Shift               : in Real_Accurately) is
      use Numerics.Reals.Elementary_Functions;
      Total_Score : constant Real := Exp (-Excitatory_Distance) - Inhibitory_Score;
   begin
      if Total_Score > Strictly_Positive_Real'First then
         This.Credibility := Total_Score;
         This.Score :=  Exp_10 (Shift - This.Specificity_Log) * Total_Score;
      end if;
      pragma Assert (Total_Score > 0.0 or This.Score = 0.0);
   end Subtract;

end Nomo.Interpreter.Conditions_Base.Evaluation;
