with Text_Io;
with String_Pkg;

package Simple_Paginated_Output is

    --| Create paginated text files with user defined heading,
    --| footing, and page length.

    --| Overview:

    --| The Paginated_Output package is used to create paginated
    --| output files.  When such a file is created, the page length,
    --| and page header length are specified. Several operations are
    --| provided for setting the header text which will appear
    --| on each output page.  The following escapes can be used in the
    --| header text:
    --|-
    --|  ~f the current external file name
    --|  ~p the current page number
    --|  ~d the current date (eg. 03/15/85)
    --|  ~c the current calendar date (eg. March 15, 1985)
    --|  ~t the current time (eg. 04:53:32)
    --|+
    --| Case is not significant after the tilde (~).  If the tilde
    --| is followed by any other character, only the second character
    --| is printed unless the line ends with a tilde in which case
    --| the line will be terminated one character before the tilde.
    --|
    --| The header is printed just before the first line of a page
    --| is output.  Thus, if a paginated file is opened and closed without
    --| any calls to print a line in between, the output is a null file.
    --|
    --| This package knows nothing about (and places no limits on)
    --| the length or contents of each line sent to the output file.
    --| In particular, if the line contains ASCII control codes
    --| for new line, form feed, and/or vertical tab the output file
    --| will not be properly paginated.  Normal usage is to call
    --| Create_Paginated_File, call Set_Header, call Put and Put_Line
    --| repeatedly to output a sequence of lines of text, and finally
    --| call Close_Paginated_File to complete the last page and close
    --| the file.

    --| N/A: Effects, Requires, Modifies, Raises

    -- Exceptions --

    File_Already_Open : exception;  --| Raised if create is attempted
    --| for an already existing file.
    File_Error : exception;  --| Raised if unable to open a file
    --| other than File_Already_Open
    File_Not_Open : exception;  --| Raised if close is attempted
    --| for an unopened file.
    Invalid_Count : exception;  --| Raised if a requested count
    --| can not be serviced.
    Invalid_File : exception;  --| Raised if output is attempted
    --| with an invalid file handle.
    Output_Error : exception;  --| Raised if error is encountered
    --| during an output operation.
    Page_Layout_Error : exception;  --| Raised if page specification
    --| is invalid.
    Page_Overflow : exception;  --| Raised if specified reserve
    --| value exceeds the page size.
    Text_Overflow : exception;  --| Raised if header text
    --| overflows area.
    Text_Underflow : exception;  --| Raised if header text
    --| underflows area.

    -- Types --

    subtype Host_File_Name is String;
    --| String of valid characters for
    --| external file name.

    type Variable_String_Array is     --| Array of variable length strings
       array (Positive range <>) of String_Pkg.String_Type;

    type Paginated_File_Handle is     --| Handle to be passed around in a
       limited private;  --| program that uses paginated output.


    -- Operations --

    procedure Create_Paginated_File ( --| Create a paginated output file
                                     --| and return the file handle.
                                     File_Name : in Host_File_Name := "";
                                     --| The name of the file to be created.
                                     File_Handle : in out Paginated_File_Handle;
                                     --| Handle to be used for subsequent
                                     --| operations
                                     Page_Size : in Integer := 60;
                                     --| The number of lines per page
                                     Header_Size : in Integer := 6
                                     --| The number of header text lines
                                     );

    --| Raises:
    --| File_Already_Open, File_Error, Page_Layout_Error

    --| Requires:
    --| File_Name is an valid external name of the file to be created (If
    --| it is omitted, the current output file is selected).  Page_Size,
    --| and Header_Size are optional values (if omitted 60, and 6 are
    --| respectively) to be used for the page layout of the file to be
    --| created.  Page_Size specifies the total number of lines per page
    --| (including the areas for the header).
    --| Header_Size specifies the number of lines to be reserved for the
    --| header area.

    --| Effects:
    --| Creates a new paginated file with Page_Size number of lines
    --| per page and Header_Size and number of lines reserved for the header.
    --| Access to the paginated file control structure Paginated_File_Handle
    --| is returned for use in subsequent operations.

    --| Errors:
    --| If any of the page layout values are negative, the exception
    --| Page_Layout_Error is raised.  Also if the total number of lines
    --| in the header plus one exceeds Page_Size, the same
    --| exception is raised.  This guarantees that at least one line of
    --| text can appear on each output page.
    --| If the output file with the specified File_Name is already open
    --| File_Already_Open exception is raised.
    --| If the file cannot be opened for any other reason, the exception
    --| File_Error is raise.

    --| N/A: Modifies

    procedure Set_Page_Layout ( --| Set the page layout for the
                               --| paginated file.
                               File_Handle : in Paginated_File_Handle;
                               --| The paginated file to be set
                               --| with the given page layout
                               Page_Size : in Integer;
                               --| The number of lines per page
                               Header_Size : in Integer
                               --| The number of header text lines
                               );

    --| Raises:
    --| Page_Layout_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Page_Size specifies the total
    --| number of lines per page (including the area for header).
    --| Header_Size and specifies the number of lines to be
    --| reserved for the header area.

    --| Effects:
    --| A paginated file is set with Page_Size number of lines per
    --| page and Header_Size number of lines reserved for the
    --| header.A page eject is performed if not at the top of the page before
    --| the new page layout values are set.

    --| Errors:
    --| If any of the page layout values are negative, the exception
    --| Page_Layout_Error is raised.  Also if the total number of lines
    --| in the header plus one exceeds Page_Size, the exception
    --| Page_Layout_Error is raised.

    --| N/A: Modifies

    procedure Set_Header ( --| Set the header text on a paginated
                          --| output file.
                          File_Handle : in Paginated_File_Handle;
                          --| Paginated file to be set
                          --| with the header text
                          Header_Text : in Variable_String_Array
                          --| Sequence of header lines
                          );

    --| Raises:
    --| Invalid_File, Text_Overflow

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Header_Text is the array
    --| of text to be used for the page header.

    --| Effects:
    --| The header text of File_Handle is set to Header_Text.  Note that
    --| the replaced header text will not be printed until the next
    --| page of the output.

    --| Errors:
    --| If File_Handle is not a valid access to a paginated file control
    --| structure exception Invalid_File is raised.
    --| Specification of a header text array which implies a greater
    --| number of lines than reserved for by Create_Paginated_File or
    --| Set_Page_Layout results in Text_Overflow exception to be raised.

    --| N/A: Modifies

    procedure Set_Header ( --| Replace a line of header text on a
                          --| paginated output file.
                          File_Handle : in Paginated_File_Handle;
                          --| Paginated file to be set
                          --| with the header text
                          Header_Line : in Integer;
                          --| Line number of header to be replaced
                          Header_Text : in String --| Header line to replace
                          );

    --| Raises:
    --| Invalid_File, Text_Overflow, Text_Underflow

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Header_Text is the text
    --| to replace the existing header line at Header_Line.

    --| Effects:
    --| The header text of File_Handle at Header_Line is set to Header_Text.
    --| Note that the replaced header text will not be printed until
    --| the next page of the output.

    --| Errors:
    --| If File_Handle is not a valid access to a paginated file control
    --| structure exception Invalid_File is raised.
    --| Specification of Header_Line greater than the number of header
    --| lines reserved by Create_Paginated_File or Set_Page_Layout
    --| results in Text_Overflow exception to be raised.
    --| If the specified Header_Line is less than or equal to 0 then
    --| Text_Underflow exception is raised.

    --| N/A: Modifies

    procedure Set_Header ( --| Replace a line of header text on a
                          --| paginated output file.
                          File_Handle : in Paginated_File_Handle;
                          --| Paginated file to be set
                          --| with the header text
                          Header_Line : in Integer;
                          --| Line number of header to be replaced
                          Header_Text : in String_Pkg.String_Type
                          --| Header line to replace
                          );

    --| Raises:
    --| Invalid_File, Text_Overflow, Text_Underflow

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Header_Text is the text
    --| to replace the existing header line at Header_Line.

    --| Effects:
    --| The header text of File_Handle at Header_Line is set to Header_Text.
    --| Note that the replaced header text will not be printed until
    --| the next page of the output.

    --| Errors:
    --| If File_Handle is not a valid access to a paginated file control
    --| structure exception Invalid_File is raised.
    --| Specification of Header_Line greater than the number of header
    --| lines reserved by Create_Paginated_File or Set_Page_Layout
    --| results in Text_Overflow exception to be raised.
    --| If the specified Header_Line is less than or equal to 0 then
    --| Text_Underflow exception is raised.

    --| N/A: Modifies

    procedure Close_Paginated_File ( --| Complete the last page and close
                                    --| the paginated file.
                                    File_Handle : in out Paginated_File_Handle
                                    --| The paginated file to be closed
                                    );

    --| Raises:
    --| Invalid_File, File_Not_Open

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.

    --| Effects:
    --| Completes the last page of output and closes the output file.

    --| Errors:
    --| If File_Handle is not a valid Paginated_File_Handle, the exception
    --| Invalid_File is raised.  If an error occurs in closing the file,
    --| File_Not_Open is raised.

    --| N/A: Modifies

    procedure Put ( --| Output a line on a paginated file
                   File_Handle : in Paginated_File_Handle;
                   --| The paginated file to
                   --| output the text
                   Text : in Variable_String_Array
                   --| The text to be output.
                   );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text of text to File_Handle.  If Text is the first string of the
    --| first line to be printed on a page, the page header is printed before
    --| printing the text.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put ( --| Output a line on a paginated file
                   File_Handle : in Paginated_File_Handle;
                   --| The paginated file to
                   --| output the text
                   Text : in String_Pkg.String_Type
                   --| The text to be output.
                   );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text of text to File_Handle.  If Text is the first string of the
    --| first line to be printed on a page, the page header is printed before
    --| printing the text.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put ( --| Output a line on a paginated file
                   File_Handle : in Paginated_File_Handle;
                   --| The paginated file to
                   --| output the text
                   Text : in String --| The text to be output.
                   );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text of text to File_Handle.  If Text is the first string of the
    --| first line to be printed on a page, the page header is printed before
    --| printing the string.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put ( --| Output a line on a paginated file
                   File_Handle : in Paginated_File_Handle;
                   --| The paginated file to
                   --| output the text
                   Text : in Character --| The text to be output.
                   );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text is a the characters to be
    --| written to the paginated output file.

    --| Effects:
    --| Outputs Text of text to File_Handle.  If Text is the first character of the
    --| first line to be printed on a page, the page header is printed before
    --| printing the string.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Space ( --| Output a specified number of spaces
                     File_Handle : in Paginated_File_Handle;
                     --| The paginated file to output the line
                     Count : in Integer --| Number of spaces
                     );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Count is the number of horizontal
    --| spaces to be output.

    --| Effects:
    --| Output Count number of blanks to File_Handle.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put_Line ( --| Output a line on a paginated file
                        File_Handle : in Paginated_File_Handle;
                        --| The paginated file to output the line
                        Text_Line : in Variable_String_Array
                        --| The line to be output.
                        );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text_Line is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text_Line of text to File_Handle.  If Text_Line is the
    --| first line to be printed on a page, the page header is printed
    --| before the line.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put_Line ( --| Output a line on a paginated file
                        File_Handle : in Paginated_File_Handle;
                        --| The paginated file to
                        --| output the line
                        Text_Line : in String_Pkg.String_Type
                        --| The line to be output.
                        );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text_Line is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text_Line of text to File_Handle.  If Text_Line is the
    --| first line to be printed on a page, the page header is printed
    --| before the line.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Put_Line ( --| Output a line on a paginated file
                        File_Handle : in Paginated_File_Handle;
                        --| The paginated file to
                        --| output the line
                        Text_Line : in String --| The line to be output.
                        );

    --| Raises:
    --| Invalid_File, Output_Error

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Text_Line is a string of
    --| characters to be written to the paginated output file.

    --| Effects:
    --| Outputs Text_Line of text to File_Handle.  If Text_Line is the
    --| first line to be printed on a page, the page header is printed
    --| before the line.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If an error
    --| occurs during output, Output_Error is raised.

    --| N/A: Modifies

    procedure Space_Line ( --| Output one or more spaces on a
                          --| paginated file
                          File_Handle : in Paginated_File_Handle;
                          --| The paginated file to
                          --| output spaces
                          Count : in Integer := 1
                          --| The number of spaces.
                          );

    --| Raises:
    --| Invalid_File, Output_Error, Invalid_Count

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Count is the number of
    --| spaces to be output to File_Handle.  If Count is omitted, 1 is
    --| assumed.

    --| Effects:
    --| Count number of line terminators are output to File_Handle.
    --| If Count is greater than the number of lines remaining on
    --| the page, a page terminator, and the page header
    --| are written before the remainder of the spaces are output.
    --| If the specified Count is less than equal to 0, no operation
    --| takes place.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If the requested space
    --| count is greater than a predetermined amount, Invalid_Count
    --| is raised.  If an error occurs during output, Output_Error
    --| is raised.

    --| N/A: Modifies

    procedure Skip_Line ( --| Output one or more spaces on a
                         --| paginated file
                         File_Handle : in Paginated_File_Handle;
                         --| The paginated file to
                         --| output skips
                         Count : in Integer := 1
                         --| The number of spaces.
                         );

    --| Raises:
    --| Invalid_File, Output_Error, Invalid_Count

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Count is the number of
    --| spaces to be output to File_Handle.  If Count is omitted, 1 is
    --| assumed.

    --| Effects:
    --| Count number of line terminators are output to File_Handle.
    --| If Count is greater than the number of lines remaining on
    --| the page, a page terminator is
    --| output and the remainder of the skips are NOT output.
    --| If the specified Count is less than equal to 0, no operation
    --| takes place.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If the requested skip
    --| count is greater than a predetermined amount, Invalid_Count
    --| is raised.  If an error occurs during output, Output_Error
    --| is raised.

    --| N/A: Modifies

    procedure Put_Page ( --| Output one or more page ejects
                        --| on a paginated file
                        File_Handle : in Paginated_File_Handle;
                        --| The paginated file to
                        --| output page ejects
                        Count : in Integer := 1
                        --| The number of pages.
                        );

    --| Raises:
    --| Invalid_File, Output_Error, Invalid_Count

    --| Requires:
    --| File_Handle is the access to the paginated file control structure
    --| returned by Create_Paginated_File.  Count is the number of
    --| pages to be output to File_Handle.  If Count is omitted, 1 is
    --| assumed.

    --| Effects:
    --| Outputs Count number of page ejects. The page header is printed as
    --| appropriate.  If the specified Count is less than equal to 0, no operation
    --| takes place.

    --| Errors:
    --| If File_Handle is not a valid, open Paginated_File_Handle,
    --| the exception Invalid_File is raised.  If the requested page
    --| count is greater than a predetermined amount, Invalid_Count
    --| is raised.  If an error occurs during output, Output_Error
    --| is raised.

    --| N/A: Modifies
