-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

with CommandLineData;
with CommandLineHandler;
with E_Strings;
with FileSystem;
with ScreenEcho;
with SystemErrors;
with XMLReport;

separate (Dictionary)
package body TargetData is

   -- Types-------------------------------------------------------------------------------

   type Val_Status is (OK_Val, Missing_Val, Illegal_Val);
   type Val_Sort is (Integer_Val, Real_Val);

   -- Local Subprograms-------------------------------------------------------------------

   procedure Open_File (Data_File : out SPARK_IO.File_Type;
                        File_OK   : out Boolean)
   --# global in     CommandLineData.Content;
   --#        in out SPARK_IO.File_Sys;
   --# derives Data_File,
   --#         File_OK,
   --#         SPARK_IO.File_Sys from CommandLineData.Content,
   --#                                SPARK_IO.File_Sys;
   is
      File_Name        : E_Strings.T;
      File_Spec_Status : FileSystem.Typ_File_Spec_Status;
      File_Status      : SPARK_IO.File_Status;
      Local_File       : SPARK_IO.File_Type;
   begin
      Local_File := SPARK_IO.Null_File;

      --# accept Flow, 10, File_Spec_Status, "Expected ineffective assignment";
      FileSystem.Find_Full_File_Name -- 782 expect flow error File_Spec_Status not used.
        (File_Spec      => CommandLineData.Content.Target_Data_File,
         File_Status    => File_Spec_Status,
         Full_File_Name => File_Name);
      --# end accept;
      E_Strings.Open
        (File         => Local_File,
         Mode_Of_File => SPARK_IO.In_File,
         Name_Of_File => File_Name,
         Form_Of_File => "",
         Status       => File_Status);

      if File_Status = SPARK_IO.Ok then
         File_OK := True;
      else
         File_OK := False;
         ScreenEcho.Put_String ("Cannot open file ");
         if CommandLineData.Content.Plain_Output then
            ScreenEcho.Put_ExaminerLine (FileSystem.Just_File (Fn  => File_Name,
                                                               Ext => True));
         else
            ScreenEcho.Put_ExaminerLine (File_Name);
         end if;
      end if;
      Data_File := Local_File;
      --# accept Flow, 33, File_Spec_Status, "Expected to be neither referenced nor exported";
   end Open_File;

   ------------------------------------------------

   procedure Close_File (Data_File : in out SPARK_IO.File_Type)
   --# global in out SPARK_IO.File_Sys;
   --# derives Data_File,
   --#         SPARK_IO.File_Sys from *,
   --#                                Data_File;
   is
      File_Status : SPARK_IO.File_Status;
   begin
      --# accept Flow, 10, File_Status, "Expected ineffective assignment";
      SPARK_IO.Close (Data_File, File_Status);
      --# accept Flow, 33, File_Status, "Expected to be neither referenced nor exported";
   end Close_File;

   ------------------------------------------------

   procedure Get_String (File : in     SPARK_IO.File_Type;
                         Str  :    out E_Strings.T)
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys,
   --#         Str               from File,
   --#                                SPARK_IO.File_Sys;
   is
      Char_OK : Boolean;
      Ch      : Character;

      procedure Get_Char (File : in     SPARK_IO.File_Type;
                          Ch   :    out Character;
                          OK   :    out Boolean)
      --# global in out SPARK_IO.File_Sys;
      --# derives Ch,
      --#         OK,
      --#         SPARK_IO.File_Sys from File,
      --#                                SPARK_IO.File_Sys;
      is
      begin
         if SPARK_IO.End_Of_File (File) then
            OK := False;
            Ch := ' ';
         elsif SPARK_IO.End_Of_Line (File) then
            SPARK_IO.Skip_Line (File, 1);
            OK := True;
            Ch := ' ';
         else
            SPARK_IO.Get_Char (File, Ch);
            OK := True;
         end if;
      end Get_Char;

   begin --Get_String
      Str := E_Strings.Empty_String;
      --skip leading white space
      loop
         Get_Char (File => File,
                   Ch   => Ch,
                   OK   => Char_OK);
         exit when Ch /= ' ';
         exit when not Char_OK;
      end loop;

      if Char_OK then
         loop
            E_Strings.Append_Char (E_Str => Str,
                                   Ch    => Ch);
            Get_Char (File => File,
                      Ch   => Ch,
                      OK   => Char_OK);
            exit when Ch = ' ';
            exit when not Char_OK;
         end loop;
      end if;
   end Get_String;

   -------------------------------

   procedure Skip_Equals (Data_File : SPARK_IO.File_Type)
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Data_File;
   is
      Unused_String : E_Strings.T;
      pragma Unreferenced (Unused_String);
   begin
      --# accept Flow, 10, Unused_String, "Expected ineffective assignment" &
      --#        Flow, 33, Unused_String, "Expected to be neither referenced nor exported";
      Get_String (File => Data_File,
                  Str  => Unused_String);
   end Skip_Equals;

   --------------------------------

   procedure Get_Data_Value
     (Data_File : in     SPARK_IO.File_Type;
      Sort      : in     Val_Sort;
      Val       :    out LexTokenManager.Lex_String;
      Status    :    out Val_Status)
   --# global in out LexTokenManager.State;
   --#        in out SPARK_IO.File_Sys;
   --# derives LexTokenManager.State,
   --#         Status,
   --#         Val                   from Data_File,
   --#                                    LexTokenManager.State,
   --#                                    Sort,
   --#                                    SPARK_IO.File_Sys &
   --#         SPARK_IO.File_Sys     from *,
   --#                                    Data_File;
   is
      Value_String : E_Strings.T;
      Lex_Val      : LexTokenManager.Lex_String;
      Conv_OK      : Maths.ErrorCode;
      Num          : Maths.Value;
      Is_Negative  : Boolean;
      Dummy_Char   : Character;
   begin --Get_Data_Value
      Skip_Equals (Data_File => Data_File);
      Get_String (File => Data_File,
                  Str  => Value_String);
      if E_Strings.Get_Length (E_Str => Value_String) /= 0 then
         if E_Strings.Get_Element (E_Str => Value_String,
                                   Pos   => 1) = '-' then
            --# accept F, 10, Dummy_Char, "Ineffective assignment here OK";
            E_Strings.Pop_Char (E_Str => Value_String,
                                Char  => Dummy_Char);
            --# end accept;
            Is_Negative := True;
         else
            Is_Negative := False;
         end if;
         LexTokenManager.Insert_Examiner_String (Str     => Value_String,
                                                 Lex_Str => Lex_Val);
         Maths.LiteralToValue (Lex_Val,
                               -- to get
                               Num, Conv_OK);
         if Conv_OK = Maths.NoError then
            if Sort = Integer_Val and not Maths.IsIntegerValue (Num) then
               Val    := LexTokenManager.Null_String;
               Status := Illegal_Val;
            elsif Sort = Real_Val and not Maths.IsRealValue (Num) then
               Val    := LexTokenManager.Null_String;
               Status := Illegal_Val;
            else
               if Is_Negative then
                  Maths.Negate (Num);
               end if;
               Maths.StorageRep (Num, Val);
               Status := OK_Val;
            end if;
         else
            Val    := LexTokenManager.Null_String;
            Status := Illegal_Val;
         end if;
      else
         Val    := LexTokenManager.Null_String;
         Status := Missing_Val;
      end if;
      --# accept F, 33, Dummy_Char, "Dummy_Char not referenced here";
   end Get_Data_Value;

   -----------------------------------------------------------

   procedure Echo_Error (Status : in Val_Status)
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *,
   --#                                Status;
   is
   begin
      case Status is
         when OK_Val =>
            null;
         when Illegal_Val =>
            ScreenEcho.Put_Line ("Illegal value");
         when Missing_Val =>
            ScreenEcho.Put_Line ("Value missing");
      end case;
   end Echo_Error;

   --Exported Subprograms-----------------------------------------------------------------

   procedure Read_Target_Data_File is
      Option    : E_Strings.T;
      File_OK   : Boolean;
      Data_File : SPARK_IO.File_Type;

      procedure Invalid_Option (Opt : E_Strings.T)
      --# global in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                Opt;
      is
      begin
         ScreenEcho.Put_String ("Invalid target compiler data item: ");
         ScreenEcho.Put_ExaminerLine (Opt);
      end Invalid_Option;

      -------------------------------

      procedure Process_Option (Opt : E_Strings.T)
      --# global in     Data_File;
      --#        in out Dictionary.Dict;
      --#        in out LexTokenManager.State;
      --#        in out SPARK_IO.File_Sys;
      --# derives Dictionary.Dict,
      --#         LexTokenManager.State,
      --#         SPARK_IO.File_Sys     from *,
      --#                                    Data_File,
      --#                                    LexTokenManager.State,
      --#                                    Opt,
      --#                                    SPARK_IO.File_Sys;
      is
         Option_Match : Boolean;
         Val          : LexTokenManager.Lex_String;
         Status       : Val_Status;
      begin
         Option_Match := False;
         case E_Strings.Get_Element (E_Str => Opt,
                                     Pos   => 1) is
            when 'i' | 'I' =>
               case E_Strings.Get_Element (E_Str => Opt,
                                           Pos   => 9) is
                  when 'f' | 'F' =>
                     if CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                              Str      => "integer'first") then
                        Get_Data_Value (Data_File => Data_File,
                                        Sort      => Integer_Val,
                                        Val       => Val,
                                        Status    => Status);
                        Echo_Error (Status => Status);
                        RawDict.Set_Type_Lower (Type_Mark => Dictionary.Get_Predefined_Integer_Type,
                                                Lower     => Val);
                        Option_Match := True;
                     end if;
                  when 'l' | 'L' =>
                     if CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                              Str      => "integer'last") then
                        Get_Data_Value (Data_File => Data_File,
                                        Sort      => Integer_Val,
                                        Val       => Val,
                                        Status    => Status);
                        Echo_Error (Status => Status);
                        RawDict.Set_Type_Upper (Type_Mark => Dictionary.Get_Predefined_Integer_Type,
                                                Upper     => Val);
                        RawDict.Set_Type_Upper (Type_Mark => Dictionary.Get_Predefined_Positive_Subtype,
                                                Upper     => Val);
                        RawDict.Set_Type_Upper (Type_Mark => Dictionary.Get_Predefined_Natural_Subtype,
                                                Upper     => Val);
                        Option_Match := True;
                     end if;
                  when others =>
                     null;
               end case;
            when 'l' | 'L' =>
               case E_Strings.Get_Element (E_Str => Opt,
                                           Pos   => 14) is
                  when 'f' | 'F' =>
                     if CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                              Str      => "long_integer'first") then
                        Get_Data_Value (Data_File => Data_File,
                                        Sort      => Integer_Val,
                                        Val       => Val,
                                        Status    => Status);
                        Echo_Error (Status => Status);
                        RawDict.Set_Type_Lower (Type_Mark => Dictionary.Get_Predefined_Long_Integer_Type,
                                                Lower     => Val);
                        Option_Match := True;
                     end if;
                  when 'l' | 'L' =>
                     if CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                              Str      => "long_integer'last") then
                        Get_Data_Value (Data_File => Data_File,
                                        Sort      => Integer_Val,
                                        Val       => Val,
                                        Status    => Status);
                        Echo_Error (Status => Status);
                        RawDict.Set_Type_Upper (Type_Mark => Dictionary.Get_Predefined_Long_Integer_Type,
                                                Upper     => Val);
                        Option_Match := True;
                     end if;
                  when others =>
                     null;
               end case;
            when others =>
               null;
         end case;

         if not Option_Match then
            Invalid_Option (Opt => Opt);
         end if;
      end Process_Option;

      -------------------------------

   begin --Read_Target_Data_File
      if CommandLineData.Content.Target_Data then
         Open_File (Data_File => Data_File,
                    File_OK   => File_OK);
         if File_OK then
            if CommandLineData.Content.Echo and not CommandLineData.Content.Brief then
               ScreenEcho.New_Line (1);
               ScreenEcho.Put_Line ("           Reading target compiler data ...");
            end if;
            loop
               Get_String (File => Data_File,
                           Str  => Option);
               exit when E_Strings.Get_Length (E_Str => Option) = 0;
               Process_Option (Opt => Option);
            end loop;
            --# accept Flow, 10, Data_File, "Expected ineffective assignment";
            Close_File (Data_File => Data_File);
            --# end accept;
         end if;
      end if;
   end Read_Target_Data_File;

   ------------------------------------------------------------------------------

   procedure Output_Target_Data_File (To_File : in SPARK_IO.File_Type) is
      Option    : E_Strings.T;
      File_OK   : Boolean;
      Data_File : SPARK_IO.File_Type;

      --------------------------------

      procedure Invalid_Option (To_File : SPARK_IO.File_Type;
                                Opt     : E_Strings.T)
      --# global in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                Opt,
      --#                                To_File;
      is
      begin
         SPARK_IO.Put_String (To_File, "Invalid target compiler data item: ", 0);
         E_Strings.Put_String (File  => To_File,
                               E_Str => Opt);
         SPARK_IO.Put_Line (To_File, " has been ignored", 0);
      end Invalid_Option;

      -------------------------------

      procedure Process_Option (Opt     : E_Strings.T;
                                To_File : SPARK_IO.File_Type)
      --# global in     CommandLineData.Content;
      --#        in     Data_File;
      --#        in out LexTokenManager.State;
      --#        in out SPARK_IO.File_Sys;
      --#        in out XMLReport.State;
      --# derives LexTokenManager.State from *,
      --#                                    CommandLineData.Content,
      --#                                    Data_File,
      --#                                    Opt,
      --#                                    SPARK_IO.File_Sys,
      --#                                    To_File &
      --#         SPARK_IO.File_Sys     from *,
      --#                                    CommandLineData.Content,
      --#                                    Data_File,
      --#                                    LexTokenManager.State,
      --#                                    Opt,
      --#                                    To_File,
      --#                                    XMLReport.State &
      --#         XMLReport.State       from *,
      --#                                    CommandLineData.Content,
      --#                                    Opt;
      is
         Option_Match : Boolean;
         --Val         : LexTokenManager.Lex_String;
         --Status      : Val_Status;

         procedure Margin
         --# global in     To_File;
         --#        in out SPARK_IO.File_Sys;
         --# derives SPARK_IO.File_Sys from *,
         --#                                To_File;
         is
         begin
            SPARK_IO.Put_String (To_File, "   ", 0);
         end Margin;

         ----------------------------------------

         procedure Separator
         --# global in     To_File;
         --#        in out SPARK_IO.File_Sys;
         --# derives SPARK_IO.File_Sys from *,
         --#                                To_File;
         is
         begin
            SPARK_IO.Put_String (To_File, " = ", 0);
         end Separator;

         ----------------------------------------

         procedure Put_Val (To_File : in SPARK_IO.File_Type;
                            Val     : in LexTokenManager.Lex_String;
                            Status  : in Val_Status)
         --# global in     LexTokenManager.State;
         --#        in out SPARK_IO.File_Sys;
         --# derives SPARK_IO.File_Sys from *,
         --#                                LexTokenManager.State,
         --#                                Status,
         --#                                To_File,
         --#                                Val;
         is
         begin
            case Status is
               when OK_Val =>
                  E_Strings.Put_Line (File  => To_File,
                                      E_Str => Maths.ValueToString (Maths.ValueRep (Val)));
               when Illegal_Val =>
                  SPARK_IO.Put_Line (To_File, "Illegal value - ignored", 0);
               when Missing_Val =>
                  SPARK_IO.Put_Line (To_File, "Value missing - ignored", 0);
            end case;
         end Put_Val;

         function Get_Val (Val    : in LexTokenManager.Lex_String;
                           Status : in Val_Status) return E_Strings.T
         --# global in LexTokenManager.State;
         is
            Tmp_String : E_Strings.T;
         begin
            case Status is
               when OK_Val =>
                  Tmp_String := Maths.ValueToString (Maths.ValueRep (Val));
               when Illegal_Val =>
                  Tmp_String := E_Strings.Copy_String (Str => "Illegal value - ignored");
               when Missing_Val =>
                  Tmp_String := E_Strings.Copy_String (Str => "Value missing - ignored");
            end case;
            return Tmp_String;
         end Get_Val;

         ---------------------------------------

         procedure Output_Value (Data_File : in SPARK_IO.File_Type;
                                 Opt       : in E_Strings.T;
                                 Valid     : in Boolean)
         --# global in     CommandLineData.Content;
         --#        in     To_File;
         --#        in out LexTokenManager.State;
         --#        in out SPARK_IO.File_Sys;
         --#        in out XMLReport.State;
         --# derives LexTokenManager.State from *,
         --#                                    CommandLineData.Content,
         --#                                    Data_File,
         --#                                    Opt,
         --#                                    SPARK_IO.File_Sys,
         --#                                    To_File,
         --#                                    Valid &
         --#         SPARK_IO.File_Sys     from *,
         --#                                    CommandLineData.Content,
         --#                                    Data_File,
         --#                                    LexTokenManager.State,
         --#                                    Opt,
         --#                                    To_File,
         --#                                    Valid,
         --#                                    XMLReport.State &
         --#         XMLReport.State       from *,
         --#                                    CommandLineData.Content,
         --#                                    Opt,
         --#                                    Valid;
         is
            Val    : LexTokenManager.Lex_String;
            Status : Val_Status;
         begin
            if Valid then
               if CommandLineData.Content.XML then
                  Get_Data_Value (Data_File => Data_File,
                                  Sort      => Integer_Val,
                                  Val       => Val,
                                  Status    => Status);
                  XMLReport.Compiler_Item (Item   => Opt,
                                           Val    => Get_Val (Val    => Val,
                                                              Status => Status),
                                           Report => To_File);
               else
                  Margin;
                  E_Strings.Put_String (File  => To_File,
                                        E_Str => Opt);
                  Separator;
                  Get_Data_Value (Data_File => Data_File,
                                  Sort      => Integer_Val,
                                  Val       => Val,
                                  Status    => Status);
                  Put_Val (To_File => To_File,
                           Val     => Val,
                           Status  => Status);
               end if;
            end if;
         end Output_Value;

      begin -- Process_Option
         Option_Match := False;
         case E_Strings.Get_Element (E_Str => Opt,
                                     Pos   => 1) is
            when 'i' | 'I' =>
               case E_Strings.Get_Element (E_Str => Opt,
                                           Pos   => 9) is
                  when 'f' | 'F' =>
                     Option_Match := CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                                           Str      => "integer'first");
                     Output_Value (Data_File => Data_File,
                                   Opt       => Opt,
                                   Valid     => Option_Match);
                  when 'l' | 'L' =>
                     Option_Match := CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                                           Str      => "integer'last");
                     Output_Value (Data_File => Data_File,
                                   Opt       => Opt,
                                   Valid     => Option_Match);
                  when others =>
                     null;
               end case;
            when 'l' | 'L' =>
               case E_Strings.Get_Element (E_Str => Opt,
                                           Pos   => 14) is
                  when 'f' | 'F' =>
                     Option_Match := CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                                           Str      => "long_integer'first");
                     Output_Value (Data_File => Data_File,
                                   Opt       => Opt,
                                   Valid     => Option_Match);
                  when 'l' | 'L' =>
                     Option_Match := CommandLineHandler.Check_Option_Name (Opt_Name => Opt,
                                                                           Str      => "long_integer'last");
                     Output_Value (Data_File => Data_File,
                                   Opt       => Opt,
                                   Valid     => Option_Match);
                  when others =>
                     null;
               end case;
            when others =>
               null;
         end case;

         if not Option_Match then
            Margin;
            Invalid_Option (To_File => To_File,
                            Opt     => Opt);
         end if;
      end Process_Option;

   begin --Output_Target_Data_File
      if CommandLineData.Content.Target_Data then
         Open_File (Data_File => Data_File,
                    File_OK   => File_OK);
         if File_OK then
            if CommandLineData.Content.XML then
               XMLReport.Start_Section (Section => XMLReport.S_Compiler_Data,
                                        Report  => To_File);
               loop
                  Get_String (File => Data_File,
                              Str  => Option);
                  exit when E_Strings.Get_Length (E_Str => Option) = 0;
                  Process_Option (Opt     => Option,
                                  To_File => To_File);
               end loop;
               XMLReport.End_Section (Section => XMLReport.S_Compiler_Data,
                                      Report  => To_File);
               --# accept Flow, 10, Data_File, "Expected ineffective assignment";
               Close_File (Data_File => Data_File);
               --# end accept;
            else
               SPARK_IO.New_Line (To_File, 2);
               SPARK_IO.Put_Line (To_File, "Target compiler data:", 0);
               loop
                  Get_String (File => Data_File,
                              Str  => Option);
                  exit when E_Strings.Get_Length (E_Str => Option) = 0;
                  Process_Option (Opt     => Option,
                                  To_File => To_File);
               end loop;
               --# accept Flow, 10, Data_File, "Expected ineffective assignment";
               Close_File (Data_File => Data_File);
               --# end accept;
            end if; -- XML
         end if;
      end if;
   end Output_Target_Data_File;

end TargetData;
