-------------------------------------------------------------------------------
-- (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.Characters.Latin_1;
with Ada.Text_IO;

with GNAT.Command_Line;

with Version;

package body CMD is

   -- helper to workaround GNAT.Command_Line.Getopt special case (TN JB18-019),
   -- this needs to stay in synch with Section_Delimiters parameter in call to
   -- GNAT.Command_Line.Initialize_Option_Scan below
   function Is_Section_Delimiter (S : String) return Boolean is
   begin
      return S = "-sargs" or S = "-zargs" or S = "-vargs" or S = "-rargs";
   end Is_Section_Delimiter;

   function Number_Of_Args_In (Section : in String) return Natural is
      Count : Natural := 0;
   begin
      GNAT.Command_Line.Goto_Section (Section);

      loop
         case GNAT.Command_Line.Getopt ("*") is
            when Ada.Characters.Latin_1.NUL =>
               exit;
            when '*' =>
               -- If the section is empty Getopt ("*") may return the
               -- delimiter itself, this check may be removed if/when
               -- GNAT.Command_Line.Getopt is fixed, see TN JB18-019
               if Count = 0 and then Is_Section_Delimiter (GNAT.Command_Line.Full_Switch) then
                  exit;
               end if;
               Count := Count + 1;
            when others =>
               null;
         end case;
      end loop;
      return Count;
   end Number_Of_Args_In;

   --  Returns the arguments in the specified section of the command
   --  line. The returned array has exactly the number of elements
   --  required (can have no elements).
   procedure Populate_Arg_List (Section  : in     String;
                                Arg_List :    out GNAT.OS_Lib.Argument_List_Access) is
      NA : constant Natural := Number_Of_Args_In (Section);
      subtype Arg_Index is Natural range 1 .. NA;
      subtype Args is GNAT.OS_Lib.Argument_List (Arg_Index);
      I : Positive := 1;
   begin
      Arg_List := new Args'(others => null);
      GNAT.Command_Line.Goto_Section (Section);
      loop
         case GNAT.Command_Line.Getopt ("*") is
            when Ada.Characters.Latin_1.NUL =>
               exit;
            when '*' =>
               -- If the section is empty Getopt ("*") may return the
               -- delimiter itself, this check may be removed if/when
               -- GNAT.Command_Line.Getopt is fixed, see TN JB18-019
               if I = 1 and then Is_Section_Delimiter (GNAT.Command_Line.Full_Switch) then
                  exit;
               end if;
               Arg_List.all (I) := new String'(GNAT.Command_Line.Full_Switch);
               I                := I + 1;
            when others =>
               null;
         end case;
      end loop;
      GNAT.OS_Lib.Normalize_Arguments (Arg_List.all);
   end Populate_Arg_List;

   procedure Process_Command_Line (Switch_Char : in Character) is
   begin
      Valid := True;

      --  See documentation of GNAT.Command_Line in
      --  GNAT library source in g-comlin.ads
      begin
         GNAT.Command_Line.Initialize_Option_Scan
           (Switch_Char              => Switch_Char,
            Stop_At_First_Non_Switch => False,
            Section_Delimiters       => "sargs zargs vargs rargs");
         loop
            case GNAT.Command_Line.Getopt ("a v victor riposte V l e n ns nz t r p= x= z= c=") is
               when Ada.Characters.Latin_1.NUL =>
                  --  Must be start of next section...
                  exit;
               when 'a' =>
                  All_Files := True;
               when 'v' =>
                  if GNAT.Command_Line.Full_Switch = "victor" then
                     Run_Victor := True;
                  else
                     Version_Requested := True;
                  end if;
               when 'V' =>
                  Verbose := True;
               when 't' =>
                  Sort_VCGs := True;
               when 'r' =>
                  if GNAT.Command_Line.Full_Switch = "riposte" then
                     Run_Riposte := True;
                  else
                     Reverse_Order := True;
                  end if;
               when 'l' =>
                  Log_Output := True;
               when 'e' =>
                  Echo_Output := True;
               when 'n' =>
                  if GNAT.Command_Line.Full_Switch = "ns" then
                     Run_Simplifier := False;
                  elsif GNAT.Command_Line.Full_Switch = "nz" then
                     Run_Zombiescope := False;
                  elsif GNAT.Command_Line.Full_Switch = "n" then
                     Dry_Run := True;
                  else
                     null;
                  end if;
               when 'p' =>
                  Processes := Positive'Value (GNAT.Command_Line.Parameter);
               when 'x' =>
                  Simplifier_Exe_Switch := new String'(GNAT.Command_Line.Parameter);
               when 'z' =>
                  ZombieScope_Exe_Switch := new String'(GNAT.Command_Line.Parameter);
               when 'c' =>
                  Victor_Exe_Switch := new String'(GNAT.Command_Line.Parameter);
               when others =>
                  null;
            end case;
         end loop;

         loop
            declare
               S : constant String := GNAT.Command_Line.Get_Argument (Do_Expansion => True);
            begin
               exit when S'Length = 0;
               --  If we get any argument at all, we know the commandline is bad.
               Valid := False;
            end;
         end loop;

         --  If we have more than one thread running it makes no sense to send
         --  simplifier output to the screen.
         if Processes > 1 and Echo_Output then
            Ada.Text_IO.Put_Line ("Simplifier output cannot be echoed to the screen");
            Ada.Text_IO.Put_Line ("when more than 1 concurrent process is used");
            Valid := False;
         end if;

         -- Section arguments here must agree with the strings used in
         -- Is_Section_Delimiter and in call to Initialize_Option_Scan

         -- Pick up the sargs section
         Populate_Arg_List (Section  => "sargs",
                            Arg_List => SArgs);

         -- Pick up the zargs section
         Populate_Arg_List (Section  => "zargs",
                            Arg_List => ZArgs);

         -- Pick up the vargs section
         Populate_Arg_List (Section  => "vargs",
                            Arg_List => VArgs);

         -- Pick up the rargs section
         Populate_Arg_List (Section  => "rargs",
                            Arg_List => RArgs);

      exception
         when GNAT.Command_Line.Invalid_Parameter =>
            Valid := False;
            Ada.Text_IO.Put_Line ("Invalid Parameter " & GNAT.Command_Line.Full_Switch);
         when GNAT.Command_Line.Invalid_Switch =>
            Valid := False;
            Ada.Text_IO.Put_Line ("Invalid Switch " & GNAT.Command_Line.Full_Switch);
      end;
   end Process_Command_Line;

   procedure Usage is
   begin
      Ada.Text_IO.Put_Line ("Usage: sparksimp [-a] [-v] [-V] [-n] [-ns] [-nz] [-t] [-r] [-l] [-e] [-p=N]");
      Ada.Text_IO.Put_Line ("                 [-victor]");
      Ada.Text_IO.Put_Line ("                 [-riposte]");
      Ada.Text_IO.Put_Line ("                 [-x=Sexec] [-z=Zexec] [-c=Vexec]");
      Ada.Text_IO.Put_Line ("                 [-sargs {simplifier_options}]");
      Ada.Text_IO.Put_Line ("                 [-zargs {zombiescope_options}]");
      Ada.Text_IO.Put_Line ("                 [-vargs {victor_options}]");
      Ada.Text_IO.Put_Line ("                 [-rargs {riposte_options}]");
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put_Line ("Options: -a  all - ignore time stamps, so process all eligible files");
      Ada.Text_IO.Put_Line ("         -v  report version and terminate");
      Ada.Text_IO.Put_Line ("         -V  verbose output");
      Ada.Text_IO.Put_Line ("         -n  dry run - print list of files found and then stop");
      Ada.Text_IO.Put_Line ("         -ns do not run the Simplifier");
      Ada.Text_IO.Put_Line ("         -nz do not run ZombieScope");
      Ada.Text_IO.Put_Line ("         -victor run Victor/Alt-Ergo on VCs left unproven by Simplifier");
      Ada.Text_IO.Put_Line ("                 (If -ns is specified then this will run Victor on all VCs.)");
      Ada.Text_IO.Put_Line ("         -riposte run Riposte on VCs left unproven by Simplifier");
      Ada.Text_IO.Put_Line ("                  (If -ns is specified then this will run Riposte on all VCs.)");
      Ada.Text_IO.Put_Line ("         -t  sort VCG files, largest first");
      Ada.Text_IO.Put_Line ("         -r  reverse simplification order");
      Ada.Text_IO.Put_Line ("         -l  log output for XXX.vcg to XXX.log and for YYY.dpc to YYY.zsl");
      Ada.Text_IO.Put_Line ("         -e  echo Simplifier output to screen");
      Ada.Text_IO.Put_Line ("         -p=N use N concurrent processes");
      Ada.Text_IO.Put_Line ("         -x=Sexec, specifies an alternative Simplifier executable");
      Ada.Text_IO.Put_Line ("         -z=Zexec, specifies an alternative ZombieScope executable");
      Ada.Text_IO.Put_Line ("         -c=Vexec, specifies an alternative Victor executable");
      Ada.Text_IO.Put_Line ("         -sargs pass following options in this section to Simplifier");
      Ada.Text_IO.Put_Line ("         -zargs pass following options in this section to ZombieScope");
      Ada.Text_IO.Put_Line ("         -vargs pass following options in this section to Victor");
      Ada.Text_IO.Put_Line ("         -rargs pass following options in this section to Riposte");
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put_Line (Version.Toolset_Support_Line1);
      Ada.Text_IO.Put_Line (Version.Toolset_Support_Line2);
      Ada.Text_IO.Put_Line (Version.Toolset_Support_Line3);
      Ada.Text_IO.Put_Line (Version.Toolset_Support_Line4);

   end Usage;

end CMD;