private

    type Variable_String_Array_Handle is access Variable_String_Array;
    --| Handle to array of variable length
    --| strings

    type Paginated_File_Structure;
    --| Data structure to store state of
    --| the output file.

    type Paginated_File_Handle is access Paginated_File_Structure;
    --| Handle to be passed around in a
    --| program that uses paginated_output.

    type Paginated_File_Structure is
    --| a structure to store state of
        record
            --| the output file.
            File_Name : String_Pkg.String_Type;
            --| External file name
            File_Reference : Text_Io.File_Type;
            --| External file reference
            Page_Size : Integer;
            --| The number of lines per page
            Maximum_Line : Integer;
            --| The maximum number of text lines
            Current_Calendar : String_Pkg.String_Type;
            --| Creation date (eg. March 15, 1985)
            Current_Date : String (1 .. 8);
            --| Creation date (eg. 03/15/85)
            Current_Time : String (1 .. 8);
            --| Creation time (eg. 15:24:07)
            Current_Page : Integer := 0;
            --| The number of lines per page
            Current_Line : Integer := 0;
            --| The number of lines used
            Header_Size : Integer;
            --| Number of lines of header text
            Page_Header : Variable_String_Array_Handle := null;
            --| Access to page header text
        end record;

end Simple_Paginated_Output;