#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <oslib/os.h>
#include <oslib/wimp.h>

#include "options.h"

#include "ztypes.h"
#include "osdepend.h"
#include "text.h"
#include "screenio.h"
#include "v6.h"

#include "v6ro.h"
#include "roinput.h"
#include "rosound.h"
#include "riscosio.h"
#include "riscoswimp.h"
#include "unicutils.h"

int in_input_line;
int input_x;

#ifdef ALLOW_RECALL
zword_t *line_buffer[LINES_RECORDED];
int current_line;
#endif

/*
 * Get next character (as ZSCII)
 */
int input_character(int timeout)
{
    int c;

#ifdef ALLOW_SPEECH
    if (use_speech)
        flush_speech();
#endif

    sound_no_input=0;

    os_update_window();

    caret_enabled=TRUE;

    if (!showcaret && h_type!=V6)
    	game_wants_caret=FALSE;

    place_caret();

    if (!timeout)
    {
        c=NextKey();
        while (!c || c>=0x100)
        {
            poll();
    	    c=NextKey();
	}
    }
    else
    {
        os_t endtime=os_read_monotonic_time() + timeout * 10;

    	c=NextKey();

    	while (!c || c>=0x100)
        {
            int nulls = claim_null_events();

            if (!nulls)
            	poll_idle(endtime);
            else
            	poll();

            release_null_events();

            c=NextKey();
            if (c || os_read_monotonic_time() > endtime)
            	break;
        }
    }

    if (h_type!=V6)
    	game_wants_caret=TRUE;

    caret_enabled=FALSE;

    place_caret();

    return c;
}

#ifdef ALLOW_RECALL
void store_line(const zword_t *buffer, int buflen)
{
    free(line_buffer[current_line]);
    line_buffer[current_line]=calloc(buflen+1, sizeof(zword_t));
    memcpy(line_buffer[current_line], buffer, buflen*sizeof(zword_t));
    line_buffer[current_line][buflen]='\0';
}
#endif

void input_blank(const zword_t *buffer, int left)
{
    if (h_type==V6)
    	v6ro_input_blank(buffer, left);
    else
    	v5ro_input_blank(buffer);
}

static void input_place_caret(const zword_t *buffer, int left, int pos)
{
    if (h_type==V6)
    	v6ro_input_place_caret(buffer, left, pos);
    else
    	v5ro_input_place_caret(buffer, left, pos);
}

int input_get_left(const zword_t *buffer)
{
    if (h_type==V6)
    	return v6ro_input_get_left(buffer);
    else
    	return v5ro_input_get_left(buffer);
}

static int input_will_fit(const zword_t *buffer, unsigned extra_z_char)
{
    if (h_type==V6)
    	return v6ro_input_will_fit(buffer, extra_z_char);
    else
    	return v5ro_input_will_fit(buffer, extra_z_char);
}

#ifdef ALLOW_EDITING
static int is_punc(char c)
{
    return c==' ' || c=='.' || c==',';
}
#endif

void roinput_abort(void)
{
    in_input_line = FALSE;
    if (caret_enabled)
    {
        caret_enabled = FALSE;
        place_caret();
    }
}

static int my_end_key(int c)
{
    return (c == key_FILE || c == zscii_F2 || c == zscii_F3) && awaiting_file != -1;
}

