-------------------------------------------------------------------------------
-- (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 Ada.Exceptions;
with Ada.Command_Line;
with CommandLineData;
with Dictionary;
with ErrorHandler;
with E_Strings;
with GNAT.Traceback.Symbolic;
with ScreenEcho;
with SparkFormatCommandLineData;
with SparkFormatCommandLineHandler;
with SparkLex;
with SPARKProgram;
with SPARK_IO;
with Version;

use type SPARK_IO.File_Status;

--# inherit CommandLineData,
--#         Dictionary,
--#         ErrorHandler,
--#         E_Strings,
--#         LexTokenManager,
--#         SparkFormatCommandLineData,
--#         SparkFormatCommandLineHandler,
--#         SparkLex,
--#         SPARKProgram,
--#         SPARK_IO,
--#         Statistics,
--#         Version;

--# main_program
procedure SPARKFormat
--# global in out ErrorHandler.Error_Context;
--#        in out LexTokenManager.State;
--#        in out SPARK_IO.File_Sys;
--#        in out Statistics.TableUsage;
--#           out CommandLineData.Content;
--#           out Dictionary.Dict;
--#           out SparkFormatCommandLineData.Content;
--#           out SparkLex.Curr_Line;
--# derives CommandLineData.Content,
--#         Dictionary.Dict,
--#         SparkFormatCommandLineData.Content from LexTokenManager.State,
--#                                                 SPARK_IO.File_Sys &
--#         ErrorHandler.Error_Context,
--#         LexTokenManager.State,
--#         SPARK_IO.File_Sys,
--#         Statistics.TableUsage              from *,
--#                                                 ErrorHandler.Error_Context,
--#                                                 LexTokenManager.State,
--#                                                 SPARK_IO.File_Sys &
--#         SparkLex.Curr_Line                 from ErrorHandler.Error_Context,
--#                                                 LexTokenManager.State,
--#                                                 SPARK_IO.File_Sys;
is
   Input          : SPARK_IO.File_Type;
   Output         : SPARK_IO.File_Type;
   Temporary_File : SPARK_IO.File_Type;
   Status         : SPARK_IO.File_Status;
   Filename       : E_Strings.T;

   procedure Print_Help
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *;
   --
   -- Outputs the usage to the user.
   is
      --# hide Print_Help;
   begin
      --  Exclude is a hidden option do not include it in help text.
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Usage: sparkformat {option} [argument-list]", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Options - all may be abbreviated to the shortest unique prefix", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "option = modes_option | indent_option | defaultfunction_option | help_option |",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "         annotation_option | expansion_option | order_option | noswitch_option",
         0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "modes_option = add_modes_option | noadd_modes_option", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  add_modes_option = " & "-" & "add_modes      - add modes to unmoded global variables.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                     - Default: off", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  noadd_modes_option = " & "-" & "noadd_modes  -  do not add modes to unmoded global",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                     variables.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                     - Default: on", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "indent_option = export_option | import_option | global_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "              | inherit_option | own_option | refinement_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "              | constituent_option | initialization_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "              | separator_option | properties_option", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  export_option = " & "-" & "export_indent       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the export variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                        them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  import_option = " & "-" & "import_indent       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the import variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  global_option = " & "-" & "global_indent       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the global variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  inherit_option = " & "-" & "inherit_indent      - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the package names from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  own_option = " & "-" & "own_indent          - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the own variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "  refinement_option = " & "-" & "refinement_indent", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the own variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "  constituent_option = " & "-" & "constituent_indent", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the constituents from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "  initialization_option = " & "-" & "initialization_indent", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the own variables from --# or keeps ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  separator_option = " & "-" & "separator_indent - specifies the degree of indentation of",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       the separators from and & from --# or ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       keeps them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  properties_option = " & "-" & "properties_indent - specifies the degree of indentation",
         0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "                                       of own variable properties from --# or ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       keeps them inline.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                       - Default: inline", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "defaultfunction_option = " & "-" & "default_function_modes = in_mode | unmoded  ",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                       - when used in conjunction with /add_modes, force ", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                       global variables of functions to the specified ", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                       default function mode. ", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                       - Default: unmoded", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "help_option = help_option | version_option", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "  help_option = " & "-" & "help       - print off help information.", 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "  version_option = " & "-" & "version - print off version information.", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "annotation_option = " & "-" & "annotation_character - specify annotation character.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                                          - Default: #", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "expansion_option = expand_option | compress_option", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  expand_option = " & "-" & "expand     - expands the dependency relations.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                              - Default: off", 0);
      SPARK_IO.Put_Line
        (SPARK_IO.Standard_Output,
         "  compress_option = " & "-" & "compress - compresses the dependency relations.",
         0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "                              - Default: on", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "noswitch_option = -noswitch   - ignore spark.sw file", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);

      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "argument-list = file-spec { separator file-spec } - File(s) to reformat", 0);
      SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line1, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line2, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line3, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Support_Line4, 0);
   end Print_Help;

   procedure Print_Version
   --# global in out SPARK_IO.File_Sys;
   --# derives SPARK_IO.File_Sys from *;
   --
   -- Outputs the usage to the user.
   is
      --# hide Print_Version;
   begin
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "SPARKFormat " & Version.Toolset_Banner_Line, 0);
      SPARK_IO.Put_Line (SPARK_IO.Standard_Output, Version.Toolset_Copyright, 0);
   end Print_Version;

