/*
 * screen.c
 *
 * Generic screen manipulation routines. Most of these routines call the machine
 * specific routines to do the actual work.
 *
 */

#include "ztypes.h"

/*
 * select_window
 *
 * Put the cursor in the text or status window. The cursor is free to move in
 * the status window, but is fixed to the input line in the text window.
 *
 */

#ifdef __STDC__
void select_window (zword_t w, z_globals *gl)
#else
void select_window (w, gl)
zword_t w;
z_globals *gl;
#endif
{
    int row, col;

    flush_buffer (FALSE, gl);

    (gl->screen_window) = w;

    if ((gl->screen_window) == STATUS_WINDOW) {

        /* Status window: disable formatting and select status window */

        (gl->formatting) = OFF;
        (gl->scripting_disable) = ON;
        select_status_window ();

        /* Put cursor at top of status area */

        if (gl->h_type < V4)
            move_cursor (2, 1);
        else
            move_cursor (1, 1);

    } else {

        /* Text window: enable formatting and select text window */

        select_text_window ();
        (gl->scripting_disable) = OFF;
        (gl->formatting) = ON;

        /* Move cursor if it has been left in the status area */

        get_cursor_position (&row, &col);
        if (row <= (gl->status_size))
            move_cursor ((gl->status_size) + 1, 1);

    }

    /* Force text attribute to normal rendition */

    set_attribute (NORMAL);

}/* select_window */

/*
 * set_status_size
 *
 * Set the size of the status window. The default size for the status window is
 * zero lines for both type 3 and 4 games. The status line is handled specially
 * for type 3 games and always occurs the line immediately above the status
 * window.
 *
 */

#ifdef __STDC__
void set_status_size (zword_t lines, z_globals *gl)
#else
void set_status_size (lines, gl)
zword_t lines;
z_globals *gl;
#endif
{

    /* Maximum status window size is 255 */

    lines &= 0xff;

    /* The top line is always set for V1 to V3 games, so account for it here. */

    if (gl->h_type < V4)
        lines++;

    if (lines) {

        /* If size is non zero the turn on the status window */

        (gl->status_active) = ON;

        /* Bound the status size to one line less than the total screen height */

        if (lines > (zword_t) ((gl->screen_rows) - 1))
            (gl->status_size) = (zword_t) ((gl->screen_rows) - 1);
        else
            (gl->status_size) = lines;

        /* Create the status window, or resize it */

        create_status_window (gl);

        /* Need to clear the status window for type 3 games */

        if (gl->h_type < V4)
            erase_window (STATUS_WINDOW, gl);

    } else {

        /* Lines are zero so turn off the status window */

        (gl->status_active) = OFF;

        /* Reset the lines written counter and status size */

        (gl->lines_written) = 0;
        (gl->status_size) = 0;

        /* Delete the status window */

        delete_status_window (gl);

        /* Return cursor to text window */

        select_text_window ();
    }

}/* set_status_size */

/*
 * erase_window
 *
 * Clear one or all windows on the screen.
 *
 */

#ifdef __STDC__
void erase_window (zword_t w, z_globals *gl)
#else
void erase_window (w, gl)
zword_t w;
z_globals *gl;
#endif
{

    flush_buffer (TRUE, gl);

    if ((zbyte_t) w == (zbyte_t) SCREEN) {
        clear_screen ();
    } else if ((zbyte_t) w == TEXT_WINDOW) {
        clear_text_window (gl);
    } else if ((zbyte_t) w == STATUS_WINDOW) {
        clear_status_window (gl);
        return;
    }

    if (gl->h_type > V4)
        move_cursor (1, 1);
    else
        move_cursor ((gl->screen_rows), 1);

}/* erase_window */

/*
 * erase_line
 *
 * Clear one line on the screen.
 *
 */

#ifdef __STDC__
void erase_line (zword_t flag, z_globals *gl)
#else
void erase_line (flag, gl)
zword_t flag;
z_globals *gl;
#endif
{

    if (flag == TRUE)
        clear_line ();

}/* erase_line */

/*
 * set_cursor_position
 *
 * Set the cursor position in the status window only.
 *
 */

#ifdef __STDC__
void set_cursor_position (zword_t row, zword_t column, z_globals *gl)
#else
void set_cursor_position (row, column, gl)
zword_t row;
zword_t column;
z_globals *gl;
#endif
{

    /* Can only move cursor if format mode is off and in status window */

    if ((gl->formatting) == OFF && (gl->screen_window) == STATUS_WINDOW)
        move_cursor (row, column);

}/* set_cursor_position */

/*
 * pad_line
 *
 * Pad the status line with spaces up to a column position.
 *
 */

#ifdef __STDC__
static void pad_line (int column, z_globals *gl)
#else
static void pad_line (column, gl)
int column;
z_globals *gl;
#endif
{
    int i;

    for (i = (gl->status_pos); i < column; i++)
        write_char (' ', gl);
    (gl->status_pos) = column;

}/* pad_line */

