-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset 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 General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

--------------------------------------------------------------------------------
--Synopsis:                                                                   --
--                                                                            --
--Procedure to analyse an .SLG file                                           --
--                                                                            --
--Uses SLG_Parser to deconstruct SLG file, finding which rule files used      --
--which had syntax errors, and which VCs proved with which rules.             --
--                                                                            --
--This info stored in temporary files and recovered later in total            --
--------------------------------------------------------------------------------

with FatalErrors;
with PathFormatter;
with SLG_Parser;
with SPARK_IO;

use type SLG_Parser.Log_Status_T;

separate (VCS)
procedure AnalyseSimpLogFile
  (Report_File       : in     SPARK_IO.File_Type;
   Filename          : in     E_Strings.T;
   Rule_Files_Errors : in out SPARK_IO.File_Type;
   Rule_Files_Used   : in out SPARK_IO.File_Type;
   SLG_Error_In_File :    out Boolean)
is

   Rule_Line              : E_Strings.T;
   Rule_File              : E_Strings.T;
   SLG_Directory          : E_Strings.T;
   Rule_File_With_Path    : E_Strings.T;
   Rule                   : E_Strings.T;
   Output_Line            : E_Strings.T;
   VC_Number              : E_Strings.T;
   SLG_Parser_Handle      : SLG_Parser.Log_Info_T;
   SLG_Parser_Status      : SLG_Parser.Log_Status_T;
   SLG_Parser_Rule_Status : SLG_Parser.Log_Status_T;
   SLG_Parser_VC_Status   : SLG_Parser.Log_Status_T;

   Encountered_A_Rule_File : Boolean;
   Encountered_A_Rule      : Boolean;
   Encountered_A_VC        : Boolean;
   Duplicated              : Boolean;

   procedure File_Contains (File     : in out SPARK_IO.File_Type;
                            E_Str    : in     E_Strings.T;
                            Contains :    out Boolean)
   --# global in out FatalErrors.State;
   --#        in out SPARK_IO.File_Sys;
   --# derives Contains,
   --#         SPARK_IO.File_Sys from E_Str,
   --#                                File,
   --#                                SPARK_IO.File_Sys &
   --#         FatalErrors.State from *,
   --#                                E_Str,
   --#                                File,
   --#                                SPARK_IO.File_Sys &
   --#         File              from *,
   --#                                SPARK_IO.File_Sys;
   is
      Status   : SPARK_IO.File_Status;
      Temp_Str : E_Strings.T;
   begin
      -- Reset file to read mode
      SPARK_IO.Reset (File, SPARK_IO.In_File, Status);

      -- Loop over all elements in the file
      -- comparing against the E_Str
      Contains := False;
      if Status = SPARK_IO.Ok then
         while not SPARK_IO.End_Of_File (File) loop
            E_Strings.Get_Line (File  => File,
                                E_Str => Temp_Str);
            if E_Strings.Eq_String (E_Str1 => E_Str,
                                    E_Str2 => Temp_Str) then
               Contains := True;
               exit;
            end if;
         end loop;

         -- Reset file to append mode
         SPARK_IO.Reset (File, SPARK_IO.Append_File, Status);
      end if;

      if Status /= SPARK_IO.Ok then
         FatalErrors.Process (FatalErrors.Could_Not_Open_Input_File, E_Strings.Empty_String);
      end if;
   end File_Contains;

