/*  Sarien - A Sierra AGI resource interpreter engine
 *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
 *
 *  $Id: cycle.c,v 1.75 2001/11/06 01:07:38 cmatsuoka Exp $
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; see docs/COPYING for further details.
 */

#include "sarien.h"
#include "agi.h"
#include "text.h"
#include "sprite.h"
#include "graphics.h"
#include "keyboard.h"
#include "menu.h"

#define TICK_SECONDS    20

#ifdef USE_MOUSE
struct mouse mouse;
#endif

/**
 * Set up new room.
 * This function is called when ego enters a new room.
 * @param n room number
 */
void new_room (int n)
{
        struct vt_entry *v;
        int i;

        _D (_D_WARN "room %d", n);
        stop_sound ();

        i = 0;
        for_each_vt_entry (v) {
                v->entry = i++;
                v->flags &= ~(ANIMATED|DRAWN);
                v->flags |= UPDATE;
                v->step_time = 1;
                v->step_time_count = 1;
                v->cycle_time = 1;
                v->cycle_time_count = 1;
                v->step_size = 1;
        }

        /* function 0x10d0 */

        game.player_control = TRUE;
        game.block.active = FALSE;
        game.horizon = 36;
        game.vars[V_prev_room] = game.vars[V_cur_room];
        game.vars[V_cur_room] = n;
        game.vars[V_border_touch_obj] = 0;
        game.vars[V_border_code] = 0;
        game.vars[V_ego_view_resource] = game.view_table[0].current_view;

        agi_load_resource (rLOGIC, n);

        /* Reposition ego in the new room */
        switch (game.vars[V_border_touch_ego]) {
        case 1:
                game.view_table[0].y_pos = _HEIGHT - 1;
                break;
        case 2:
                game.view_table[0].x_pos = 0;
                break;
        case 3:
                game.view_table[0].y_pos = HORIZON + 1;
                break;
        case 4:
                game.view_table[0].x_pos = _WIDTH - game.view_table[0].x_size;
                break;
        }

        game.vars[V_border_touch_ego] = 0;
        setflag (F_new_room_exec, TRUE);

        game.exit_all_logics = TRUE;

        /* clear kb buffer */
        /* write status */
        /* function 0x38d5 */
}

static void reset_controllers ()
{
        int i;

        for (i = 0; i < MAX_DIRS; i++) {
                game.ev_scan[i].occured = FALSE;
                game.ev_keyp[i].occured = FALSE;
        }
}

static void interpret_cycle ()
{
        int old_sound, old_score;

        if (game.player_control)
                game.vars[V_ego_dir] = game.view_table[0].direction;
        else
                game.view_table[0].direction = game.vars[V_ego_dir];

        check_all_motions ();
        old_score = game.vars[V_score];
        old_sound = getflag (F_sound_on);

        game.exit_all_logics = FALSE;
        while (run_logic (0) == 0 && !game.quit_prog_now) {
                game.vars[V_word_not_found] = 0;
                game.vars[V_border_touch_obj] = 0;
                game.vars[V_border_code] = 0;
                old_score = game.vars[V_score];
                setflag (F_entered_cli, FALSE);
                game.exit_all_logics = FALSE;
                reset_controllers ();
        }
        reset_controllers ();

        game.view_table[0].direction = game.vars[V_ego_dir];

        if (game.vars[V_score] != old_score || getflag(F_sound_on) != old_sound)
                write_status ();

        game.vars[V_border_touch_obj] = 0;
        game.vars[V_border_code] = 0;
        setflag (F_new_room_exec, FALSE);
        setflag (F_restart_game, FALSE);
        setflag (F_restore_just_ran, FALSE);

        if (game.gfx_mode) {
                update_viewtable ();
                do_update ();
        }
}

/**
 * Print user input prompt.
 */
static void print_line_prompt ()
{
        _D (_D_WARN "input mode = %d", game.input_mode);
        if (game.input_mode == INPUT_NORMAL) {
                _D (_D_WARN "prompt = '%s'", agi_sprintf (game.strings[0]));
                print_text (game.strings[0], 0, 0, game.line_user_input, 1,
                        game.color_fg, game.color_bg);
                print_text ((char *)game.input_buffer, 0, 1,
                        game.line_user_input, game.cursor_pos + 1,
                        game.color_fg, game.color_bg);
                print_character (game.cursor_pos + 1, game.line_user_input,
                        game.cursor_char, game.color_fg, game.color_bg);
                do_update ();   /* synchronous */
        }
}


void update_timer ()
{
        if (!game.clock_enabled)
                return;

        clock_count++;
        if (clock_count <= TICK_SECONDS)
                return;

        clock_count -= TICK_SECONDS;
        setvar (V_seconds, getvar (V_seconds) + 1);
        if (getvar (V_seconds) < 60)
                return;

        setvar (V_seconds, 0);
        setvar (V_minutes, getvar (V_minutes) + 1);
        if (getvar (V_minutes) < 60)
                return;

        setvar (V_minutes, 0);
        setvar (V_hours, getvar (V_hours) + 1);
        if (getvar (V_hours) < 24)
                return;

        setvar (V_hours, 0);
        setvar (V_days, getvar (V_days) + 1);
}


