--  with Interfaces.C;            use Interfaces.C;
--  with Interfaces.C.Strings;    use Interfaces.C.Strings;

with Ada.Finalization;        use Ada.Finalization;

with System;                  use System;
--  with System.Storage_Elements; use System.Storage_Elements;

with Ada.Strings.Maps;
use Ada.Strings;

package Jwa.Strings is
   pragma Preelaborate;

   type String_8_Data (Capacity : Natural) is record
      Length : aliased Natural;
      chr : aliased String (1 .. Capacity);
--        Terminator : aliased Character := Character'Val (0);
   end record;
   pragma Pack (String_8_Data);

   type String_8_Ptr is access all String_8_Data;

   Null_String_8_Ptr : constant String_8_Ptr;

   type String_8 is new Controlled with private;
      pragma Preelaborable_Initialization (String_8);

      ---------------------------------
      -- String_8-specific functions --
      ---------------------------------

      function Create (Ptr : String_8_Ptr) return String_8;
      procedure Unique (Object : in out String_8);
      function p (Item : String_8) return String_8_Ptr;
      function a (Item : String_8) return Char_8_Ptr;

      --------------------
      -- create/convert --
      --------------------

      function Create (Source : String) return String_8;
      function To_String_8 (Source : String) return String_8 renames Create;
      function "+" (Source : String) return String_8 renames Create;

      function Create (Length : Natural) return String_8;

      function To_Ada (Source : String_8) return String;
      function To_String (Source : String_8) return String renames To_Ada;
      function "+" (Source : String_8) return String renames To_Ada;

      function Create
        (Ptr : Char_8_Ptr;
         Length : Natural) return String_8;

      function Create_Z
        (Ptr : Char_8_Ptr;
         Max_Length : Natural := Natural'Last) return String_8;
      -- create from zero-terminated C-style string

      function Create_Z
        (Buffer : String_8_Buffer) return String_8;
      -- create from zero-terminated string inside Buffer

      ---------
      -- set --
      ---------

      procedure Make_Buffer (Target : in out String_8; Length : Natural);
      -- does not preserve old contents
      -- Target turns into unique string

      procedure Set (Target : in out String_8; Ptr : String_8_Ptr);
      procedure Set_String_8 (Target : in out String_8; Ptr : String_8_Ptr)
        renames Set;

      procedure Set (Target : in out String_8; Source : String);
      procedure Set_String_8 (Target : in out String_8; Source : String)
        renames Set;

      procedure Set_Length (Target : in out String_8; Length : Natural);
      procedure Set_Empty (Target : in out String_8);

      procedure Set
        (Target : in out String_8;
         Ptr : Char_8_Ptr;
         Length : Natural);
      procedure Set_String_8
        (Target : in out String_8;
         Ptr : Char_8_Ptr;
         Length : Natural) renames Set;

      procedure Set_Z
        (Target : in out String_8;
         Ptr : Char_8_Ptr;
         Max_Length : Natural := Natural'Last);
      -- create from zero-terminated C-style string

      procedure Set_Z
        (Target : in out String_8;
         Buffer : String_8_Buffer);
      -- create from zero-terminated string inside Buffer

      ----------------------------
      -- Mere string facilities --
      ----------------------------

      function Length (Source : String_8) return Natural;
      function Is_Empty (Source : String_8) return Boolean;

      procedure Append (Source : in out String_8; New_Item : String_8);
      procedure Append (Source : in out String_8; New_Item : String);
      procedure Append (Source : in out String_8; New_Item : Char_8);

      function Get (Source : String_8; Index : Positive) return Char_8;
      function Element (Source : String_8; Index : Positive) return Char_8
                        renames Get;
      procedure Set (Source : in out String_8; Index : Positive; By : Char_8);
      procedure Replace_Element (Source : in out String_8; Index : Positive;
                                 By : Char_8) renames Set;

      function Slice (Source : String_8; Low : Positive; High : Natural)
                      return String;
      function Slice_8 (Source : String_8; Low : Positive; High : Natural)
                        return String_8;
      procedure Slice_8 (Source : String_8; Target : in out String_8;
                         Low : Positive; High : Natural);

      function "&" (Left : String_8; Right : String_8) return String_8;
      function "&" (Left : String_8; Right : String) return String_8;
      function "&" (Left : String; Right : String_8) return String_8;
      function "&" (Left : String_8; Right : Char_8) return String_8;
      function "&" (Left : Char_8; Right : String_8) return String_8;

      function "*" (Left : Natural; Right : Char_8) return String_8;
      function "*" (Left : Natural; Right : String) return String_8;
      function "*" (Left : Natural; Right : String_8) return String_8;

      function "=" (Left : String_8; Right : String_8) return Boolean;
      function "=" (Left : String_8; Right : String) return Boolean;
      function "=" (Left : String; Right : String_8) return Boolean;

      function "<" (Left : String_8; Right : String_8) return Boolean;
      function "<" (Left : String_8; Right : String) return Boolean;
      function "<" (Left : String; Right : String_8) return Boolean;

      function "<=" (Left : String_8; Right : String_8) return Boolean;
      function "<=" (Left : String_8; Right : String) return Boolean;
      function "<=" (Left : String; Right : String_8) return Boolean;

      function ">" (Left : String_8; Right : String_8) return Boolean;
      function ">" (Left : String_8; Right : String) return Boolean;
      function ">" (Left : String; Right : String_8) return Boolean;

      function ">=" (Left : String_8; Right : String_8) return Boolean;
      function ">=" (Left : String_8; Right : String) return Boolean;
      function ">=" (Left : String; Right : String_8) return Boolean;

      ------------------------
      -- Search Subprograms --
      ------------------------

      function Index
        (Source  : String_8;
         Pattern : String;
         Going   : Direction := Forward;
         Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;

      function Index
        (Source  : String_8;
         Pattern : String;
         Going   : Direction := Forward;
         Mapping : Maps.Character_Mapping_Function) return Natural;

      function Index
        (Source : String_8;
         Set    : Maps.Character_Set;
         Test   : Membership := Inside;
         Going  : Direction  := Forward) return Natural;

      function Index
        (Source  : String_8;
         Pattern : String;
         From    : Positive;
         Going   : Direction := Forward;
         Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;

      function Index
        (Source  : String_8;
         Pattern : String;
         From    : Positive;
         Going   : Direction := Forward;
         Mapping : Maps.Character_Mapping_Function) return Natural;

      function Index
        (Source  : String_8;
         Set     : Maps.Character_Set;
         From    : Positive;
         Test    : Membership := Inside;
         Going   : Direction := Forward) return Natural;

      function Index_Non_Blank
        (Source : String_8;
         Going  : Direction := Forward) return Natural;

      function Index_Non_Blank
        (Source : String_8;
         From   : Positive;
         Going  : Direction := Forward) return Natural;

      function Count
        (Source  : String_8;
         Pattern : String;
         Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;

      function Count
        (Source  : String_8;
         Pattern : String;
         Mapping : Maps.Character_Mapping_Function) return Natural;

      function Count
        (Source : String_8;
         Set    : Maps.Character_Set) return Natural;

      procedure Find_Token
        (Source : String_8;
         Set    : Maps.Character_Set;
         Test   : Membership;
         First  : out Positive;
         Last   : out Natural);

      ------------------------------------
      -- String Translation Subprograms --
      ------------------------------------

      function Translate
        (Source  : String_8;
         Mapping : Maps.Character_Mapping) return String_8;

      procedure Translate
        (Source  : in out String_8;
         Mapping : Maps.Character_Mapping);

      function Translate
        (Source  : String_8;
         Mapping : Maps.Character_Mapping_Function) return String_8;

      procedure Translate
        (Source  : in out String_8;
         Mapping : Maps.Character_Mapping_Function);

      ---------------------------------------
      -- String Transformation Subprograms --
      ---------------------------------------

      function Replace_Slice
        (Source : String_8;
         Low    : Positive;
         High   : Natural;
         By     : String) return String_8;

      procedure Replace_Slice
        (Source : in out String_8;
         Low    : Positive;
         High   : Natural;
         By     : String);

      function Insert
        (Source   : String_8;
         Before   : Positive;
         New_Item : String) return String_8;

      procedure Insert
        (Source   : in out String_8;
         Before   : Positive;
         New_Item : String);

      function Overwrite
        (Source   : String_8;
         Position : Positive;
         New_Item : String) return String_8;

      procedure Overwrite
        (Source   : in out String_8;
         Position : Positive;
         New_Item : String);

      function Delete
        (Source  : String_8;
         From    : Positive;
         Through : Natural) return String_8;

      procedure Delete
        (Source  : in out String_8;
         From    : Positive;
         Through : Natural);

      function Trim
        (Source : String_8;
         Side   : Trim_End) return String_8;

      procedure Trim
        (Source : in out String_8;
         Side   : Trim_End);

      function Trim
        (Source : String_8;
         Left   : Maps.Character_Set;
         Right  : Maps.Character_Set) return String_8;

      procedure Trim
        (Source : in out String_8;
         Left   : Maps.Character_Set;
         Right  : Maps.Character_Set);

      function Head
        (Source : String_8;
         Count  : Natural;
         Pad    : Character := Space) return String_8;

      procedure Head
        (Source : in out String_8;
         Count  : Natural;
         Pad    : Character := Space);

      function Tail
        (Source : String_8;
         Count  : Natural;
         Pad    : Character := Space) return String_8;

      procedure Tail
        (Source : in out String_8;
         Count  : Natural;
         Pad    : Character := Space);
   -- end String_8;

   Null_String_8 : constant String_8;

private

--     Chunk_Delta : constant := 4;

   Null_String_8_Data : aliased String_8_Data :=
     (Capacity => 4,
      Length => 0,
      chr => (others => NUL_8));
   Null_String_8_Ptr : constant String_8_Ptr :=
     Null_String_8_Data'Access;
   Null_String_8_Address : constant Char_8_Ptr :=
     Char_8_Ptr (Null_String_8_Data.chr'Address);

   type Reference_Counter_Type is record
      rc : Integer := 1;
      pragma Volatile (rc);
   end record;

--     Reference_Counter_Size : constant Storage_Count :=
--       Reference_Counter_Type'Max_Size_In_Storage_Elements;

   type String_8_Internal (Capacity : Natural) is record
      Counter : aliased Reference_Counter_Type;
      Data : aliased String_8_Data (Capacity);
   end record;
   pragma Pack (String_8_Internal);

   type String_8_Internal_Ptr is access all String_8_Internal;

   type String_8 is new Controlled with record
      Ptr : String_8_Internal_Ptr;
   end record;

   pragma Inline (Length);
   pragma Inline (Is_Empty);
   pragma Inline (To_Ada);
   pragma Inline (To_String_8);
   pragma Inline (p);
   pragma Inline (a);

   procedure Set_Empty (Target : in out String_8) renames Finalize;

   overriding procedure Adjust (Object : in out String_8);
   overriding procedure Finalize (Object : in out String_8);

   Null_String_8 : constant String_8 :=
     (Controlled with Ptr => null);

end Jwa.Strings;
