(********************************************************************)
(*                                                                  *)
(*  more.s7i      Filter file which shows another file screenwise   *)
(*  Copyright (C) 1992, 1993, 1994, 2005, 2023  Thomas Mertes       *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  [[file|File]] implementation type of a more filter file.
 *)
const type: moreFile is sub null_file struct
    var file: destFile is STD_NULL;
    var file: commandFile is STD_NULL;
    var integer: pageSize is 0;
    var integer: nextPromptLine is 0;
    var integer: line is 0;
    var boolean: active is TRUE;
  end struct;


(**
 *  Open a more filter file for viewing a file page by page or line by line.
 *   more := openMore(OUT, KEYBOARD, 20);
 *   writeln(more, multiPageDocument);
 *  After writing the requested number of lines it prompts for the
 *  next command with:
 *   --More--
 *  The following commands are accepted:
 *  * space        Display the next ''pageSize'' lines.
 *  * return/enter Display the next line.
 *  * q            Skip the remaining data written to the ''moreFile''.
 *  @param destFile Destination file for all data written to the ''moreFile''.
 *  @param commandFile File from which commands (' ', '\n', 'q') are read.
 *  @param pageSize Size of the page displayed after pressing the space key.
 *  @return the ''moreFile'' opened.
 *)
const func file: openMore (in file: destFile, in file: commandFile,
    in integer: pageSize) is func
  result
    var file: newFile is STD_NULL;
  local
    var moreFile: new_moreFile is moreFile.value;
  begin
    new_moreFile.destFile := destFile;
    new_moreFile.commandFile := commandFile;
    new_moreFile.pageSize := pageSize;
    new_moreFile.nextPromptLine := pageSize;
    new_moreFile.line := 0;
    new_moreFile.active := TRUE;
    newFile := toInterface(new_moreFile);
  end func;


(**
 *  Write end-of-line to ''outFile''.
 *  This function writes the end-of-line marker to the destination file.
 *  It also handles the commands allowed by a ''moreFile'':
 *  * space        Display the next ''pageSize'' lines.
 *  * return/enter Display the next line.
 *  * q            Skip the remaining data written to the ''moreFile''.
 *)
const proc: writeln (inout moreFile: outFile) is func
  local
    var char: command is ' ';
  begin
    if outFile.active then
      writeln(outFile.destFile);
      incr(outFile.line);
      if outFile.line >= outFile.nextPromptLine then
        write(outFile.destFile, "--More-- ");
        flush(outFile.destFile);
        command := getc(outFile.commandFile);
        backSpace(outFile.destFile, "--More-- ");
        if command = ' ' then
          outFile.nextPromptLine := outFile.pageSize;
        elsif command = '\n' then
          outFile.nextPromptLine := 1;
        elsif lower(command) = 'q' then
          outFile.active := FALSE;
        end if;
        outFile.line := 0;
      end if;
    end if;
  end func;


(**
 *  Write the [[string]] ''stri'' to a ''moreFile''.
 *  New lines ('\n') trigger a call of the function ''writeln''.
 *)
const proc: write (inout moreFile: outFile, in string: stri) is func
  local
    var integer: startPos is 1;
    var integer: nlPos is 0;
  begin
    if outFile.active then
      nlPos := pos(stri, '\n');
      while nlPos <> 0 and outFile.active do
        write(outFile.destFile, stri[startPos .. pred(nlPos)]);
        writeln(outFile);
        startPos := succ(nlPos);
        nlPos := pos(stri, '\n', startPos);
      end while;
      if outFile.active then
        write(outFile.destFile, stri[startPos ..]);
      end if;
    end if;
  end func;