--  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 Standard_Random;

with Nomo.Interpreter.Plant.Scopes;

with Nomo.Interpreter.Internal_Events_Index.Registers.Plant;

package body Nomo.Interpreter.Event_Memory.Internal.Plant is

   use Internal_Events_Index.Registers.Plant;

   function Get_Before_Arrival_Time (I        : in Internal_Type_Index;
                                     Position : in Internal_Event_Index) return Positive_Time is
   begin
      return Get_Before_Arrival_Time (This (I), Position);
   end Get_Before_Arrival_Time;

   function Get_First_Arrival_Time (I        : in Internal_Type_Index;
                                    Position : in Internal_Event_Index) return Positive_Time is
   begin
      return Get_First_Arrival_Time (This (I), Position);
   end Get_First_Arrival_Time;

   function Get_Last_Arrival_Time (I        : in Internal_Type_Index;
                                   Position : in Internal_Event_Index) return Positive_Time is
   begin
      return Get_Last_Arrival_Time (This (I), Position);
   end Get_Last_Arrival_Time;

   function Get_After_Arrival_Time (I        : in Internal_Type_Index;
                                   Position : in Internal_Event_Index) return Positive_Time is
   begin
      return Get_After_Arrival_Time (This (I), Position);
   end Get_After_Arrival_Time;

   function Get_Same_Arrival_Time (I        : in Internal_Type_Index;
                                   Position : in Internal_Event_Index) return Positive_Time is
   begin
      return Get_Same_Arrival_Time (This (I), Position);
   end Get_Same_Arrival_Time;

   function Get_Before_Position (I        : in Internal_Type_Index;
                                 Position : in Internal_Event_Index) return Internal_Event_Index is
   begin
      return Get_Before_Position (This (I), Position);
   end Get_Before_Position;

   function Get_First_Position (I        : in Internal_Type_Index;
                                Position : in Internal_Event_Index) return Internal_Event_Index is
   begin
      return Get_First_Position (This (I), Position);
   end Get_First_Position;

   function Get_Last_Position (I        : in Internal_Type_Index;
                               Position : in Internal_Event_Index) return Internal_Event_Index is
   begin
      return Get_Last_Position (This (I), Position);
   end Get_Last_Position;

   function Get_After_Position (I        : in Internal_Type_Index;
                                Position : in Internal_Event_Index) return Internal_Event_Index is
   begin
      return Get_After_Position (This (I), Position);
   end Get_After_Position;

   function Get_Same_Position (I        : in Internal_Type_Index;
                                Position : in Internal_Event_Index) return Internal_Event_Index is
   begin
      return Get_Same_Position (This (I), Position);
   end Get_Same_Position;

   procedure Get_Current_Event (Scope_Index  : in Positive;
                                I            : out Internal_Type_Index;
                                Position     : out Internal_Event_Index;
                                Arrival_Time : out Positive_Time) is
      use Interpreter.Plant.Scopes;
      use Standard_Random;

      type Pointed_Event is record
         Index        : Internal_Type_Index;
         Arrival_Time : Positive_Time;
         Position     : Internal_Event_Index;
      end record;

      Selection : array (1 .. Internal_Scopes (Scope_Index).Last) of Pointed_Event;
      Last      : Positive := 1;
   begin
      Selection(1).Index := Internal_Scopes (Scope_Index).List (1);
      Internal_Events_Index.Registers.Plant.Get_Current_Event (This (Selection (1).Index), Position, Arrival_Time);
      Selection (1).Position := Position;
      Selection (1).Arrival_Time := Arrival_Time;

      for I in 2 .. Selection'Last loop
         Internal_Events_Index.Registers.Plant.Get_Current_Event (This (Internal_Scopes (Scope_Index).List(I)), Position, Arrival_Time);
         if Arrival_Time = Selection (1).Arrival_Time then
            Last := Last + 1;
            Selection (Last).Index := Internal_Scopes (Scope_Index).List(I);
            Selection (Last).Position := Position;
            Selection (Last).Arrival_Time := Arrival_Time;
         elsif Arrival_Time > Selection (1).Arrival_Time then
            Last := 1;
            Selection (1).Index := Internal_Scopes (Scope_Index).List (I);
            Selection (1).Position := Position;
            Selection (1).Arrival_Time := Arrival_Time;
         end if;
      end loop;
      Last := Standard_Random.Random mod Last + 1;
      I := Selection (Last).Index;
      Arrival_Time := Selection (Last).Arrival_Time;
      Position := Selection (Last).Position;
   end Get_Current_Event;

   procedure Get_Event (I             : in Internal_Type_Index;
                        Arrival_Time  : in out Positive_Time;
                        Internal_Data : out Internal_Message) is
   begin
      Get_Event (This (I), Arrival_Time, Internal_Data);
   end Get_Event;

   procedure Get_Event (I             : in Internal_Type_Index;
                        Position      : in Internal_Event_Index;
                        Internal_Data : out Internal_Message) is
   begin
      Get_Event (This (I), Position, Internal_Data);
   end Get_Event;

   procedure Get_Event (I             : in Internal_Type_Index;
                        Position      : in Internal_Event_Index;
                        Arrival_Time  : out Positive_Time;
                        Internal_Data : out Internal_Message) is
   begin
      Get_Event (This (I), Position, Arrival_Time, Internal_Data);
   end Get_Event;

end Nomo.Interpreter.Event_Memory.Internal.Plant;