begin

   CommandLineData.Initialize;

   -- Allow SPARK95 mode
   --# accept w, 169, CommandLineData.Content.Language_Profile, "Direct update OK here.";
   CommandLineData.Content.Language_Profile := CommandLineData.SPARK95;
   --# end accept;

   -- Always allow FDL reserved words as identifiers. Leave it to the Examiner
   -- to reject them later if required.
   --# accept w, 169, CommandLineData.Content.FDL_Reserved, "Direct update OK here.";
   CommandLineData.Content.FDL_Reserved := False;
   --# end accept;

   Dictionary.Initialize (Write_To_File => False);

   SparkFormatCommandLineData.Initialize;
   SparkFormatCommandLineHandler.Process;
   ErrorHandler.Spark_Make_Init;
   SparkLex.Clear_Line_Context;

   if SparkFormatCommandLineData.Content.Valid then

      if SparkFormatCommandLineData.Content.Help then

         Print_Help;

      elsif SparkFormatCommandLineData.Content.Version then

         Print_Version;

      elsif SparkFormatCommandLineData.Content.Number_Source > 0 then

         for SourceFile in SparkFormatCommandLineData.Source_File_Counts range
           1 .. SparkFormatCommandLineData.Content.Number_Source loop

            Filename := SparkFormatCommandLineData.Content.Source_File_List (SourceFile).Source_Filename;
            Input    := SPARK_IO.Null_File;
            E_Strings.Open
              (File         => Input,
               Mode_Of_File => SPARK_IO.In_File,
               Name_Of_File => Filename,
               Form_Of_File => "",
               Status       => Status);
            if Status /= SPARK_IO.Ok then
               SPARK_IO.Put_String (SPARK_IO.Standard_Output, "Can't open ", 0);
               E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                   E_Str => Filename);
            else
               Temporary_File := SPARK_IO.Null_File;

               --# accept Flow_Message, 10, Status, "Status is ignored";
               SPARK_IO.Create (Temporary_File, 0, "", "", Status);
               -- don't know what we can do if we can't create the temporary file
               --# end accept;

               SPARKProgram.Reformat_Annotations (Input  => Input,
                                                  Output => Temporary_File);

               --# accept Flow_Message, 10, Status, "Status is ignored" &
               --#        Flow_Message, 10, Input, "File 'Input'  is closed";
               SPARK_IO.Close (Input, Status);
               -- don't know what we can do if we can't close the input file
               --# end accept;

               --# accept Flow_Message, 10, Status, "Status is ignored";
               SPARK_IO.Reset (Temporary_File, SPARK_IO.In_File, Status);
               -- don't know what we can do if we can't reset the temporary file
               --# end accept;

               Output := SPARK_IO.Null_File;
               E_Strings.Create
                 (File         => Output,
                  Name_Of_File => SparkFormatCommandLineData.Content.Source_File_List (SourceFile).Source_Filename,
                  Form_Of_File => "",
                  Status       => Status);
               if Status /= SPARK_IO.Ok then
                  SPARK_IO.Put_String (SPARK_IO.Standard_Output, "Can't write to ", 0);
                  E_Strings.Put_Line (File  => SPARK_IO.Standard_Output,
                                      E_Str => Filename);
               else
                  SPARKProgram.Copy (Temporary_File, Output);
               end if;
            end if;

         end loop;

      else
         SPARKProgram.Reformat_Annotations (Input  => SPARK_IO.Standard_Input,
                                            Output => SPARK_IO.Standard_Output);
      end if;

   end if;

exception
   --# hide SPARKFormat;
   when E : others =>
      ScreenEcho.New_Line (1);
      ScreenEcho.Put_Line ("Unexpected internal error in SPARKFormat");
      ScreenEcho.New_Line (1);
      ScreenEcho.Put_Line (Version.Toolset_Support_Line1);
      ScreenEcho.Put_Line (Version.Toolset_Support_Line2);
      ScreenEcho.Put_Line (Version.Toolset_Support_Line3);
      ScreenEcho.Put_Line (Version.Toolset_Support_Line4);
      ScreenEcho.New_Line (1);
      ScreenEcho.Put_Line ("Exception information:");
      ScreenEcho.Put_Line (Ada.Exceptions.Exception_Information (E));
      ScreenEcho.Put_Line ("Traceback:");
      ScreenEcho.Put_Line (GNAT.Traceback.Symbolic.Symbolic_Traceback (E));
      Ada.Command_Line.Set_Exit_Status (9);

end SPARKFormat;