/*
 * display_status_line
 *
 * Format and output the status line for type 3 games only.
 *
 */

#ifdef __STDC__
void display_status_line (z_globals *gl)
#else
void display_status_line (gl)
z_globals *gl;
#endif
{
    int i, count = 0, end_of_string[3];
    char *status_part[3];

    /* Move the cursor to the top line of the status window, set the reverse
       rendition and print the status line */

    select_window (STATUS_WINDOW, gl);
    move_cursor (1, 1);
    set_attribute (REVERSE);

    /* Redirect output to the status line buffer */

    set_print_modes (3, 0, gl);

    /* Print the object description for global variable 16 */

    pad_line (1, gl);
    status_part[count] = &(gl->status_line)[(gl->status_pos)];
    if (load_variable (16, gl) != 0)
        print_object (load_variable (16, gl), gl);
    end_of_string[count++] = (gl->status_pos);
    (gl->status_line)[(gl->status_pos)++] = '\0';

    if (get_byte (H_CONFIG) & CONFIG_TIME) {

        /* If a time display print the hours and minutes from global
           variables 17 and 18 */

        pad_line ((gl->screen_cols) - 21, gl);
        status_part[count] = &(gl->status_line)[(gl->status_pos)];
        write_string (" Time: ", gl);
        print_time (load_variable (17, gl), load_variable (18, gl), gl);
        end_of_string[count++] = (gl->status_pos);
        (gl->status_line)[(gl->status_pos)++] = '\0';
    } else {

        /* If a moves/score display print the score and moves from global
           variables 17 and 18 */

        pad_line ((gl->screen_cols) - 31, gl);
        status_part[count] = &(gl->status_line)[(gl->status_pos)];
        write_string (" Score: ", gl);
        print_number (load_variable (17, gl), gl);
        end_of_string[count++] = (gl->status_pos);
        (gl->status_line)[(gl->status_pos)++] = '\0';

        pad_line ((gl->screen_cols) - 15, gl);
        status_part[count] = &(gl->status_line)[(gl->status_pos)];
        write_string (" Moves: ", gl);
        print_number (load_variable (18, gl), gl);
        end_of_string[count++] = (gl->status_pos);
        (gl->status_line)[(gl->status_pos)++] = '\0';
    }

    /* Pad the end of status line with spaces then disable output redirection */

    pad_line ((gl->screen_cols), gl);
    set_print_modes ((zword_t) -3, 0, gl);

    /* Try and print the status line for a proportional font screen. If this
       fails then remove embedded nulls in status line buffer and just output
       it to the screen */

    if (print_status (count, status_part, gl) == FALSE) {
        for (i = 0; i < count; i++)
            (gl->status_line)[end_of_string[i]] = ' ';
        (gl->status_line)[(gl->status_pos)] = '\0';
        write_string ((gl->status_line), gl);
    }

    set_attribute (NORMAL);
    select_window (TEXT_WINDOW, gl);

}/* display_status_line */

/*
 * blank_status_line
 *
 * Output a blank status line for type 3 games only.
 *
 */

#ifdef __STDC__
void blank_status_line (z_globals *gl)
#else
void blank_status_line (gl)
z_globals *gl;
#endif
{

    /* Move the cursor to the top line of the status window, set the reverse
       rendition and print the status line */

    select_window (STATUS_WINDOW, gl);
    move_cursor (1, 1);
    set_attribute (REVERSE);

    /* Redirect output to the status line buffer and pad the status line with
       spaces then disable output redirection */

    set_print_modes (3, 0, gl);
    pad_line ((gl->screen_cols), gl);
    (gl->status_line)[(gl->status_pos)] = '\0';
    set_print_modes ((zword_t) -3, 0, gl);

    /* Write the status line */

    write_string ((gl->status_line), gl);

    /* Turn off attributes and return to text window */

    set_attribute (NORMAL);
    select_window (TEXT_WINDOW, gl);

}/* blank_status_line */

/*
 * output_string
 *
 * Output a string of characters.
 *
 */

#ifdef __STDC__
void output_string (const char *s, z_globals *gl)
#else
void output_string (s, gl)
const char *s;
z_globals *gl;
#endif
{

    while (*s)
        output_char (*s++, gl);

}/* output_string */

/*
 * output_line
 *
 * Output a string of characters followed by a new line.
 *
 */

#ifdef __STDC__
void output_line (const char *s, z_globals *gl)
#else
void output_line (s, gl)
const char *s;
z_globals *gl;
#endif
{

    output_string (s, gl);
    output_new_line (gl);

}/* output_line */

