--  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.Text_IO;

with Ada.Streams.Stream_IO;

with Ada.Sequential_IO;

with Nomo.Rule_Storages_IO;

with Nomo.Gen_Factory;

with Nomo.Gen_Rule_Storages.Partial_Printing;

with Nomo.Gen_Rule_Storages.Full_Printing;

with Nomo.Gen_Premises_Index.Arrays.Printing;

with Nomo.Gen_External_Messages.Printing;

with Nomo.Gen_External_Messages.Premises.Printing;

with Nomo.Gen_Time_Log_Header.Reading;

with Nomo.Gen_Types_Index;

with Nomo.File_Properties;

with Nomo.Numerics.Times;

with Nomo.Type_Categories;
use Nomo.Type_Categories;

package body Nomo.Converter.Text is

   procedure PR_To_CSV (Log_Path : in String) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path          : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Partial_Rule_Extension'Length ) & Partial_Time_Extension;
      Times_File          : File_Type;
      Times_Stream        : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types  : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;
            package Printing is new Factory.Rule_Storages.Partial_Printing (Time_Log_Header_Reading.Get_Category);
            package Storages_IO is new Rule_Storages_Io.Gen_Bytes (Partial_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            CVS_Path   : constant String := Log_Path & CVS_Extension;
            CVS_File   : Ada.Text_IO.File_Type;
            Rules_File : Rules_IO.File_Type;
            Rule       : Partial_Rule_Storage;
            Storage    : Storages_IO.Bytes;
            for Storage'Address use Rule'Address;

         begin
            Ada.Text_IO.Create (CVS_File, Ada.Text_IO.Out_File, CVS_Path);
            Printing.CVS.Put_Header (CVS_File);
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Ada.Text_IO.New_Line (CVS_File);
                  Read (Rules_File, Storage);
                  Printing.CVS.Put (CVS_File, Rule, T);
               end loop;
            end loop;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end PR_To_CSV;

   procedure FR_To_CSV (Log_Path : in String) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path            : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Full_Rule_Extension'Length) & Full_Time_Extension;
      Times_File            : File_Type;
      Times_Stream          : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types       : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;

            package External_Messages_Printing is new Factory.External_Messages.Printing;
            package External_Messages_Premises_Printing is new Factory.External_Messages_Premises.Printing;
            package Premises_Index_Arrays_Printing is new Factory.Premises_Index_Arrays.Printing (Time_Log_Header_Reading.Get_Category);

            package Printing is new Factory.Rule_Storages.Full_Printing (Time_Log_Header_Reading.Get_Category);

            package Printing_CVS is new Printing.CVS (External_Messages_Premises_Printing.CVS.Put_Header,
                                                      External_Messages_Premises_Printing.CVS.Put,
                                                      External_Messages_Premises_Printing.CVS.Put_Na,
                                                      Premises_Index_Arrays_Printing.CVS.Put_Header,
                                                      Premises_Index_Arrays_Printing.CVS.Put,
                                                      External_Messages_Printing.CVS.Put_Header,
                                                      External_Messages_Printing.CVS.Put,
                                                      External_Messages_Printing.CVS.Put_NA);

            package Storages_Io is new Rule_Storages_Io.Gen_Bytes (Full_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            CVS_Path   : constant String := Log_Path & CVS_Extension;
            CVS_File   : Ada.Text_IO.File_Type;
            Rules_File : Rules_IO.File_Type;
            Rule       : Full_Rule_Storage;
            Storage    : Storages_Io.Bytes;
            for Storage'Address use Rule'Address;

         begin
            Ada.Text_IO.Create (CVS_File, Ada.Text_IO.Out_File, CVS_Path);
            Printing_CVS.Put_Header (CVS_File);
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Ada.Text_IO.New_Line (CVS_File);
                  Read (Rules_File, Storage);
                  Printing_CVS.Put (CVS_File, Rule, T);
               end loop;
            end loop;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end FR_To_CSV;

   procedure PR_To_XML (Log_Path : in String) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path            : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Partial_Rule_Extension'Length) & Partial_Time_Extension;
      Times_File            : File_Type;
      Times_Stream          : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types       : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;
            package Printing is new Factory.Rule_Storages.Partial_Printing (Time_Log_Header_Reading.Get_Category);
            package Storages_IO is new Rule_Storages_Io.Gen_Bytes (Partial_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            XML_Path   : constant String := Log_Path & CVS_Extension;
            XML_File   : Ada.Text_IO.File_Type;
            Rules_File : Rules_IO.File_Type;
            Rule       : Partial_Rule_Storage;
            Storage    : Storages_IO.Bytes;
            for Storage'Address use Rule'Address;

         begin
            Ada.Text_IO.Create (XML_File, Ada.Text_IO.Out_File, XML_Path);
            Printing.XML.Put_Header (XML_File);
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Read (Rules_File, Storage);
                  Printing.XML.Put (XML_File, Rule, T);
               end loop;
            end loop;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end PR_To_XML;

   procedure FR_To_XML (Log_Path : in String) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path            : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Full_Rule_Extension'Length) & Full_Time_Extension;
      Times_File            : File_Type;
      Times_Stream          : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types       : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;

            package External_Messages_Printing is new Factory.External_Messages.Printing;
            package External_Messages_Premises_Printing is new Factory.External_Messages_Premises.Printing;
            package Premises_Index_Arrays_Printing is new Factory.Premises_Index_Arrays.Printing (Time_Log_Header_Reading.Get_Category);

            package Printing is new Factory.Rule_Storages.Full_Printing (Time_Log_Header_Reading.Get_Category);

            package Printing_XML is new Printing.XML (External_Messages_Premises_Printing.XML.Put,
                                                      Premises_Index_Arrays_Printing.XML.Put,
                                                      External_Messages_Printing.XML.Put);

            package Storages_Io is new Rule_Storages_Io.Gen_Bytes (Full_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            XML_Path   : constant String := Log_Path & XML_Extension;
            XML_File   : Ada.Text_IO.File_Type;
            Rules_File : Rules_IO.File_Type;
            Rule       : Full_Rule_Storage;
            Storage    : Storages_Io.Bytes;
            for Storage'Address use Rule'Address;
         begin
            Ada.Text_IO.Create (XML_File, Ada.Text_IO.Out_File, XML_Path);
            Printing_XML.Put_Header (XML_File);
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Read (Rules_File, Storage);
                  Printing_XML.Put (XML_File, Rule, T);
               end loop;
            end loop;
            if End_Of_File (Rules_File) then
               Printing_XML.Put_Ending (XML_File);
            end if;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end FR_To_XML;

   procedure PR_To_DB (Log_Path : in String;
                       DB_Path  : in String;
                       Create   : in Boolean) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path          : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Partial_Rule_Extension'Length ) & Partial_Time_Extension;
      Times_File          : File_Type;
      Times_Stream        : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types  : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;
            package Printing is new Factory.Rule_Storages.Partial_Printing (Time_Log_Header_Reading.Get_Category);
            package Storages_IO is new Rule_Storages_Io.Gen_Bytes (Partial_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            Rules_File : Rules_IO.File_Type;
            Rule       : Partial_Rule_Storage;
            Storage    : Storages_IO.Bytes;
            for Storage'Address use Rule'Address;

         begin
            if Create then
               Printing.DB.Create (DB_Path);
            else
               Printing.DB.Open (DB_Path);
            end if;
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Read (Rules_File, Storage);
                  Printing.DB.Put (Rule, T);
               end loop;
            end loop;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end PR_To_DB;

   procedure FR_To_DB (Log_Path : in String;
                       DB_Path  : in String;
                       Create   : in Boolean) is
      use File_Properties.Log;
      use Ada.Streams.Stream_IO;
      Times_Path            : constant String := Log_Path (Log_Path'First .. Log_Path'Last - Full_Rule_Extension'Length) & Full_Time_Extension;
      Times_File            : File_Type;
      Times_Stream          : Stream_Access;
      Number_Of_Input_Types : Natural;
      Number_Of_Types       : Positive;

      procedure Read;

      procedure Read 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;

         procedure Read_Rules;

         procedure Read_Rules is

            package Factory is new Gen_Factory (Types_Index,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Premises,
                                                Time_Log_Header_Reading.Get_Maximum_Of_Components,
                                                Types_Index.Type_Index (Number_Of_Input_Types));
            use Factory.Rule_Storages;

            package External_Messages_Printing is new Factory.External_Messages.Printing;
            package External_Messages_Premises_Printing is new Factory.External_Messages_Premises.Printing;
            package Premises_Index_Arrays_Printing is new Factory.Premises_Index_Arrays.Printing (Time_Log_Header_Reading.Get_Category);

            package Printing is new Factory.Rule_Storages.Full_Printing (Time_Log_Header_Reading.Get_Category);

            package Printing_DB is new Printing.DB (External_Messages_Premises_Printing.DB.Put,
                                                    Premises_Index_Arrays_Printing.DB.Put,
                                                    External_Messages_Printing.DB.Put);

            package Storages_Io is new Rule_Storages_Io.Gen_Bytes (Full_Rule_Storage'Size);
            package Rules_IO is new Ada.Sequential_IO (Storages_Io.Bytes);
            use Rules_IO;

            use Numerics.Times;

            T          : Positive_Time;
            Compt      : Natural;
            Rules_File : Rules_IO.File_Type;
            Rule       : Full_Rule_Storage;
            Storage    : Storages_Io.Bytes;
            for Storage'Address use Rule'Address;

         begin
            if Create then
               Printing_DB.Create (DB_Path);
            else
               Printing_DB.Open (DB_Path);
            end if;
            Open (Rules_File, Rules_IO.In_File, Log_Path);
            while not End_Of_File (Rules_File) loop
               Positive_Time'Read(Times_Stream, T);
               Natural'Read(Times_Stream, Compt);
               for I in 1 .. Compt loop
                  Read (Rules_File, Storage);
                  Printing_DB.Put (Rule, T);
               end loop;
            end loop;
         end Read_Rules;

      begin
         Time_Log_Header_Reading.Initialize (Times_Stream);
         Read_Rules;
      end Read;

   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;
   end FR_To_DB;

end Nomo.Converter.Text;
