-------------------------------------------------------------------------------
-- (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;

package body Wrap is

   ---------------------------------------------------------
   --  Most of this code has been borrowed and adapted from
   --  spade/simplifier/utils/wraps.  Functionally, it's
   --  the same as wrap_utility, but this code has been
   --  brought up to date for Ada95, GNAT, and -gnaty style
   ---------------------------------------------------------

   --  Maximum no. of output columns normally permitted
   MaxCols : constant := 80;
   --  Columns to indent for 2nd. & subsequent wraps
   Indentation         : constant := 10;
   SubsequentLineWidth : constant := MaxCols - Indentation;

   --  Copies a line, wrapping if it appears to be necessary.
   procedure CopyAndMaybeWrapLine (File     : in Ada.Text_IO.File_Type;
                                   SimpLine : in String) is
      function IsALongLine (L : in String) return Boolean;

      --  Writes out the given slice of L
      procedure OutPartOfLine (L              : in String;
                               FromCol, ToCol : in Positive);

      --  Writes out line-buffer L unchanged, without any line-wrapping.
      procedure OutUnchangedLine (L : in String);

      --  Write out line-buffer L, wrapping where necessary.
      procedure OutWrappedLine (L : in String);

      --  True if we need to wrap the line around - i.e. more than MaxCols.
      function IsALongLine (L : in String) return Boolean is
      begin
         return L'Length > MaxCols;
      end IsALongLine;

      procedure OutPartOfLine (L              : in String;
                               FromCol, ToCol : in Positive) is
      begin
         Ada.Text_IO.Put (File, L (FromCol .. ToCol));
      end OutPartOfLine;

      procedure OutUnchangedLine (L : in String) is
      begin
         Ada.Text_IO.Put_Line (File, L);
      end OutUnchangedLine;

      procedure OutWrappedLine (L : in String) is
         procedure OutNextPartOfLine (L       : in     String;
                                      FromCol : in out Positive;
                                      InWidth : in     Positive);

         procedure Indent;

         OnCol, Width : Positive;

         procedure OutNextPartOfLine (L       : in     String;
                                      FromCol : in out Positive;
                                      InWidth : in     Positive) is
            function OKSplitChar (C : in Character) return Boolean;
            ToCol : Natural;

            --  Returns true if C is a space, parenthesis or bracket.
            function OKSplitChar (C : in Character) return Boolean is
               use Ada.Characters.Latin_1;
            begin
               return (C = Space)
                 or else (C = Left_Parenthesis)
                 or else (C = Right_Parenthesis)
                 or else (C = Left_Square_Bracket)
                 or else (C = Right_Square_Bracket);
            end OKSplitChar;

         begin --  OutNextPartOfLine
            ToCol := FromCol + InWidth - 1;
            --  if line can't be split at the exact length you want it,
            --  search left along the text for the first feasible place
            if not OKSplitChar (L (ToCol)) then      --  drat!
               loop
                  ToCol := ToCol - 1;
                  exit when OKSplitChar (L (ToCol)) or else (ToCol = FromCol);
               end loop;
               --  if the line can't be split at any point then search right
               --  along the text i.e. the line will be longer than you want
               if ToCol = FromCol then                      --  double drat!
                  ToCol := FromCol + InWidth - 1;
                  loop
                     exit when (ToCol >= L'Length) or else OKSplitChar (L (ToCol));
                     ToCol := ToCol + 1;
                  end loop;
               end if;
            end if;
            OutPartOfLine (L, FromCol, ToCol);
            FromCol := ToCol + 1;
         end OutNextPartOfLine;

         procedure Indent is
            subtype IndentIndex is Positive range 1 .. Indentation;
            subtype IndentString is String (IndentIndex);
            IndentC : constant IndentString := IndentString'(others => ' ');
         begin
            Ada.Text_IO.Put (File, IndentC);
         end Indent;

      begin --  OutWrappedLine
         OnCol := 1;       --  Start at column 1
         Width := MaxCols; --  To start with
         while OnCol + Width <= L'Length loop
            OutNextPartOfLine (L, OnCol, Width);
            Width := SubsequentLineWidth;
            Ada.Text_IO.New_Line (File, 1);
            Indent;
         end loop;
         if OnCol <= L'Length then
            OutPartOfLine (L, OnCol, L'Length);
            Ada.Text_IO.New_Line (File, 1);
         end if;
      end OutWrappedLine;

   begin --  CopyAndMaybeWrapLine
         --  if line is too long, wrap it, otherwise output it unchanged
      if IsALongLine (SimpLine) then
         OutWrappedLine (SimpLine);
      else
         OutUnchangedLine (SimpLine);
      end if;
   end CopyAndMaybeWrapLine;

end Wrap;
