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

separate (SPARKProgram)
package body Annotations is

   procedure Initialize
     (This        :    out Anno_Type;
      Anno_Intro  : in     String;
      Anno_Succ   : in     String;
      Anno_Indent : in     Natural)
   is
      Start_Str : Anno_Start_Type;
      Loc_Succ  : E_Strings.T;
   begin
      Start_Str := Anno_Start_Type'(1 | 2 => '-',
                                    3     => CommandLineData.Content.Anno_Char);

      if Anno_Succ = "" then
         Loc_Succ := E_Strings.Empty_String;
      else
         Loc_Succ := E_Strings.Copy_String (Str => Anno_Succ);
      end if;

      This :=
        Anno_Type'
        (Anno_Start  => Start_Str,
         Anno_Intro  => E_Strings.Copy_String (Str => Anno_Intro),
         Anno_Succ   => Loc_Succ,
         Anno_Indent => Anno_Indent);
   end Initialize;

   function Intro (This : Anno_Type) return E_Strings.T is
   begin
      return This.Anno_Intro;
   end Intro;

   function Indent (This : Anno_Type) return Natural is
   begin
      return This.Anno_Indent;
   end Indent;

   procedure Is_Anno_Start
     (This       : in     Anno_Type;
      Input_Line : in     E_Strings.T;
      Index      : in out E_Strings.Lengths;
      OK         :    out Boolean)
   is
   begin
      White_Space.Skip (Input_Line => Input_Line,
                        Index      => Index);

      if E_Strings.Get_Length (E_Str => Input_Line) >= Index + 2
        and then E_Strings.Get_Element (E_Str => Input_Line,
                                        Pos   => Index) = This.Anno_Start (1)
        and then E_Strings.Get_Element (E_Str => Input_Line,
                                        Pos   => Index + 1) = This.Anno_Start (2)
        and then E_Strings.Get_Element (E_Str => Input_Line,
                                        Pos   => Index + 2) = This.Anno_Start (3) then
         Index := Index + 3;
         OK    := True;
      else
         OK := False;
      end if;

   end Is_Anno_Start;

   function Starts_With
     (Start_Str     : E_Strings.T;
      Input_Line    : E_Strings.T;
      Initial_Index : E_Strings.Positions)
     return          Boolean
   is
      Index  : E_Strings.Positions;
      Loc_OK : Boolean;
   begin
      Index := Initial_Index;
      White_Space.Skip (Input_Line => Input_Line,
                        Index      => Index);

      Loc_OK := E_Strings.Get_Length (E_Str => Input_Line) > 0
        and then ((E_Strings.Get_Length (E_Str => Input_Line) - Index) + 1) >=
        E_Strings.Get_Length (E_Str => Start_Str);

      if Loc_OK then
         for I in E_Strings.Lengths range 1 .. E_Strings.Get_Length (E_Str => Start_Str) loop
            Loc_OK := Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => Start_Str,
                                                                               Pos   => I)) =
              Ada.Characters.Handling.To_Lower (E_Strings.Get_Element (E_Str => Input_Line,
                                                                       Pos   => (I + Index) - 1));
            exit when not Loc_OK;
         end loop;
      end if;

      return Loc_OK;

   end Starts_With;

   function Is_Start_Of (This       : Anno_Type;
                         Input_Line : E_Strings.T) return Boolean is
      Index  : E_Strings.Positions;
      Result : Boolean;
   begin
      Index := 1;
      Is_Anno_Start (This       => This,
                     Input_Line => Input_Line,
                     Index      => Index,
                     OK         => Result);
      Result := Result and then Starts_With (Start_Str     => This.Anno_Intro,
                                             Input_Line    => Input_Line,
                                             Initial_Index => Index);
      return Result;
   end Is_Start_Of;

   function Is_End_Of (This       : Anno_Type;
                       Input_Line : E_Strings.T) return Boolean is
      Index  : E_Strings.Positions;
      Loc_OK : Boolean;
      Result : Boolean;
   begin
      Index := 1;
      Is_Anno_Start (This       => This,
                     Input_Line => Input_Line,
                     Index      => Index,
                     OK         => Loc_OK);

      if not Loc_OK then
         -- No longer in an annotation and therfore ended
         Result := True;
      else
         -- Otherwise only ended if this is the start of a successive annotation
         if E_Strings.Get_Length (E_Str => This.Anno_Succ) > 0 then
            Result := Starts_With (Start_Str     => This.Anno_Succ,
                                   Input_Line    => Input_Line,
                                   Initial_Index => Index);
         else
            Result := False;
         end if;
      end if;

      return Result;

   end Is_End_Of;

   function Proper_Start_Col (Start_Col : E_Strings.Positions) return E_Strings.Positions is
   begin
      return (Start_Col + Anno_Start_Type'Length) + 1;
   end Proper_Start_Col;

   procedure Write (This      : in Anno_Type;
                    Output    : in SPARK_IO.File_Type;
                    Start_Col : in E_Strings.Positions) is
   begin
      SPARK_IO.Set_Col (Output, Start_Col);
      SPARK_IO.Put_String (Output, This.Anno_Start, 0);
   end Write;

   procedure Write_Intro (This      : in Anno_Type;
                          Output    : in SPARK_IO.File_Type;
                          Start_Col : in E_Strings.Positions) is
   begin
      Write (This      => This,
             Output    => Output,
             Start_Col => Start_Col);
      SPARK_IO.Set_Col (Output, Proper_Start_Col (Start_Col => Start_Col));
      E_Strings.Put_String (File  => Output,
                            E_Str => This.Anno_Intro);
      -- If Indent is not Inline, then don't put out a trailing space,
      -- since this will be rejected by GNAT -gnatyb style check mode.
      if This.Anno_Indent = SparkFormatCommandLineData.Inline then
         SPARK_IO.Put_String (Output, " ", 0);
      end if;
   end Write_Intro;

   function Name1_Start_Col (This      : in Anno_Type;
                             Start_Col : in E_Strings.Positions) return E_Strings.Positions is
      Name_Column : E_Strings.Positions;
   begin
      if This.Anno_Indent /= SparkFormatCommandLineData.Inline then
         -- Start_Col "is --# " but indent value is given from "--#"
         Name_Column := Proper_Start_Col (Start_Col => Start_Col) + (This.Anno_Indent - 1);
      else
         Name_Column := Proper_Start_Col (Start_Col => Start_Col) + (E_Strings.Get_Length (E_Str => This.Anno_Intro) + 1);
      end if;
      return Name_Column;
   end Name1_Start_Col;

end Annotations;