begin -- AnalyseSimpLogFile

   SLG_Error_In_File := False;
   SLG_Parser.Init (Filename, SLG_Parser_Handle, SLG_Parser_Status);

   if SLG_Parser_Status = SLG_Parser.Success then

      SLG_Parser.Find_Rulefiles_Read (SLG_Parser_Handle, SLG_Parser_Status);

      if SLG_Parser_Status = SLG_Parser.Success then
         -- Find path prefix to use for rule files
         SLG_Directory := OSFiling.Dir_Name (Path => Filename);
         SLG_Directory := PathFormatter.Format (SLG_Directory);

         SLG_Parser.Find_Rule_Syntax_Errors (SLG_Parser_Handle, SLG_Parser_Status);

         if SLG_Parser_Status = SLG_Parser.Success then
            -- Store syntax errors
            while SLG_Parser_Status = SLG_Parser.Success loop
               SLG_Parser.Get_Next_Rulefile_Syntax_Error (SLG_Parser_Handle, Rule_File, SLG_Parser_Status);

               if SLG_Parser_Status = SLG_Parser.Success then
                  -- Store the syntax error
                  Rule_File_With_Path := SLG_Directory;
                  E_Strings.Append_Examiner_String (E_Str1 => Rule_File_With_Path,
                                                    E_Str2 => Rule_File);
                  File_Contains (File     => Rule_Files_Errors,
                                 E_Str    => Rule_File_With_Path,
                                 Contains => Duplicated);
                  if not Duplicated then
                     E_Strings.Put_Line (File  => Rule_Files_Errors,
                                         E_Str => Rule_File_With_Path);
                  end if;
               end if;

               -- Otherwise loop will terminate
            end loop;
         end if;

         -- All syntax errors stored, now proceed to rule summary
         SLG_Parser.Find_Rule_Summary (SLG_Parser_Handle, SLG_Parser_Status);

         if SLG_Parser_Status = SLG_Parser.Success then

            Encountered_A_Rule_File := False;
            while SLG_Parser_Status = SLG_Parser.Success loop
               SLG_Parser.Get_Next_Rulefile (SLG_Parser_Handle, Rule_File, SLG_Parser_Status);

               if SLG_Parser_Status = SLG_Parser.Success then
                  -- Add the rulefile to the list of used files
                  Rule_File_With_Path := SLG_Directory;
                  E_Strings.Append_Examiner_String (E_Str1 => Rule_File_With_Path,
                                                    E_Str2 => Rule_File);
                  File_Contains (File     => Rule_Files_Used,
                                 E_Str    => Rule_File_With_Path,
                                 Contains => Duplicated);
                  if not Duplicated then
                     E_Strings.Put_Line (File  => Rule_Files_Used,
                                         E_Str => Rule_File_With_Path);
                  end if;

                  -- Output to the report
                  if not Encountered_A_Rule_File then
                     SPARK_IO.New_Line (Report_File, 1);
                     SPARK_IO.Put_Line (Report_File, "The following user rules were used:", 0);
                     Encountered_A_Rule_File := True;
                  end if;

                  Rule_Line := E_Strings.Copy_String (Str => "from ");
                  E_Strings.Append_Examiner_String (E_Str1 => Rule_Line,
                                                    E_Str2 => Rule_File_With_Path);
                  E_Strings.Put_Line (File  => Report_File,
                                      E_Str => Rule_Line);

                  Encountered_A_Rule     := False;
                  SLG_Parser_Rule_Status := SLG_Parser.Success;
                  while SLG_Parser_Rule_Status = SLG_Parser.Success loop
                     SLG_Parser.Get_Next_Rule (SLG_Parser_Handle, Rule, SLG_Parser_Rule_Status);

                     if SLG_Parser_Rule_Status = SLG_Parser.Success then
                        -- Output rule number to report
                        -- Rule.Length should be < 256 - 20 - 3
                        Encountered_A_Rule := True;
                        Output_Line        := E_Strings.Copy_String (Str => "   ");
                        E_Strings.Append_Examiner_String (E_Str1 => Output_Line,
                                                          E_Str2 => Rule);
                        E_Strings.Append_String (E_Str => Output_Line,
                                                 Str   => " used in proving VCs:");
                        E_Strings.Put_Line (File  => Report_File,
                                            E_Str => Output_Line);

                        Encountered_A_VC     := False;
                        Output_Line          := E_Strings.Copy_String (Str => "      ");
                        SLG_Parser_VC_Status := SLG_Parser.Success;
                        while SLG_Parser_VC_Status = SLG_Parser.Success loop
                           SLG_Parser.Get_Next_VC (SLG_Parser_Handle, VC_Number, SLG_Parser_VC_Status);

                           if SLG_Parser_VC_Status = SLG_Parser.Success then
                              -- Output VC number to report
                              E_Strings.Append_Examiner_String (E_Str1 => Output_Line,
                                                                E_Str2 => VC_Number);
                              E_Strings.Append_String (E_Str => Output_Line,
                                                       Str   => ", ");
                              Encountered_A_VC := True;
                           else
                              -- Remove the comma and replace with full stop
                              Output_Line :=
                                E_Strings.Section
                                (E_Str     => Output_Line,
                                 Start_Pos => 1,
                                 Length    => E_Strings.Get_Length (E_Str => Output_Line) - 2);
                              E_Strings.Append_Char (E_Str => Output_Line,
                                                     Ch    => '.');
                           end if;

                        end loop;

                        -- Must have found a VC
                        if not Encountered_A_VC or SLG_Parser_VC_Status = SLG_Parser.Unexpected_Text then
                           SLG_Error_In_File := True;
                        end if;
                        E_Strings.Put_Line (File  => Report_File,
                                            E_Str => Output_Line);
                     end if;

                  end loop;

                  -- Must have found a rule
                  if not Encountered_A_Rule or SLG_Parser_Rule_Status = SLG_Parser.Unexpected_Text then

                     SLG_Error_In_File := True;
                  end if;
               end if;

            end loop;

            -- Must have found a rulefile
            if not Encountered_A_Rule_File or SLG_Parser_Status = SLG_Parser.Unexpected_Text then

               SLG_Error_In_File := True;
            end if;
         end if;
      end if;

      --# accept Flow, 10, SLG_Parser_Handle, "Modify filehandle to close file";
      SLG_Parser.Finalise (SLG_Parser_Handle);
      --# end accept;

   else
      SLG_Error_In_File := True;
   end if;

   --# accept Flow, 601, Rule_Files_Used, Report_File, "False coupling through SPARK_IO" &
   --#        Flow, 601, Rule_Files_Used, Rule_Files_Errors, "False coupling through SPARK_IO";
end AnalyseSimpLogFile;
