!
!   bquote.h
!
!   Synopsis
!
!       Extension for writing block quotes, i.e. indented
!       paragraphs, in Version 5 games. The effect is similar to
!       the <blockquote> tag in HTML, but it always uses a fixed
!       width font.
!
!   Inclusion
!
!       Include the file "bquote.h" at the beginning of your
!       game file. For z-code, this extension is stand-alone.
!
!       For Glulx, it relies on using the standard Inform
!       library. Glulx compability is provided for the case that
!       you decide to flip the -G switch during development. If
!       you're going for Glulx from the start, you might be
!       better off using the block quote style as produced by
!       glk($0086, 7);
!
!   How to print block quotes
!
!       First, the block quote must be opened with
!
!           bq_open(margin, padding, maxwidth);
!
!       This will start a block quote with the specified margin
!       left and right. If you specify a padding, the block
!       quote is printed in a special style, usually reverse,
!       with the padding from the left and right borders.
!
!       Usually, the width of the quote is determined by the
!       width of the screen and the margins and paddings. If you
!       specify maxwidth, the width will still be adjusted to
!       the screen width, but if the inner width of the box is
!       greater than maxwidth, maxwidth is used.
!
!       If you specify a negative maxwidth, the margin will be
!       calculated so that the quote will be centered and
!       -maxwidth characters wide. If the calculated margin is
!       less than the specified margin, the width will be
!       adjusted.
!
!       For printing the text, use the routines
!
!           bq_text(str, a1, a2, a3);
!           bq_newline();
!
!       where str may be the reference to a static string or a
!       routine which can take up to three arguments a#.
!
!       The output text must not contain the newline character ^
!       - that's what the bq_newline() function is for - and it
!       must not be longer than BQ_MAX_LENGTH characters. The
!       complete text may be of any length, but it must be
!       defined in chunks of less than BQ_MAX_LENGTH characters.
!
!       If the text is longer, a warning is issued, which
!       should tell you to either break down your text into
!       smaller chunks or to define a larger value for the
!       constant BQ_MAX_LENGTH (default is 512) before including
!       "bqoute.h".
!
!       Between two chunks of text, a space is inserted
!       automatically.
!
!       Finally, close your quote to return to normal printing
!       by calling
!
!           bq_close();
!
!       Before opening and after closing a quote, a newline is
!       printed. You might want to end the text before the quote
!       and begin the text after it with an extra newline:
!
!           description [;
!               text "One section in particular catches
!                   your attention:^";
!               bq_open(4);
!               bq_text("The security system ...");
!               bq_newline();
!               bq_newline();
!               bq_text("The password is ~Halifax~.");
!               bq_close();
!               "^Now that's interesting.";
!           ];
!
!   Examples of quote styles:
!
!       Regular blockquote, four spaces left and right:
!
!           bq_open(4);
!
!       Reverse box across the whole screen with a padding of
!       four:
!
!           bq_open(0, 4);
!
!       Zero margin boxes work on many interpreters, but on
!       some terps they might cause extra blank lines, so better
!       use a safty margin:
!
!           bq_open(1, 3);
!
!       Narrow box 30 spaces wide, aligned to left margin (4):
!
!           bq_open(4, 1, 30);
!
!       Same, but centered. The text is always left flushed:
!
!           bq_open(4, 1, -30);
!
!   Customization
!
!       The style for quotes with a positive margin is reverse
!       per default, making such block quotes inline box quotes.
!
!       You can change the style of these quotes by specifying
!       your own routines bq_style_on and bq_style_off, e.g. to
!       use coloured blocks and a "mail quote" effect:
!
!           Replace bq_style_on;
!           Replace bq_style_off;
!
!           [bq_style_on;
!             if ((0->1) & 1 ~= 0) @set_colour 3 9;
!             else style reverse;
!             print">";
!           ];
!
!           [bq_style_off;
!             if ((0->1) & 1 ~= 0) @set_colour 1 1;
!             else style roman;
!           ];
!
!           Include "bquote";
!
!       Words that run longer than a line are printed over the
!       right margin. Buffering is off in bq mode, so that the
!       layout is usually not very much affected. You can change
!       this behaviour by defining the constant BQ_TRIM_LINES
!       which causes long words to be pruned. A tilde is then
!       printed and the word continues on the next line. This
!       might be tidier for reverse boxes.
!
!   Versions and History
!
!       18-May-2005     Version 1.0
!
!   Author
!
!       Martin Oehm     (martin.oehm at gmx.de)
!
!---------------------------------------------------------------