int os_read(int buflen, zword_t *buffer, int timeout, int continuing)
{
    os_t endtime;
    int c, u;
    #ifdef ALLOW_RECALL
    int search_line=current_line;
    #endif
    static int pos;
    int length;
    int left;
    int redraw;

    UNSET(endtime);

#ifdef ALLOW_SPEECH
    if (use_speech)
        flush_speech();
#endif

    in_input_line=TRUE;

    if (timeout)
    	endtime=os_read_monotonic_time() + timeout * 10;

    sound_no_input=0;
    optimise_status_line();
    os_update_window();

    length=strlen_u(buffer);
    if (!continuing)
    	pos=length;
    else
    	if (pos>length)
    	    pos=length;

    redraw=length>0;

    left=input_get_left(buffer);

    caret_enabled=TRUE;

    input_place_caret(buffer, left, pos);

    for (;;)
    {
        c=NextKey();

        while (!c)
        {
            if (timeout)
            {
                int nulls = claim_null_events();

                if (!nulls)
                    poll_idle(endtime);
                else
                    poll();

                release_null_events();

                if (os_read_monotonic_time() >= endtime)
                {
                    caret_enabled=FALSE;
                    place_caret();
                    in_input_line=FALSE;
                    return 0;
                }
            }
            else
            {
        	poll();
            }

            c=NextKey();
        }

        u = zscii_to_unicode(c);

        #ifdef ALLOW_RECALL
        if (command_recall && (c==zscii_UP || c==zscii_DOWN))
        {
            /* Up or down - first store current line */
            if (search_line==current_line)
                store_line(buffer, length);

            input_blank(buffer, left);

            if (c==zscii_UP)
            {
                if (--search_line<0)
                    for (search_line=LINES_RECORDED-1;
                         line_buffer[search_line]==0;
                         search_line--)
                        ;
            }
            else
            {
                /* c==zscii_DOWN */
                search_line++;
                if (search_line>=LINES_RECORDED || line_buffer[search_line]==NULL)
                    search_line=0;
            }

            strncpy_u(buffer, line_buffer[search_line], buflen);

            buffer[buflen]='\0'; /* Just to make sure */

            length=pos=strlen_u(buffer);

            if (h_type != V6)
                display_string(buffer);
            else
                v6ro_display_string(buffer, strlen_u(buffer));
            redraw=0;

            os_update_window();
            input_place_caret(buffer, left, pos);
        }
        else
        #endif
        #ifdef ALLOW_EDITING
        if (line_editing && (c==zscii_LEFT || c==zscii_RIGHT ||
                             c==key_CTRL_LEFT || c==key_CTRL_RIGHT ||
                             c==key_SHIFT_LEFT || c==key_SHIFT_RIGHT))
        {
            if (c==zscii_LEFT)
            {
                if (pos==0)
                    beep();
                else
                    input_place_caret(buffer, left, --pos);
            }
            else if (c==zscii_RIGHT)
            {
                if (pos==length)
                    beep();
                else
                    input_place_caret(buffer, left, ++pos);
            }
            else if (c==key_CTRL_LEFT)
                input_place_caret(buffer, left, pos=0);
            else if (c==key_CTRL_RIGHT)
            	input_place_caret(buffer, left, pos=length);
            else if (c==key_SHIFT_LEFT && pos > 0)
            {
                do
                    pos--;
                while (pos > 0 && !(!is_punc(buffer[pos-1]) && is_punc(buffer[pos])));
                input_place_caret(buffer, left, pos);
            }
            else if (c==key_SHIFT_RIGHT && pos < length)
            {
                do
                    pos++;
                while (pos < length && !(is_punc(buffer[pos-1]) && !is_punc(buffer[pos])));
                input_place_caret(buffer, left, pos);
            }
        }
        else
        #endif
        if (c==zscii_DELETE)
        {
            if (pos == 0)
                beep();
            else
            {
                input_blank(buffer, left);

                memmove(buffer+pos-1, buffer+pos, (length-pos+1)*sizeof(zword_t));

                length--; pos--;

                if (h_type != V6)
                    display_string(buffer);
                else
                    v6ro_display_string(buffer, strlen_u(buffer));

	    	input_place_caret(buffer, left, pos);

    	    	/* Hack to reduce flicker */
    	    	if (pos==length && !redraw && h_type != V6)
    	    	    changed_box.x0=input_x-8*400;

    	    	redraw=0;

                /* Redraw input line */
	    	os_update_window();
            }
        }
        #ifdef ALLOW_EDITING
        else if (c==key_COPY)
        {
            if (pos == length)
                beep();
            else
            {
                input_blank(buffer, left);

                memmove(buffer+pos, buffer+pos+1, (length-pos)*sizeof(zword_t));

                length--;

                if (h_type != V6)
                    display_string(buffer);
                else
                    v6ro_display_string(buffer, strlen_u(buffer));
                redraw=0;

                /* Redraw input line */
	    	os_update_window();
	    	input_place_caret(buffer, left, pos);
            }
        }
        else if (c==key_CTRL_COPY)
        {
            input_blank(buffer, left);

    	    buffer[pos]='\0';

            length=pos;

            if (h_type != V6)
                display_string(buffer);
            else
                v6ro_display_string(buffer, strlen_u(buffer));
            redraw=0;

            /* Redraw input line */
 	    os_update_window();
 	    input_place_caret(buffer, left, pos);
        }
        #endif
        else if (c==key_CTRL_U)
        {
            input_blank(buffer, left);

            length=0; pos=0; redraw=0;

            buffer[0]='\0';

 	    os_update_window();
 	    input_place_caret(buffer, left, 0);
        }
        else
        {
            #ifdef ALLOW_RECALL
            if (c == NEWLINE && command_recall)
            {
                store_line(buffer, length);
                if (++current_line >= LINES_RECORDED)
                    current_line=0;
            }
            #endif

            if (c==zscii_NEWLINE || c==zscii_ESCAPE || end_key(c) || my_end_key(c))
            {
                caret_enabled=FALSE;
                place_caret();
                in_input_line=FALSE;
                return c==27 ? -1 : c;
            }

  	    /* Make sure what's left is a valid ZSCII printable */
       	    if (c < 32 || c>126 && c<155 || c>=155+zscii_to_unicode_table_size)
        	continue;

            if (length == buflen || !input_will_fit(buffer, u))
                /* Ring bell if buffer is full */
                beep();
            else
            {
                /* Put key in buffer and display it */

    	    	if (pos == length && h_type!=V6)
    	    	{
    	    	    /* Optimisation */
    	    	    display_char(u);
    	    	    buffer[pos++]=u; buffer[pos]='\0';
    	    	    length++;
    	    	}
    	    	else
    	    	{
    	    	    input_blank(buffer, left);
    	    	    memmove(buffer+pos+1, buffer+pos, (length-pos+1)*sizeof(zword_t));
    	    	    buffer[pos++]=u; length++;
    	    	    if (h_type != V6)
    	    	        display_string(buffer);
    	    	    else
    	    	        v6ro_display_string(buffer, strlen_u(buffer));
    	    	    redraw=0;
    	    	}

                os_update_window();
                input_place_caret(buffer, left, pos);
            }
        }

    }

    return 0;
}