/*
 * output_char
 *
 * Output a character and rendition selection. This routine also handles
 * selecting rendition attributes such as bolding and reverse. There are
 * five attributes distinguished by a bit mask. 0 means turn all attributes
 * off. The attributes are: 1 = reverse, 2 = bold, 4 = emphasis, and
 * 8 = fixed font.
 *
 */

#ifdef __STDC__
void output_char (int c, z_globals *gl)
#else
void output_char (c, gl)
int c;
z_globals *gl;
#endif
{

    /* If output is enabled then either select the rendition attribute
       or just display the character */

    if ((gl->outputting) == ON) {

        /* Make sure we are dealing with a positive integer */

        c = (unsigned int) (c & 0xff);

        /* Attribute selection */

        if (c >= (MIN_ATTRIBUTE + 1) && c <= (MAX_ATTRIBUTE + 1)) {

            set_attribute (--c);

        } else {

            display_char (c, gl);

        }
    }

}/* output_char */

/*
 * output_new_line
 *
 * Scroll the text window up one line and pause the window if it is full.
 *
 */

#ifdef __STDC__
void output_new_line (z_globals *gl)
#else
void output_new_line (gl)
z_globals *gl;
#endif
{
    int row, col;

    /* Don't print if output is disabled or replaying commands */

    if ((gl->outputting) == ON) {

        if ((gl->formatting) == ON && (gl->screen_window) == TEXT_WINDOW) {

            /* If this is the text window then scroll it up one line */

            scroll_line (gl);

            /* See if we have filled the screen. The spare line is for the [MORE] message */

            if (++(gl->lines_written) >= (((gl->screen_rows) - (gl->top_margin)) - (gl->status_size) - 1)) {

                /* Display the new status line while the screen in paused */

                if (gl->h_type < V4)
                    display_status_line (gl);

                /* Reset the line count and display the more message */

                (gl->lines_written) = 0;

                if ((gl->replaying) == OFF) {
                    get_cursor_position (&row, &col);
                    output_string ("[MORE]", gl);
                    (void) input_character (0);
                    move_cursor (row, col);
                    clear_line ();
                }
            }
        } else

            /* If this is the status window then just output a new line */

            output_char ('\n', gl);
    }

}/* output_new_line */

/*
 * print_window
 *
 * Writes text into a rectangular window on the screen.
 *
 *    argv[0] = start of text address
 *    argv[1] = rectangle width
 *    argv[2] = rectangle height (default = 1)
 *
 */

#ifdef __STDC__
void print_window (int argc, zword_t *argv, z_globals *gl)
#else
void print_window (argc, argv, gl)
int argc;
zword_t *argv;
z_globals *gl;
#endif
{
    unsigned long address;
    unsigned int width, height;
    unsigned int row, column;

    /* Supply default arguments */

    if (argc < 3)
        argv[2] = 1;

    /* Don't do anything if the window is zero high or wide */

    if (argv[1] == 0 || argv[2] == 0)
        return;

    /* Get coordinates of top left corner of rectangle */

    get_cursor_position ((int *) &row, (int *) &column);

    address = argv[0];

    /* Write text in width * height rectangle */

    for (height = 0; height < argv[2]; height++) {

        for (width = 0; width < argv[1]; width++)
            write_char (read_data_byte (&address, gl), gl);

        /* Put cursor back to lefthand side of rectangle on next line */

        if (height != (argv[2] - 1))
            move_cursor (++row, column);

    }

}/* print_window */

/*
 * set_font_attribute
 *
 * Set text or graphic font. 1 = text font, 3 = graphics font.
 *
 */

#ifdef __STDC__
void set_font_attribute (zword_t new_font, z_globals *gl)
#else
void set_font_attribute (new_font, gl)
zword_t new_font;
z_globals *gl;
#endif
{
    zword_t old_font = (gl->font);

    if (new_font != old_font) {
        (gl->font) = new_font;
        set_font ((gl->font), gl);
    }

    store_operand (old_font, gl);

}/* set_font_attribute */

/*
 * set_colour_attribute
 *
 * Set the colour of the screen. Colour can be set on four things:
 *    Screen background
 *    Text typed by player
 *    Text written by game
 *    Graphics characters
 *
 * Colors can be set to 1 of 9 values:
 *    1 = machine default (IBM/PC = blue background, everything else white)
 *    2 = black
 *    3 = red
 *    4 = green
 *    5 = brown
 *    6 = blue
 *    7 = magenta
 *    8 = cyan
 *    9 = white
 *
 */

#ifdef __STDC__
void set_colour_attribute (zword_t foreground, zword_t background, z_globals *gl)
#else
void set_colour_attribute (foreground, background, gl)
zword_t foreground;
zword_t background;
z_globals *gl;
#endif
{

    if (foreground < 1 || foreground > 9 || background < 1 || background > 9)
        fatal ("Bad colour!", gl);

    flush_buffer (FALSE, gl);

    set_colours (foreground, background, gl);

    return;

}/* set_colour_attribute */