System_file;

Default BQ_MAX_LENGTH 512;

Global bq_width = -1;
Global bq_margin;
Global bq_padding;
Global bq_pos;

Array bq_buffer -> BQ_MAX_LENGTH + 2;

[bq_getwidth;
    #ifdef TARGET_ZCODE;
    ! read the z-code header byte
    return 0->$21;
    #ifnot;
    ! use the width of the status window if possible
    if (gg_statuswin)
      glk($0025, gg_statuswin, gg_arguments, gg_arguments + WORDSIZE);
    else
      glk($0025, gg_mainwin, gg_arguments, gg_arguments + WORDSIZE);
    return gg_arguments-->0;
    #endif;
];

[bq_open marg padd max;
    if (bq_width > 0) print "[*** Waring: bq already open. ***]";
    bq_width = bq_getwidth();
    bq_padding = padd;
    if (max < 0) {
        ! margins are determined by width
        bq_margin = (bq_width + max)/2 - bq_padding;
        if (bq_margin < marg) bq_margin = marg;
        max = 0;
    } else {
        ! width is determined by margins
        bq_margin = marg;
    }
    bq_width = bq_width - 2*bq_margin - 2*bq_padding;
    if (max && bq_width > max) bq_width = max;
    font off;
    bq_newline(1);

    #ifdef TARGET_ZCODE;
    #ifndef BQ_TRIM_LINES;
    ! no buffering for words that run over the right margin
    @buffer_mode 0;
    #endif;
    #endif;
];

[bq_style_on; style reverse; ];

[bq_style_off; style roman; ];

[bq_newline suppress;
    if (suppress <= 0 && bq_padding) {
        spaces bq_width - bq_pos + bq_padding;
        bq_style_off();
    }
    new_line;
    bq_pos = 0;
    spaces bq_margin;
    if (suppress < 0) return;
    if (bq_padding) {
        bq_style_on();
        spaces bq_padding;
    }
];

[bq_print a l;
    if (l < 0 || l > BQ_MAX_LENGTH) "***",  l, " ***";
    while (l--) {
        if (a->0 == 13 or 10) print "@@94";
        else print (char) a->0;
        a++; bq_pos++;
    }
];

[bq_text s a1 a2 a3   a l ll sep;
    if (bq_width < 0) "[*** Error: bq not opened! ***]";
    #ifdef TARGET_ZCODE;
    @output_stream 3 bq_buffer;
    if (s ofclass String) s.print();
    if (s ofclass Routine) s.call(a1, a2, a3);
    @output_stream -3;
    l = bq_buffer-->0;
    a = bq_buffer + 2;
    #ifnot;
    l = PrintAnyToArray(bq_buffer, BQ_MAX_LENGTH, s, a1, a2, a3);
    a = bq_buffer;
    #endif;

    if (l > BQ_MAX_LENGTH) "[*** Error: String chunk too long! ***]";

    while (bq_pos + l > bq_width) {
        ll = bq_width - bq_pos - 1;
        while (ll > 0 && a->ll ~= ' ') ll--;
        #ifdef BQ_TRIM_LINES;
        if (ll <= 0 && bq_pos == 0) {
            ! trim overlong words
            ll = bq_width - bq_pos -1;
            sep = true;
        }
        #ifnot;
        if (ll <= 0 && bq_pos == 0) {
            ! find next space
            ll = bq_width - bq_pos;
            while (ll < l && a->ll ~= ' ') ll++;
        }
        #endif;
        if (ll > 0) {
            bq_print(a, ll);
            a = a + ll + 1;
            l = l -ll - 1;
            if (sep) {
                print "@@126"; bq_pos++;
                a--; l++; sep = false;
            }
            if (l < 0) { a = a + l; l = 0; }
        }
        bq_newline();
    }
    bq_print(a, l);
    if (bq_pos < bq_width) {
        print " "; bq_pos++;
    }
];

[bq_close;
    #ifdef TARGET_ZCODE;
    #ifndef BQ_TRIM_LINES;
    @buffer_mode 1;
    #endif;
    #endif;
    bq_newline(-1);
    font on;
    bq_width = -1;
];
