-- This package provides a lines abstraction, allowing an arbitrary
-- number of arbitrary-length lines to be collected together in a
-- well-defined order.
--
-- All operations raise "Not_Initialized" if passed an uninitialized
-- object.
--
with Unbounded_String;
with Singly_Linked_List_Generic;
package Lines is

    type Line is private;

    function Create (From_String : in String) return Line;
    --
    -- Returns an initialized line corresponding to the specified string.

    function Empty_Line return Line;
    --
    -- Returns an empty, initialized line.

    function Copy (Of_Line : in Line) return Line;
    --
    -- Returns a true (non-aliased) copy of the specified line.
    -- Very different from ":=".

    function Image (Of_Line : in Line) return String;
    --
    -- Returns the string image of the specified line.

    type Iterator is private; -- Is ordered.

    function Create return Iterator;
    --
    -- Returns an empty, initialized line iterator.

    function Copy (Of_Iterator : in Iterator) return Iterator;
    --
    -- Returns a true (non-aliased) copy of the specified iterator.
    -- Very different from ":=".

    procedure Add (To_Iterator : in out Iterator; This_Line : in Line);
    --
    -- Adds the specified line to the end of the specified iterator.
    -- Alters iterator state, so is not safe to call while iterating.

    function Done (This_Iterator : in Iterator) return Boolean;

    procedure Reset (This_Iterator : in out Iterator);
    --
    -- Resets the current line in the iterator to be the first line.
    --
    -- If the iterator is empty, sets "Done" to True.

    function Current (This_Iterator : in Iterator) return Line;
    --
    -- Returns the current line in the iterator.
    --
    -- Raises "No_Current_Line" if already "Done".

    procedure Modify (This_Iterator : in out Iterator; New_Line : in Line);
    --
    -- Replaces the current line in the iterator with the new line.
    -- Can be called during iteration without altering iterator's position.
    --
    -- Raises "No_Current_Line" if already "Done".

    procedure Next (This_Iterator : in out Iterator);
    --
    -- Advances the current line in the iterator to the next line.
    --
    -- Raises "No_Next_Line" if already "Done".

    subtype Line_Number is Positive; -- Lines are numbered 1..N

    function Position (In_Iterator : in Iterator) return Line_Number;
    --
    -- Returns the line number corresponding to the current line in
    -- the iterator.
    --
    -- Raises "No_Current_Line" if already "Done".

    procedure Set (This_Iterator : in out Iterator; To_Line : in Line_Number);
    --
    -- Sets the current line in the iterator to the specified line number.
    --
    -- Raises "Out_Of_Range" if the specified line number is out of range.

    function Line_At (This_Position : in Line_Number; In_Iterator : in Iterator)
                     return Line;
    --
    -- Returns the line corresponding to the specified line number.
    -- Can be called during iteration without altering iterator's position.
    --
    -- Raises "Out_Of_Range" if the specified line number is out of range.

    function Is_Empty (This_Iterator : in Iterator) return Boolean;
    --
    -- Returns True if the specified iterator contains no lines.

    function Lines_In (This_Iterator : in Iterator) return Natural;
    --
    -- Returns the number of lines in the specified iterator.

    Not_Initialized : exception;

    No_Current_Line : exception;
    No_Next_Line : exception;

    Out_Of_Range : exception;

private

    package Vstrings is new Unbounded_String;

    type Line is
        record
            Contents : Vstrings.Variable_String;
            Is_Initialized : Boolean := False;
        end record;

    package Strings is new Singly_Linked_List_Generic (Line);

    type Iterator is
        record
            Contents : Strings.List := Strings.Create;
            Is_Initialized : Boolean := False;
        end record;

end Lines;