--  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 Ada.Calendar.Formatting;

with Ada.Sequential_IO;

with Ada.Streams.Stream_IO;

with Nomo.Rule_Storages_IO;

with Nomo.File_Properties;

package body Nomo.Gen_Logger is

   Dir      : String (1..256);
   Dir_Last : Natural := 0;

   generic
      Storage_Size           : Positive;
      Times_Log_Extension    : String;
      Storages_Log_Extension : String;
      with procedure Write_Header (Stream : in Ada.Streams.Stream_IO.Stream_Access);
   package Rules_Logger is

      package Storages_IO is new Rule_Storages_Io.Gen_Bytes (Storage_Size);

      package Rule_IO is new Ada.Sequential_Io (Storages_Io.Bytes);

      procedure Finalize (Last_Time : in Time);

      procedure Create (Last_Time : in Positive_Time;
                        Name      : in String);

      procedure Log (Buffer_Ptr : not null access Full_Storage);

      procedure Set_Time (Last_Time : in Positive_Time);

      function Is_Initialized return Boolean;

   private

      Compt        : Natural := 0;
      Rules_File   : Rule_IO.File_Type;
      Times_File   : Ada.Streams.Stream_IO.File_Type;
      Times_Stream : Ada.Streams.Stream_IO.Stream_Access;

   end Rules_Logger;

   package body Rules_Logger is

      procedure Finalize (Last_Time : in Time) is
         use Ada.Streams.Stream_IO;
         use Rule_IO;
      begin
         if Is_Open (Times_File) then
            pragma Assert (Is_Open (Rules_File));
            Time'Write (Times_Stream, Last_Time);
            Natural'Write (Times_Stream, Compt);
            Close (Times_File);
            Close (Rules_File);
         end if;
         pragma Assert (not Is_Open (Rules_File));
      end Finalize;

      procedure Create (Last_Time : in Positive_Time;
                        Name      : in String) is
         use Ada.Streams.Stream_IO;
         use Rule_IO;
         Cycle : constant String := Time'Image (Last_Time)(2..Time'Image (Last_Time)'Last);
      begin
         if Is_Open (Times_File) then
            pragma Assert (Is_Open (Rules_File));
            Close (Times_File);
            Close (Rules_File);
         end if;
         Create (Times_File, Ada.Streams.Stream_IO.Out_File, Dir (1 .. Dir_Last) & Name & Cycle & Times_Log_Extension);
         Times_Stream := Stream (Times_File);
         Write_Header (Times_Stream);
         Create (Rules_File, Rule_IO.Out_File, Dir (1 .. Dir_Last) & Name & Cycle & Storages_Log_Extension);
      end Create;

      procedure Log (Buffer_Ptr : not null access Full_Storage) is
         use Rule_IO;
         Buffer : Storages_IO.Bytes;
         for Buffer'Address use Buffer_Ptr.all'Address;
      begin
         Write (Rules_File, Buffer);
         Compt := Compt + 1;
      end Log;

      procedure Set_Time (Last_Time : in Positive_Time) is
         use Rule_IO;
      begin
         Positive_Time'Write (Times_Stream, Last_Time);
         Natural'Write (Times_Stream, Compt);
         Compt := 0;
      end Set_Time;

      function Is_Initialized return Boolean is
         use Ada.Streams.Stream_IO;
      begin
         return Is_Open (Times_File);
      end Is_Initialized;

   end Rules_Logger;

   use File_Properties.Log;

   package Partial_Logger is new Rules_Logger (Partial_Storage_Size,
                                               Partial_Time_Extension,
                                               Partial_Rule_Extension,
                                               Time_Log_Header_Writing.Write);

   package Full_Logger is new Rules_Logger (Full_Storage'Size,
                                            Full_Time_Extension,
                                            Full_Rule_Extension,
                                            Time_Log_Header_Writing.Write);

   use Ada;
   use Ada.Calendar.Formatting;

   Last_Time : Time := -1;
   Epoch     : constant Calendar.Time := Calendar.Clock;
   Name      : constant String := (Image (Epoch) (1 .. 10) & "_" & Image (Epoch) (12 .. 13) & "h" & Image (Epoch) (15 .. 16) & "-");

   procedure Initialize (Log_Directory : in String) is
   begin
      pragma Assert (Last_Time = -1);
      Dir (Log_Directory'Range) := Log_Directory;
      Dir_Last := Log_Directory'Last;
   end Initialize;

   procedure Finalize is
   begin
      Partial_Logger.Finalize (Last_Time);
      Full_Logger.Finalize (Last_Time);
   end Finalize;

   procedure Log_Partial (Buffer_Ptr : not null access Full_Storage) renames Partial_Logger.Log;

   procedure Log_Full (Buffer_Ptr : not null access Full_Storage) renames Full_Logger.Log;

   procedure Set_Time (T       : in Positive_Time;
                       Context : in File_Flag) is
      use Streams.Stream_IO;
   begin
      if Last_Time = -1 then
         Time_Log_Header_Writing.Initialize (Maximum_Of_Premises, Maximum_Of_Components);
      else
         if Partial_Logger.Is_Initialized then
            Partial_Logger.Set_Time (Last_Time);
         end if;
         if Full_Logger.Is_Initialized then
            Full_Logger.Set_Time (Last_Time);
         end if;
      end if;
      Last_Time := T;
      case Context is
         when No =>
            null;
         when New_Partial_File =>
            Partial_Logger.Create (Last_Time, Name);
         when New_Full_File =>
            Full_Logger.Create (Last_Time, Name);
         when New_Files =>
            Partial_Logger.Create (Last_Time, Name);
            Full_Logger.Create (Last_Time, Name);
         when Temp =>
            Full_Logger.Create (Last_Time, Log_Final_Prefix & Name);
      end case;
   end Set_Time;

   function Get_Directory return String is
   begin
      return Dir (1 .. Dir_Last);
   end Get_Directory;

end Nomo.Gen_Logger;
