--  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.Streams.Stream_IO;

with Ada.Calendar.Formatting;

with Nomo.Rule_Storages_IO;

with Nomo.Numerics.Times;

with Nomo.File_Properties;

with Nomo.Gen_Time_Log_Header.Reading;

with Nomo.Gen_Types_Index;

with Nomo.Interpreter_Parameters;

package body Nomo.Converter is

   procedure FR_To_Seed (Log_Path  : in String;
                         Seed_Path : in String;
                         Crush     : in Boolean) is
      use Ada.Streams.Stream_IO;
      use File_Properties.Seed;
      use Rule_Storages_IO;

      Title         : String_Header;
      Datas         : Data_Counts;
      Datas_Count   : Count;
      Temp_File     : File_Type;
      Temp_Stream   : Stream_Access;
      Seed_File     : File_Type;
      Seed_Stream   : Stream_Access;
      Log_File      : File_Type;
      Log_Stream    : Stream_Access;
      Byte_Buffer   : Byte;
      Engine_Buffer : Interpreter_Parameters.Engine_Parameters;
      Rule_Size     : Positive;
      Shift_Index   : Count;
   begin
      if Crush then
         Create (Temp_File);
      else
         declare
            use Ada.Calendar.Formatting;
            use Numerics.Times;
            use File_Properties;

            Epoch                 : constant Ada.Calendar.Time := Ada.Calendar.Clock;
            Date                  : constant String := (Image (Epoch) (1 .. 10) & "_" & Image (Epoch) (12 .. 13) & "h" & Image (Epoch) (15 .. 16) & "-");
            Result_Path           : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Log.Full_Rule_Extension'Length);
            Times_Path            : constant String := Result_Path & Log.Full_Time_Extension;
            Times_File            : File_Type;
            Times_Stream          : Stream_Access;
            Number_Of_Input_Types : Natural;
            Number_Of_Types       : Positive;
            Cycle                 : Positive_Time;

            procedure Read_Cycle;

            procedure Read_Cycle is
               package Types_Index is new Gen_Types_Index (Number_Of_Types);
               package Time_Log_Header is new Gen_Time_Log_Header (Types_Index, Types_Index.Type_Index (Number_Of_Input_Types));
               package Time_Log_Header_Reading is new Time_Log_Header.Reading;
            begin
               Time_Log_Header_Reading.Initialize (Times_Stream);
               Positive_Time'Read (Times_Stream, Cycle);
            end Read_Cycle;

         begin
            Open (Times_File, In_File, Times_Path);
            Times_Stream := Stream (Times_File);
            Short_Short_Integer'Read (Times_Stream, Short_Short_Integer (Number_Of_Input_Types));
            Short_Short_Integer'Read (Times_Stream, Short_Short_Integer (Number_Of_Types));
            Read_Cycle;
            Close (Times_File);
            Create (Temp_File, Out_File, Result_Path & "_" & Date & Time'Image (Cycle)(2..Time'Image (Cycle)'Last) & Seed.Extension);
         end;
      end if;
      Temp_Stream := Stream (Temp_File);
      Open (Seed_File, In_File, Seed_Path);
      Seed_Stream := Stream (Seed_File);
      String_Header'Read (Seed_Stream, Title);
      String_Header'Write (Temp_Stream, Title);
      Datas_Count := Index (Temp_File);
      Data_Counts'Read (Seed_Stream, Datas);
      Data_Counts'Write (Temp_Stream, Datas);
      Interpreter_Parameters.Engine_Parameters'Read(Seed_Stream, Engine_Buffer);
      Interpreter_Parameters.Engine_Parameters'Write(Temp_Stream, Engine_Buffer);
      Rule_Size := positive (Datas(Operator_External) - Datas(Rules)) / Engine_Buffer.Number_Of_Rules;
      for I in Index (Seed_File) .. Datas (Rules) - 1 loop
         Byte'Read (Seed_Stream, Byte_Buffer);
         Byte'Write (Temp_Stream, Byte_Buffer);
      end loop;
      Open (Log_File, In_File, Log_Path);
      Log_Stream := Stream (Log_File);
      while not End_Of_File (Log_File) loop
         Byte'Read (Log_Stream, Byte_Buffer);
         Byte'Write (Temp_Stream, Byte_Buffer);
      end loop;
      Close (Log_File);
      if Index (Temp_File) > Datas (Operator_External) then
         Shift_Index := Index (Temp_File) - Datas (Operator_External);
         Datas (Operator_Internal) := Datas (Operator_Internal) + Shift_Index;
         Datas (Scope_External) := Datas (Scope_External) + Shift_Index;
         Datas (Scope_Internal) := Datas (Scope_Internal) + Shift_Index;
      else
         Shift_Index := Datas (Operator_External) - Index (Temp_File);
         Datas (Operator_Internal) := Datas (Operator_Internal) - Shift_Index;
         Datas (Scope_External) := Datas (Scope_External) - Shift_Index;
         Datas (Scope_Internal) := Datas (Scope_Internal) - Shift_Index;
      end if;
      Engine_Buffer.Number_Of_Rules := positive (Index (Temp_File) - Datas(Rules)) / Rule_Size;
      Set_Index (Seed_File, Datas (Operator_External));
      Datas (Operator_External) := Index (Temp_File);
      while not End_Of_File (Seed_File) loop
         Byte'Read (Seed_Stream, Byte_Buffer);
         Byte'Write (Temp_Stream, Byte_Buffer);
      end loop;
      Set_Index (Temp_File, Datas_Count);
      Data_Counts'Write (Temp_Stream, Datas);
      Set_Index (Temp_File, Datas (Engine));
      Interpreter_Parameters.Engine_Parameters'Write(Temp_Stream, Engine_Buffer);
      Close (Seed_File);
      if Crush then
         Open (Seed_File, Out_File, Seed_Path);
         Seed_Stream := Stream (Seed_File);
         Reset (Temp_File);
         Set_Mode (Temp_File, In_File);
         while not End_Of_File (Temp_File) loop
            Byte'Read (Temp_Stream, Byte_Buffer);
            Byte'Write (Seed_Stream, Byte_Buffer);
         end loop;
         Close (Temp_File);
         Close (Seed_File);
      else
         Close (Temp_File);
      end if;
   end FR_To_Seed;

end Nomo.Converter;