static int old_mode = -1;
void new_input_mode (int i)
{
        old_mode = game.input_mode;
        game.input_mode = i;
}

void old_input_mode ()
{
        game.input_mode = old_mode;
}


/* If main_cycle returns FALSE, don't process more events! */
int main_cycle ()
{
        unsigned int key, kascii;
        struct vt_entry *v = &game.view_table[0];

        poll_timer ();          /* msdos driver -> does nothing */
        update_timer ();

        if (game.ver == 0) {
                message_box ("Sarien didn't recognize this game and may "
                        "execute it with an incorrect interpreter version. "
                        "Please check the configuration file; if this game "
                        "is not listed, please contact the authors.");
                game.ver = -1;
        }

        key = do_poll_keyboard ();

#ifdef USE_MOUSE
        /* In AGI Mouse emulation mode we must update the mouse-related
         * vars in every interpreter cycle.
         */
        if (opt.agimouse) {
                game.vars[28] = mouse.x / 2;
                game.vars[29] = mouse.y;
        }
#endif

#ifdef USE_CONSOLE
        if (key == KEY_PRIORITY) {
                erase_both ();
                debug.priority = !debug.priority;
                show_pic ();
                blit_both ();
                commit_both ();
                key = 0;
        }

        if (key == KEY_STATUSLN) {
                debug.statusline = !debug.statusline;
                write_status ();
                key = 0;
        }
#endif

        /* Click-to-walk mouse interface */
        if (game.player_control && v->flags & ADJ_EGO_XY) {
                v->direction = get_direction (v->x_pos, v->y_pos,
                        v->parm1, v->parm2, v->step_size);

                if (v->direction == 0)
                        in_destination (v);
        }

        kascii = KEY_ASCII (key);

        if (!console_keyhandler (key)) {
                if (kascii) setvar (V_key, kascii);
                switch (game.input_mode) {
                case INPUT_NORMAL:
                        if (!handle_controller (key)) {
                                if (key == 0)
                                        break;
                                handle_keys (key);
                                /* commented out to close bug #438872
                                 * if (key) game.keypress = key;
                                 */
                        }
                        break;
                case INPUT_GETSTRING:
                        handle_controller (key);
                        handle_getstring (key);
                        break;
                case INPUT_MENU:
                        menu_keyhandler (key);
                        console_cycle ();
                        return FALSE;
                case INPUT_NONE:
                        handle_controller (key);
                        if (key) game.keypress = key;
                        break;
                }
        } else {
                if (game.input_mode == INPUT_MENU) {
                        console_cycle ();
                        return FALSE;
                }
        }

        console_cycle ();

        if (game.msg_box_ticks > 0)
                game.msg_box_ticks--;

        return TRUE;
}

int run_game ()
{
        int ec = err_OK;

        _D (_D_WARN "initializing...");
        stop_sound ();

        setflag (F_logic_zero_firsttime, TRUE); /* not in 2.917 */
        setflag (F_new_room_exec, TRUE);        /* needed for MUMG and SQ2! */
        setflag (F_restart_game, FALSE);
        setflag (F_sound_on, TRUE);             /* enable sound */
#ifdef OPENVMS
        setvar (V_time_delay, 0);               /* "fastest" speed - default for OpenVMS */
#else
        setvar (V_time_delay, 2);               /* "normal" speed */
#endif

        game.gfx_mode = TRUE;
        game.quit_prog_now = FALSE;
        game.clock_enabled = TRUE;
        game.input_mode = INPUT_NONE;
        game.line_user_input = 22;

#ifdef USE_MOUSE
        if (opt.agimouse)
                report ("Using AGI Mouse 1.0 protocol\n");
#endif

        report ("Running AGI script.\n");

#ifdef USE_CONSOLE
        console.count = 5;
        console_prompt ();
#endif

        setflag (F_entered_cli, FALSE);
        setflag (F_said_accepted_input, FALSE);

        _D (_D_WARN "Entering main loop");
        do {
                if (!main_cycle ())
                        continue;

                if (getvar (V_time_delay) == 0 || (1 + clock_count) % getvar (V_time_delay) == 0) {
                        if (!game.has_prompt && game.input_mode == INPUT_NORMAL) {
                                print_line_prompt ();
                                game.has_prompt = 1;
                        }
                        interpret_cycle ();
                        setflag (F_entered_cli, FALSE);
                        setflag (F_said_accepted_input, FALSE);
                }

                if (game.quit_prog_now == 0xff)
                        ec = err_RestartGame;

        } while (game.quit_prog_now == 0);

        stop_sound ();

        return ec;
}


