with Ada.Command_Line, Ada.Strings.Unbounded.Text_IO, Ada.Integer_Text_IO, Globals;
use Globals;

pragma Elaborate_All(Ada.Command_Line,Ada.Strings.Unbounded.Text_IO,Ada.Integer_Text_IO,Globals);

package body Strings is

  package Txt_IO renames Ada.Text_IO;
  package Int_IO renames Ada.Integer_Text_IO;

  procedure Put(P: in File_Access; N: in String) is
  begin
    if P /= null then
      Txt_IO.Put(P.all,N);
    end if;
  end Put;

  procedure Put(F: in File_Type; N: in Boolean) is
  begin
    if N then Txt_IO.Put(F,"true");
    else Txt_IO.Put(F,"false"); end if;
    New_Line(F);
  end Put;

  procedure Get(F: in File_Type; N: out Boolean) is
    S: constant String := Get_Next_Line(F);
  begin
    if S="true" then N := True;
    elsif S="false" then N := False;
    else Txt_IO.Put_Line("Strings.GetArg: non-Boolean value"); raise Sorry; end if;
  end Get;

  procedure Put_Line(P: in File_Access; N: in String) is
  begin
    if P /= null then
      Txt_IO.Put_Line(P.all,N);
    end if;
  end Put_Line;

  procedure New_Line(P: in File_Access) is
  begin
    if P /= null then
      Txt_IO.New_Line(P.all);
    end if;
  end New_Line;

  procedure Flush(P: in File_Access) is
  begin
    if P /= null then
      Txt_IO.Flush(P.all);
    end if;
  end Flush;

  function GetArg return Character is
    S: constant String := Ada.Command_Line.Argument(ArgIndex);
  begin
    ArgIndex := ArgIndex+1;
    return S(S'First);
  end GetArg;

  function GetArg return String is
    S: constant String := Ada.Command_Line.Argument(ArgIndex);
  begin
    ArgIndex := ArgIndex+1;
    return S;
  end GetArg;

  function GetArg return Boolean is
    S: constant String := GetArg;
  begin
    if (S="True") or else (S="true") then return True; end if;
    if (S="False") or else (S="false") then return False; end if;
    Txt_IO.Put_Line("Strings.GetArg: non-Boolean value");
    raise Sorry;
    return False;
  end GetArg;

  function Get_Next_Line(F: File_Type) return String is
    use Ada.Strings.Unbounded;
    U: Unbounded_String := Null_Unbounded_String;
  begin
    while Length(U)=0 loop
      Text_IO.Get_Line(F,U);
    end loop;
    return To_String(U);
  end Get_Next_Line;

  function Prompt(N: String) return String is
  begin
    Txt_IO.Put(N);
    return Get_Next_Line(Current_Input);
  end Prompt;

  function Choose(ChooseC0: Boolean; C0,C1: Character) return Character is
  begin
    if ChooseC0 then return C0; else return C1; end if;
  end Choose;

  function Choose(ChooseS0: Boolean; S0,S1: String) return String is
  begin
    if ChooseS0 then return S0; else return S1; end if;
  end Choose;

  function Val(C: Character; VMax: Positive) return Natural is
  begin
    for L in 0 .. VMax loop
      if C=LoDig(L) then return L; end if;
    end loop;
    for L in 0 .. VMax loop
      if C=UpDig(L) then return L; end if;
    end loop;
    raise Data_Error with "illegal character";
    return 0;
  end Val;

  function Value(S: String; Base: Positive) return Integer is
    pragma Unsuppress (Overflow_Check);
    VMax: constant Positive := Base-1;
    Neg: Boolean := False;
    I: Integer := 0;
  begin
    for K in S'Range loop
      if S(K)='-' then
        Neg := True;
      elsif S(K) /= ' ' and then S(K) /= '+' then
        I := Base*I+Val(S(K),VMax);
      end if;
    end loop;
    if Neg then I := -I; end if;
    return I;
  end Value;

  function Strip0(I: Natural; Base: Positive := 10) return Natural is
    J: Natural := I;
  begin
    while J>0 and then (J mod Base)=0 loop J := J/Base; end loop;
    return J;
  end Strip0;

  function Width(I: Integer; Base: Positive := 10) return Positive is
    K: Integer := abs(I);
    L: Integer;
  begin
    if I<0 then L := 2; else L := 1; end if;
    loop
      K := K/Base;
      exit when K=0;
      L := L+1;
    end loop;
    return L;
  end Width;

  function Image(I: Integer; Base: Positive; ShowPlus: Boolean) return String is
  begin
    if I<0 then
      return "-" & Image(abs(I),Base,False);
    elsif ShowPlus then
      return "+" & Image(I,Base,False);
    else
      declare
        L: constant Positive := Width(I,Base);
        K: Integer := I;
        S: String(1..L);
      begin
        for N in reverse 1 .. L loop
          S(N) := UpDig(K mod Base);
          K := K/Base;
        end loop;
        return S;
      end;
    end if;
  end Image;

  function Strng(I: Integer; W: Positive) return String is
    S: String(1 .. W);
  begin
    Int_IO.Put(S,I);
    return S;
  end Strng;

  function Strng(I: Integer) return String is
    S: String(1 .. Width(I));
  begin
    Int_IO.Put(S,I);
    return S;
  end Strng;

  function Strng0(I: Natural; W: Positive) return String is
    S: String(1 .. W);
  begin
    Int_IO.Put(S,I);
    for I in S'Range loop
      if S(I)=' ' then S(I) := '0'; end if;
    end loop;
    return S;
  end Strng0;

  function RankStrng(I: Natural; W: Positive) return String is
    S: String(1 .. W);
  begin
    Int_IO.Put(S,I);
    case I is
      when 1 => return S & "st";
      when 2 => return S & "nd";
      when 3 => return S & "rd";
      when others => return S & "th";
    end case;
  end RankStrng;

  procedure Show0(NewLine: in Boolean := True) is
  begin
    if NewLine then
      New_Line(Default_Output.all);
    end if;
  end Show0;

  procedure Show0(N: in String; NewLine: in Boolean := True) is
    P: constant File_Access := Default_Output.all;
  begin
    Put(P,N);
    if NewLine then New_Line(P); end if;
  end Show0;

  function Show0(N: in String) return String is
    P: constant File_Access := Default_Output.all;
  begin
    Put_Line(P,N);
    Flush(P);
    return N;
  end Show0;

  procedure Show1(N: in String; B: in Boolean; NewLine: in Boolean := True) is
    P: constant File_Access := Default_Output.all;
  begin
    if B then
      Put(P,N & "True");
    else
      Put(P,N & "False");
    end if;
    if NewLine then New_Line(P); end if;
  end Show1;

end Strings;
