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

#include "macros.h"
#include "osspriteop.h"
#include "wimp.h"
#include "colourtrans.h"
#include "toolbox.h"
#include "menu.h"
#include "event.h"

#include "options.h"

#include "ztypes.h"
#include "unicutils.h"
#include "osdepend.h"
#include "text.h"
#include "v6.h"
#include "pngro.h"
#include "v6ro.h"
#include "graphics.h"

#include "fileio.h"
#include "riscosio.h"
#include "riscoswimp.h"
#include "roinput.h"
#include "rosound.h"

#define os_MODE16BPP90X45 ((os_mode) ((5 << 27) | (45 << 14) | (90 << 1) | 1))
#define os_MODE32BPP90X45 ((os_mode) ((6 << 27) | (45 << 14) | (90 << 1) | 1))
#define os_MODE16BPP90X90 ((os_mode) ((5 << 27) | (90 << 14) | (90 << 1) | 1))
#define os_MODE32BPP90X90 ((os_mode) ((6 << 27) | (90 << 14) | (90 << 1) | 1))

osspriteop_area *v6_screen_area;
osspriteop_header *v6_screen;
osspriteop_trans_tab *v6_trans_tab;
static osspriteop_save_area *savearea;
int screencols;
int hires_screen;
static os_colour fg_col, bg_col;
static os_coord origin;
static const os_factors *gfx_fac;
static const os_factors gfx_fac_hires={1, 2, 1, 1};
static int last_char;
static os_t last_update;
int update_often;

/* Memory for the non-standard colours for each window */
unsigned colourmem[8][2];

static int v6ro_get_colour(void);

static unsigned v6ro_colour_number_to_palette(unsigned n)
{
    switch (bitmap_depth)
    {
        default:
        {
            os_colour *pal=(os_colour *)(v6_screen+1);
            return pal[n*2];
        }
        case 4:
        {
            unsigned r,g,b;
            r = n & 0x1F;          r = (r << 3) | (r >> 2);
            g = (n >> 5) & 0x1F;   g = (g << 3) | (g >> 2);
            b = (n >> 10) & 0x1F;  b = (b << 3) | (b >> 2);
            return (b << 24) | (g << 16) | (r << 8);
        }
        case 5:
            return n << 8;
    }
}

static void v6ro_mark_area(int w, int minx, int maxx, int miny, int maxy)
{
    if (w>=0)
    {
        minx+=wind_prop[w][WPROP_X]-2;
        maxx+=wind_prop[w][WPROP_X]-2;
        miny+=wind_prop[w][WPROP_Y]-2;
        maxy+=wind_prop[w][WPROP_Y]-2;
    }
    else
    {
    	minx-=1;
    	maxx-=1;
    	miny-=1;
    	maxy-=1;
    }

    if (minx < -2) minx = 0;
    if (miny < -2) miny = 0;
    if (maxx > 642) maxx = 642;
    if (maxy > 402) maxy = 402;

    if (changed_box.x0 > minx)
    	changed_box.x0 = minx;
    if (changed_box.x1 < maxx)
    	changed_box.x1 = maxx;
    if (changed_box.y0 > miny)
        changed_box.y0 = miny;
    if (changed_box.y1 < maxy)
    	changed_box.y1 = maxy;
}

void v6ro_update_window(void)
{
    int temp;

    if (changed_box.x1==0)
    	return;

    changed_box.x0*=V6_PIX_X;
    changed_box.x1*=V6_PIX_X;
    temp=changed_box.y0;
    changed_box.y0=-changed_box.y1*V6_PIX_Y;
    changed_box.y1=-temp*V6_PIX_Y;
    switchoutput(-1);
    do_update_window(&changed_box);
    switchoutput(+1);
    changed_box.x0=640;
    changed_box.x1=0;
#ifdef HIRES_COORDS
    changed_box.y0=400;
#else
    changed_box.y0=200;
#endif
    changed_box.y1=0;
    last_update = os_read_monotonic_time();
}

void v6ro_redraw_window(wimp_draw *r)
{
    int rx, ry;
    os_box box;

    /* Relative x+y offsets */
    rx=r->box.x0 - r->xscroll;
    ry=r->box.y1 - r->yscroll;

    box.x0=r->clip.x0 - rx;
    box.x1=r->clip.x1 - rx;
    box.y0=r->clip.y0 - ry;
    box.y1=r->clip.y1 - ry;

    if (log2bpp == bitmap_depth && log2bpp >= 4
        && factors.xmul == factors.xdiv
        && factors.ymul == factors.ydiv)
        osspriteop_put_sprite_user_coords(osspriteop_PTR,
                                          v6_screen_area,
                                          (osspriteop_id) v6_screen,
                                          rx,
                                          ry-800,
                                          os_ACTION_OVERWRITE);
    else
        osspriteop_put_sprite_scaled(osspriteop_PTR,
                                     v6_screen_area,
                                     (osspriteop_id) v6_screen,
                                     rx,
                                     ry-800,
                                     os_version >= RISC_OS_350 ? osspriteop_USE_PALETTE
                                                               : NONE,
                                     &factors,
                                     log2bpp <= 3 ? v6_trans_tab : 0);

    if ((box.x0 < 0 || box.x1 > SWidthOS || box.y1 > 0 || box.y0 < - SHeightOS) && border)
        draw_border(rx, ry);

   /* os_plot(os_MOVE_TO, r->clip.x0, r->clip.y0);
    os_plot(os_PLOT_TO, r->clip.x0, r->clip.y1-1);
    os_plot(os_PLOT_TO, r->clip.x1-1, r->clip.y1-1);
    os_plot(os_PLOT_TO, r->clip.x1-1, r->clip.y0);
    os_plot(os_PLOT_TO, r->clip.x0, r->clip.y0);*/
}

void switchoutput(int dir)
{
    static int screenno=0;
    static int c0,c1,c2,c3;

    if (dir > 0 && screenno==0)
    {
        osspriteop_switch_output_to_sprite(osspriteop_PTR, v6_screen_area,
                                           (osspriteop_id) v6_screen,
                                           savearea, &c0, &c1, &c2, &c3);
    	screenno=1;
    }
    else if (dir < 0 && screenno==1)
    {
    	osspriteop_unswitch_output(c0, c1, c2, c3);
    	screenno=0;
    }
}

static void v6ro_do_gfx(const zword_t *text, int x, int y, int bg, int fg, int n)
{
    os_PALETTE(2) pal;
    int i;
    osspriteop_action flags;
    osspriteop_TRANS_TAB(16) trans_tab;

    if (!gfx_area)
    	load_gfx();

    pal.entries[0]=bg;
    pal.entries[1]=fg;

    if (os_version < RISC_OS_360)
    	flags=NONE;
    else
    	flags=colourtrans_RETURN_WIDE_ENTRIES;

    colourtrans_generate_table(0, (os_palette *) &pal,
                               os_CURRENT_MODE, colourtrans_CURRENT_PALETTE,
                               (osspriteop_trans_tab *) &trans_tab,
                               flags, NULL, NULL);

    if (os_version < RISC_OS_360)
    	flags=os_ACTION_OVERWRITE;
    else
    	flags=os_ACTION_OVERWRITE | osspriteop_GIVEN_WIDE_ENTRIES;

    for (i=0; i<n; i++)
    {
    	osspriteop_put_sprite_scaled(osspriteop_PTR,
                                    gfx_area,
                                    (osspriteop_id) gfx_ptr[text[i]-32],
                                    x*V6_PIX_X, -y*V6_PIX_Y-30,
                                    flags,
                                    gfx_fac,
                                    (osspriteop_trans_tab *) &trans_tab);
        x+=8;
    }
}

void v6ro_display_string(const zword_t *t, int len)
{
    int w=screen_window;
    unsigned *const wprop=wind_prop[w];
    int x=wprop[WPROP_X_CURSOR];
    int y=wprop[WPROP_Y_CURSOR];
    int font=wprop[WPROP_FONT_NUMBER];
    int style=wprop[WPROP_TEXT_STYLE];
    int f, b;
    int width;
    int generous=FALSE;
    int i, this;
    int n=0;
    zword_t tex[128];
    const zword_t *text=tex;

    if (fixed_space_bit)
    	style|=FIXED_FONT;

    if (font==4)
    {
        font=TEXT_FONT;
        style|=FIXED_FONT;
    }

    if (style & REVERSE)
    {
        f=bg_col; b=fg_col;
    }
    else
    {
        f=fg_col; b=bg_col;
    }

    if ((style & FIXED_FONT) || font==3)
    {
    	width=16*len;
    	for (i=0; i<len; i++)
    	{
    	    this = t[i];
    	    if (font == 3 && (this < 0x20 || this > 0x7E))
    	        this = 0x7E; /* Inverted ? */
    	    tex[i] = this;
    	}
    }
    else
    {
        #ifdef ALLOW_QUOTES
        int last;
        for (i=0; i<len; i++)
        {
            this=t[i];
            last = n==0 ? last_char : tex[n-1];

            if (smartquotes && (this=='i' || this=='l' || this=='-' || this=='\''
                    || this == '`' || this=='\"'))
            {
                if (this=='\'' && unicode_to_native(0x2018, 0))
                {
                    if (last != ' ' && last != '(' && last != 0x201C /**/)
                        tex[n++]=0x2019 /**/;
                    else
                        tex[n++]=0x2018 /**/;
                }
                else if (this=='`' && unicode_to_native(0x2018, 0))
                    tex[n++]=0x2018 /**/;
                else if (this=='\"' && unicode_to_native(0x201C, 0))
                {
                    if (last != ' ' && last != '(' && last != '[' && last != 0x2018 /**/)
                        tex[n++]=0x201D/**/;
                    else
                        tex[n++]=0x201C/**/;
                }
                else if ((this=='i' || this=='-' || this=='l') && !in_input_line)
                {
                    /* Check for ligatures */
                    if (n>0 && last=='f' && this=='i' && unicode_to_native(0xFB01, 0))
                        tex[n-1]=0xFB01;
                    else if (n>0 && last=='f' && this=='l' && unicode_to_native(0xFB02, 0))
                        tex[n-1]=0xFB02;
                    #if 0
                    /*else if (n>0 && (last=='-' || last=='') && this=='-')
                        tex[n-1]=''; This can't happen, unfortunately */
                    else if (last==' ' && this=='-')
                    	tex[n++]='';
                    #endif
                    else if ((last==' ' || last==0x2013) && this=='-' && unicode_to_native(0x2013, 0))
                    	tex[n++]=0x2013;
                    else
                    	tex[n++]=t[i];
                }
                else
                    tex[n++]=t[i];
            }
            else
            	tex[n++]=t[i];
        }
        last_char=tex[n-1];
        len=n;
        #else
        text = t;
        #endif

        for (i=0, width=0; i<len; i++)
            width+=fwidth[style>>1][unicode_to_native(text[i], '?')];

        width=(width+200)/400;
    }

    if ((wprop[WPROP_ATTRIBUTES] & (WATTR_WRAPPING|WATTR_BUFFER))==WATTR_WRAPPING)
    {
        if (x-1+(width+1)/2>wprop[WPROP_X_WIDTH]-wprop[WPROP_R_MARGIN])
        {
            /* Right, we need to character wrap at some stage. Let's figure out where */
            for (i=0, width=0; i<len; i++)
            {
                if ((style & FIXED_FONT) || font==3)
                    width+=16*400;
                else
                    width+=fwidth[style>>1][unicode_to_native(text[i], '?')];

                if ((x-1)*800+width>wprop[WPROP_X_WIDTH]*800-wprop[WPROP_R_MARGIN]*800)
                {
                    /* Check whether the overrunning characters are just spaces */
                    int j,s;
                    for (j=i,s=0; j<n; j++)
                    {
                        if (text[j]!=' ')
                        {
                            s=1;
                            break;
                        }
                    }

    	    	    if (s)
    	    	    {
                    	v6ro_display_string(text, i);
                    	script_new_line();
                    	v6_output_new_line();
                    	v6ro_display_string(text+i, len-i);
                    	return;
                    }
                }
            }

            /* Paranoia - shouldn't get here */
            width=(width+200)/400;

        }
    }

    if (font==3)
    {
    	v6ro_do_gfx(text, x, y, b, f, len);
    }
    else
    {
        int bwidth;

        for (i=0; i<len; i++)
        {
            if (text[i]>=128)
            {
                generous=TRUE;
        	break;
            }
        }

        if (style & REVERSE)
            os_set_colour(os_COLOUR_SET_BG, fg);

    	bwidth=MIN((wprop[WPROP_X_WIDTH] - wprop[WPROP_R_MARGIN] - (x-1))*2, width);

    	os_plot(os_MOVE_BY, bwidth-1, -30);
    	os_plot(os_PLOT_BG_BY|os_PLOT_RECTANGLE, -(bwidth-1), 30);
        colourtrans_set_font_colours(fontno[style>>1], b, f, 14, 0, 0, 0);
        if (style & FIXED_FONT)
            font_paint_u(fontno[style>>1], text,
                       font_GIVEN_LENGTH | font_GIVEN_BLOCK | font_GIVEN_FONT,
                       (origin.x+x*V6_PIX_X)*400, (origin.y-V6_PIX_Y*y+10-32)*400,
                       &fblock, NULL, len);
        else
            font_paint_u(fontno[style>>1], text,
                       font_GIVEN_LENGTH | font_OS_UNITS | font_GIVEN_FONT,
                       origin.x+x*V6_PIX_X, origin.y-V6_PIX_Y*y+10-32, NULL, NULL, len);
        if (style & REVERSE)
            os_set_colour(os_COLOUR_SET_BG, bg);
    }
    v6ro_mark_area(w, x, x+(width+V6_PIX_X)/V6_PIX_X+V6_PIX_X/*for f*/, y-(generous?2:0), y+V6_CHAR_H);
    v6ro_move_cursor(y, x+(width+1)/V6_PIX_X);
}

void v6ro_display_char(int c)
{
    zword_t buf[1];

    if (c=='\n')
    {
        v6ro_scroll_line();
        return;
    }

    buf[0]=c;
    v6ro_display_string(buf, 1);
}

int v6ro_text_length(const zword_t *line_buffer, int len)
{
    return (text_length(line_buffer, len,
                                    wind_prop[screen_window][WPROP_FONT_NUMBER],
                                    wind_prop[screen_window][WPROP_TEXT_STYLE]>>1,
                                    fixed_space_bit)+400)/800;
}

int v6ro_text_length_z(const char *line_buffer, int len)
{
    zword_t *l = calloc(len, sizeof (zword_t));
    int i;

    for (i=0; i<len; i++)
        l[i] = zscii_to_unicode(line_buffer[i], '?');

    i = (text_length(l, len,
                       wind_prop[screen_window][WPROP_FONT_NUMBER],
                       wind_prop[screen_window][WPROP_TEXT_STYLE]>>1,
                       fixed_space_bit)+400)/800;

    free(l);

    return i;
}

int v6ro_fit_word(const zword_t *line_buffer, int len)
{
    int width=v6ro_text_length(line_buffer, len);

    if (width==0)
    	return 1;

    return wind_prop[screen_window][WPROP_X_CURSOR]-1+width <= wind_prop[screen_window][WPROP_X_WIDTH]-wind_prop[screen_window][WPROP_R_MARGIN];
}

void v6ro_scroll_pixels(int w, int n)
{
    int x, y, height, width;

    x=wind_prop[w][WPROP_X];
    y=wind_prop[w][WPROP_Y];
    width=wind_prop[w][WPROP_X_WIDTH];
    height=wind_prop[w][WPROP_Y_HEIGHT];

    v6ro_update_window();

    v6ro_select_window(w);

    /* Scroll window in screen memory */
    if (n>0)
    {
        os_plot(os_MOVE_TO, V6_PIX_X, -height*V6_PIX_Y-1);
        os_plot(os_MOVE_TO, V6_PIX_X*width, -(n+1)*V6_PIX_Y);
        os_plot(os_PLOT_BLOCK|os_PLOT_TO, V6_PIX_X, -(height-n)*V6_PIX_Y-1);

        if (n>=height/2)
        {
            os_plot(os_MOVE_TO, V6_PIX_X, -height*V6_PIX_Y-1);
            os_plot(os_PLOT_RECTANGLE|os_PLOT_BG_BY, (width-1)*V6_PIX_X, n*V6_PIX_Y-1);
        }
    }
    else
    {
        os_plot(os_MOVE_TO, V6_PIX_X, -(height+n)*V6_PIX_Y-1);
        os_plot(os_MOVE_TO, V6_PIX_X*width, -V6_PIX_Y);
        os_plot(os_PLOT_BLOCK|os_PLOT_TO, V6_PIX_X, -height*V6_PIX_Y-1);

        if (n>=height/2)
        {
            os_plot(os_MOVE_TO, V6_PIX_X, -(height+n)*V6_PIX_Y-1);
            os_plot(os_PLOT_RECTANGLE|os_PLOT_BG_BY, (width-1)*V6_PIX_X, -V6_PIX_Y);
        }
    }

    if (w==screen_window)
        v6ro_move_cursor(wind_prop[w][WPROP_Y_CURSOR],
                         wind_prop[w][WPROP_X_CURSOR]);

    /* Do wimp block copy for desktop */
    switchoutput(-1);
    wimp_block_copy(ScreenW,
                    (x-1)*V6_PIX_X,
                    -(y-1+height)*V6_PIX_Y,
                    (x-1+width)*V6_PIX_X,
                    -(y+n-1)*V6_PIX_Y,
                    (x-1)*V6_PIX_X,
                    -(y-1+height-n)*V6_PIX_Y);
    v6ro_mark_area(w, 1, 1+width,
                      1+height-n, 1+height);

    /* Move cursor */
    v6ro_update_window();     /* This unswitches output itself */
    complete_redraw();
    switchoutput(+1);
    v6ro_select_window(screen_window);
}

#define Y_CURSOR (wind_prop[screen_window][WPROP_Y_CURSOR])
#define X_CURSOR (wind_prop[screen_window][WPROP_X_CURSOR])
#define HEIGHT (wind_prop[screen_window][WPROP_Y_HEIGHT])
#define L_MARGIN (wind_prop[screen_window][WPROP_L_MARGIN])
#define ATTRIBUTES (wind_prop[screen_window][WPROP_ATTRIBUTES])

void v6ro_scroll_line(void)
{
    last_char=' ';

    if (Y_CURSOR > HEIGHT - 2*V6_CHAR_H + 1 && (ATTRIBUTES & WATTR_SCROLL))
    {
        v6ro_scroll_pixels(screen_window, Y_CURSOR-(HEIGHT-2*V6_CHAR_H+1));
        v6ro_move_cursor(HEIGHT-V6_CHAR_H+1, L_MARGIN+1);
    }
    else
    {
        /* Move cursor */
        int x, y;
        x=(zword_t)(L_MARGIN+1);
        y=(zword_t)(Y_CURSOR+V6_CHAR_H);
        v6ro_move_cursor(y, x);
        X_CURSOR=x;
        Y_CURSOR=y;
        /*update_window();*/
    }
}

static void v6ro_exit(void)
{
    switchoutput(-100);
}

#if 0
void add_palette_entry(int r, int g, int b)
{
    os_colour col=(b<<24)+(g<<16)+(r<<8);
    os_colour *palptr;
    int i;

    if (screencols==256)
    	return;

    palptr=(os_colour *)(v6_screen+1);

    for (i=0; i<screencols; i++)
        if (palptr[i*2]==col)
            return;

    palptr[screencols*2]=palptr[screencols*2+1]=col;

    screencols+=1;

    /* Because we've poked the palette */
    colourtrans_invalidate_cache();

    /* Sort out the desktop redraw routine's translation table */
    switchoutput(-1);
    palettechange_handler(NULL, NULL);
    switchoutput(+1);
}
#endif

int bitmap_width, bitmap_height, bitmap_depth;

void v6ro_initialize_screen(void)
{
    int savesize, yeig, height, width, log2bpp;
    os_mode mode;
    int area_size;
    zword_t *loading;
    int loading_len;

    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR, &yeig);
    os_read_mode_variable(os_CURRENT_MODE, os_MODEVAR_LOG2_BPP, &log2bpp);

    if (yeig<=1)
    {
    	height=400; hires_screen=TRUE;
    	bitmap_width = 640;
    	bitmap_height = 400;
    	switch (log2bpp)
    	{
    	    default: mode = os_MODE8BPP90X90; bitmap_depth = 3; break;
    	    case 4: mode = os_MODE16BPP90X90; bitmap_depth = 4; break;
    	    case 5: mode = os_MODE32BPP90X90; bitmap_depth = 5; break;
    	}
    }
    else
    {
        height=200; hires_screen=FALSE;
    	bitmap_width = 640;
    	bitmap_height = 200;
    	switch (log2bpp)
    	{
    	    default: mode = os_MODE8BPP90X45; bitmap_depth = 3; break;
    	    case 4: mode = os_MODE16BPP90X45; bitmap_depth = 4; break;
    	    case 5: mode = os_MODE32BPP90X45; bitmap_depth = 5; break;
    	}
    }

    /* First, we want to allocate a screen sprite. The sprite will be
       640*(200|400), 256|32K|16M colours, with palette if 256 cols */

    area_size=640*height;
    if (log2bpp == 4) area_size *= 2;
    else if (log2bpp == 5) area_size *= 4;
    else area_size += 8*256;
    area_size += 16 + 44;

    v6_screen_area=(osspriteop_area *)malloc(area_size);

    if (v6_screen_area == NULL)
    	fatal_lookup("NoMem");

    v6_screen_area->size=area_size;
    v6_screen_area->first=16;
    osspriteop_clear_sprites(osspriteop_USER_AREA, v6_screen_area);

    osspriteop_create_sprite(osspriteop_USER_AREA, v6_screen_area, "screen",
                             FALSE, 640, height, mode);

    v6_screen=osspriteop_select_sprite(osspriteop_USER_AREA, v6_screen_area,
                                       (osspriteop_id) "screen");

    if (bitmap_depth == 3)
        osspriteop_create_true_palette(osspriteop_PTR, v6_screen_area,
                                          (osspriteop_id) v6_screen);

    savesize=osspriteop_read_save_area_size(osspriteop_PTR, v6_screen_area,
                                                   (osspriteop_id) v6_screen);

    savearea=(osspriteop_save_area *)malloc(savesize);

    if (savearea == NULL)
    	fatal_lookup("NoMem");

    savearea->a[0]=0;

    modechange_handler(NULL, NULL);

    gfx_fac=hires_screen?&gfx_fac_hires:NULL;

    /*colourtrans_write_palette(v6_screen_area, (osspriteop_id) v6_screen,
                              (os_palette *) palette, colourtrans_PALETTE_FOR_SPRITE);*/

    switchoutput(+1);
    atexit(v6ro_exit);

    find_fonts();

    os_join_cursors();

    if (hires_screen)
    {
        // Select a system font size of 8x16
        // (VDU 23,17,7,2,8;16;0,0)
        os_writen("\x17\x11\x07\x02\x08\x00\x10\x00\x00\x00", 10);
    }

    set_colours(1, 1, -1);
    v6ro_clear_screen();

    loading=msgs_lookup_u("Loading");
    loading_len=strlen_u(loading);

    width=v6ro_text_length(loading, loading_len);

    v6ro_move_cursor(1+(25*V6_CHAR_H-V6_CHAR_H)/2,
                     1+(80*V6_CHAR_W-width)/2);

    v6ro_display_string(loading, loading_len);

    v6ro_move_cursor(1, 1);

    initialise_wimp();
}

void v6ro_move_cursor(zword_t y, zword_t x)
{
#ifdef HIRES_COORDS
    os_plot(os_MOVE_TO, x*2, -y*2);
#else
    os_plot(os_MOVE_TO, x*2, -y*4);
#endif

    wind_prop[screen_window][WPROP_Y_CURSOR]=y;
    wind_prop[screen_window][WPROP_X_CURSOR]=x;
}

void v6ro_get_cursor_position(int *row, int *col)
{
    /*Flush any sort of buffer*/
    *row=wind_prop[screen_window][WPROP_Y_CURSOR];
    *col=wind_prop[screen_window][WPROP_X_CURSOR];
}

void v6ro_set_attribute(int a)
{
    wind_prop[screen_window][WPROP_TEXT_STYLE]=(zword_t) a;
}

void v6ro_set_font(int font)
{
    wind_prop[screen_window][WPROP_FONT_NUMBER]=(zword_t) font;
}

void v6ro_select_window(int w)
{
    int x0, y0;
    int xmin, xmax, ymin, ymax;

    last_char=' ';

    display_attr=wind_prop[w][WPROP_TEXT_STYLE];
    //v6ro_set_colour(NONE, (wind_prop[w][WPROP_COLOUR_DATA]&0xFF)-2);
    //v6ro_set_colour(os_COLOUR_SET_BG, (wind_prop[w][WPROP_COLOUR_DATA]>>8)-2);
    //os_set_colour(NONE, colourmem[w][0]);
    //os_set_colour(os_COLOUR_SET_BG, colourmem[w][1]);
    colourtrans_set_gcol(fg_col = colourmem[w][0], 0, 0, 0);
    colourtrans_set_gcol(bg_col = colourmem[w][1], 0x80, 0, 0);
    fg=colourtrans_return_colour_number(fg_col);
    bg=colourtrans_return_colour_number(bg_col);

    x0=(wind_prop[w][WPROP_X]-2)*V6_PIX_X;
    y0=798-(wind_prop[w][WPROP_Y]-2)*V6_PIX_Y;

    origin.x=x0;
    origin.y=y0;

    os_reset_windows();

    xmin=(wind_prop[w][WPROP_X]-1)*V6_PIX_X;
    xmax=xmin+wind_prop[w][WPROP_X_WIDTH]*V6_PIX_X-1;
    ymax=799-(wind_prop[w][WPROP_Y]-1)*V6_PIX_Y;
    ymin=ymax-wind_prop[w][WPROP_Y_HEIGHT]*V6_PIX_Y+1;
    if (xmin<0) xmin=0;
    if (xmax>1279) xmax=1279;
    if (ymin<0) ymin=0;
    if (ymax>799) ymax=799;
    os_set_graphics_window();
    os_writec(xmin & 0xFF); os_writec(xmin >> 8);
    os_writec(ymin & 0xFF); os_writec(ymin >> 8);
    os_writec(xmax & 0xFF); os_writec(xmax >> 8);
    os_writec(ymax & 0xFF); os_writec(ymax >> 8);

    os_set_graphics_origin();
    os_writec(x0 & 0xFF); os_writec(x0 >> 8);
    os_writec(y0 & 0xFF); os_writec(y0 >> 8);
}

/*
 * f=0-8 - basic palette. -3 = colour at cursor.
 * Anything else, ignore.
 */
void v6ro_set_colours(int f, int b, int w)
{
    os_colour rf=os_COLOUR_TRANSPARENT, rb=os_COLOUR_TRANSPARENT;
    int i;

    if (w == -1)
        w = screen_window;

    if (f==-3)
    {
        f=v6ro_get_colour();
        rf=v6ro_colour_number_to_palette(f);
        f=253;
        for (i=0; i<9; i++)
            if (palette[i] == rf)
            {
                f=i;
                break;
            }
    }
    else if (f>=0 && f<=8)
        rf=palette[f];
    else
        return;

    if (b==-3)
    {
        b=v6ro_get_colour();
        rb=v6ro_colour_number_to_palette(b);
        b=253;
        for (i=0; i<9; i++)
            if (palette[i] == rb)
            {
                b=i;
                break;
            }
    }
    else if (b>=0 && b<=8)
        rb=palette[b];
    else
        return;

    wind_prop[w][WPROP_COLOUR_DATA]=(zword_t)(((b+2)<<8)+(f+2));

    colourmem[w][0] = rf;
    colourmem[w][1] = rb;

    if (w == screen_window)
    {
        fg_col=rf;
        bg_col=rb;
        fg=colourtrans_return_colour_number(rf);
        bg=colourtrans_return_colour_number(rb);
        os_set_colour(NONE, fg);
        os_set_colour(os_COLOUR_SET_BG, bg);
    }
}

void v6ro_clear_screen(void)
{
    os_reset_windows();
    os_clg();
    v6ro_mark_area(-1, 1-2, h_screen_width+1+2,
                       1-2, h_screen_height+1+2);   /* Allow for border */
    screen_bg=bg_col;
    v6ro_select_window(screen_window);
    /*v6ro_update_window();*/
}

void v6ro_clear_window(int w)
{
    v6ro_select_window(w);
    os_plot(os_MOVE_TO, V6_PIX_X, -V6_PIX_Y);
    os_plot(os_PLOT_BG_BY | os_PLOT_RECTANGLE, (wind_prop[w][WPROP_X_WIDTH]-1)*V6_PIX_X,
                                               -(wind_prop[w][WPROP_Y_HEIGHT]-1)*V6_PIX_Y-1);
    v6ro_select_window(screen_window);

    v6ro_mark_area(w, 1, 1+wind_prop[w][WPROP_X_WIDTH], 1, 1+wind_prop[w][WPROP_Y_HEIGHT]);
    /*v6ro_update_window();*/
}

void v6ro_place_caret(void)
{
    int x, y;

    if (in_input_line)
    	x=input_x-2;
    else
    	x=wind_prop[screen_window][WPROP_X]+wind_prop[screen_window][WPROP_X_CURSOR]-2;

    y=wind_prop[screen_window][WPROP_Y]+wind_prop[screen_window][WPROP_Y_CURSOR]-2;

    switchoutput(-1);
    wimp_set_caret_position(ScreenW, wimp_ICON_WINDOW,
                            x*V6_PIX_X,
                            -y*V6_PIX_Y-31,
                            32, 0);
    switchoutput(+1);
}

void v6ro_clear_line(int value)
{
    /* Flush buffers */
    if (value==1)
        value=wind_prop[screen_window][WPROP_X_WIDTH]
                    - (wind_prop[screen_window][WPROP_X_CURSOR]-1)
                    - wind_prop[screen_window][WPROP_R_MARGIN];

    os_plot(os_PLOT_RECTANGLE|os_PLOT_BG_BY, value*V6_PIX_X-1, -30);
    os_plot(os_MOVE_BY, -(value*V6_PIX_X-1), 30);
    v6ro_mark_area(screen_window,
                   wind_prop[screen_window][WPROP_X_CURSOR],
                   wind_prop[screen_window][WPROP_X_CURSOR]+value,
                   wind_prop[screen_window][WPROP_Y_CURSOR],
                   wind_prop[screen_window][WPROP_Y_CURSOR]+V6_CHAR_H);
}

void v6ro_open_gfx(void)
{
    char gfxfilename[256];
    char *leaf=strrchr(StoryName, '.')+1;

    sprintf(gfxfilename, "<Zip2000$Dir>.Resources.Graphics.%s", leaf);

    GFile=fopen(gfxfilename, "rb");

    if (!GFile)
    {
        strcpy(gfxfilename, StoryName);
        strcpy(strrchr(gfxfilename, '.')+1, "Graphics");

        GFile=fopen(gfxfilename, "rb");
    }
}

void v6ro_plot_picture(int n, gfx_dir *p, int y, int x)
{
    osspriteop_area *area;
    osspriteop_action action;
    int cached;
    os_factors fac, *f;

    NOT_USED(n);

    /*if (blorb_map)
    {
        os_plot(os_MOVE_TO, x*V6_PIX_X, -y*V6_PIX_Y);
        os_plot(os_PLOT_BY, p->image_width*V6_PIX_X-2, 0);
        os_plot(os_PLOT_BY, 0, -(p->image_height*V6_PIX_Y-4));
        os_plot(os_PLOT_BY, -(p->image_width*V6_PIX_X-2), 0);
        os_plot(os_PLOT_BY, 0, (p->image_height*V6_PIX_Y-4));
        os_plot(os_MOVE_BY, p->image_width*V6_PIX_X/2-24, -p->image_height*V6_PIX_Y/2+16);
        printf("%d", n);
        v6ro_move_cursor(wind_prop[screen_window][WPROP_Y_CURSOR],
                         wind_prop[screen_window][WPROP_X_CURSOR]);

        v6ro_mark_area(screen_window, x, x+p->image_width, y, y+p->image_height);
        //v6ro_update_window();
        return;
    }*/

    area=build_sprite(bfp ? bfp : GFile, p, &cached);
    if (!area)
    	return;

    fac.xmul = p->image_width;
    fac.ymul = p->image_height;
    fac.xdiv = p->phys_width;
    fac.ydiv = p->phys_height;
    if (!hires_screen)
        fac.ydiv *= 2;

    if (fac.xmul == fac.xdiv && fac.ymul == fac.ydiv)
        f = NULL;
    else
        f = &fac;

    action = p->alphatype == alpha_NONE ? NONE : osspriteop_USE_MASK;

    if (!bfp)
    {
        int tabsize;
        void *tab = NULL;

        /*
         * Plotting 256-colour, true-colour palette sprite into 8,16 or 32bpp.
         * In the 8 bit case, use a translation table. In 16 or 32bpp, do it
         * straight from the palette, to give optimum quality on RISC OS 3.5.
         */

        if (bitmap_depth <= 3)
        {
            tabsize =
            colourtrans_generate_table_for_sprite(area, (osspriteop_id) (area+1),
                                                  os_CURRENT_MODE,
                                                  colourtrans_CURRENT_PALETTE,
                                                  NULL, colourtrans_GIVEN_SPRITE,
                                                  NULL, NULL);
            if (tabsize)
            {
                tab = malloc(tabsize);
                if (!tab)
                {
                    if (!cached)
                        free(area);
                    return;
                }
                colourtrans_generate_table_for_sprite(area, (osspriteop_id) (area+1),
                                                      os_CURRENT_MODE,
                                                      colourtrans_CURRENT_PALETTE,
                                                      tab, colourtrans_GIVEN_SPRITE,
                                                      NULL, NULL);
            }
        }
        else
            action|=osspriteop_USE_PALETTE;

        osspriteop_put_sprite_scaled(osspriteop_PTR, area, (osspriteop_id) (area+1),
                                     x*V6_PIX_X, -(y+p->image_height-1)*V6_PIX_Y,
                                     action, f, tab);
        free(tab);
    }
    else
    {
        if (p->alphatype & alpha_COMPLEX)
        {
            int px, py;
            px = wind_prop[screen_window][WPROP_X]+x-2;
            py = wind_prop[screen_window][WPROP_Y]+y-2;
            if (!hires_screen) py>>=1;
            if (dithering && bitmap_depth == 3)
            {
                if (f)
                    do_alpha_plot_scaled_dithered((osspriteop_header *)(area+1),
                                                  v6_screen, px, py, f);
                else
                    do_alpha_plot_dithered((osspriteop_header *)(area+1),
                                           v6_screen, px, py);
            }
            else
            {
                if (f)
                    do_alpha_plot_scaled((osspriteop_header *)(area+1),
                                         v6_screen, px, py, f);
                else
                    do_alpha_plot((osspriteop_header *)(area+1),
                                  v6_screen, px, py);
            }

        }
        else
        {
            if (f)
            {
                osspriteop_put_sprite_scaled(osspriteop_PTR, area,
                                             (osspriteop_id) (area+1),
                                             x*V6_PIX_X,
                                             -(y+p->image_height-1)*V6_PIX_Y,
                                             action, f, NULL);
            }
            else
            {
                /* Have an image in the "screen" depth and palette. Can do an untranslated,
                 * possibly unscaled plot.
                 */
                osspriteop_put_sprite_user_coords(osspriteop_PTR, area,
                                                  (osspriteop_id) (area+1),
                                                  x*V6_PIX_X,
                                                  -(y+p->image_height-1)*V6_PIX_Y,
                                                  action);
            }
        }
    }

    if (!cached)
    	free(area);

   /* os_plot(os_MOVE_TO, x*V6_PIX_X, -y*V6_PIX_Y);
    os_plot(os_PLOT_BY, p->image_width*V6_PIX_X-2, 0);
    os_plot(os_PLOT_BY, 0, -(p->image_height*V6_PIX_Y-4));
    os_plot(os_PLOT_BY, -(p->image_width*V6_PIX_X-2), 0);
    os_plot(os_PLOT_BY, 0, (p->image_height*V6_PIX_Y-4));
    os_plot(os_MOVE_BY, p->image_width*V6_PIX_X/2-24, -p->image_height*V6_PIX_Y/2+16);
    printf("%d", n);
    v6ro_move_cursor(wind_prop[screen_window][WPROP_Y_CURSOR],
                     wind_prop[screen_window][WPROP_X_CURSOR]);*/

    v6ro_mark_area(screen_window, x, x+p->image_width, y, y+p->image_height);
    /* We'll update at most 10 times a second - updates can be quite slow */
    if (update_often || os_read_monotonic_time() - last_update > 10)
        v6ro_update_window();
}

void v6ro_remove_picture(int n, gfx_dir *p, int y, int x)
{
    NOT_USED(n);

    os_plot(os_MOVE_TO, x*V6_PIX_X, -y*V6_PIX_Y);
    os_plot(os_PLOT_BG_BY | os_PLOT_RECTANGLE, p->image_width*V6_PIX_X-1, -p->image_height*V6_PIX_Y+2);
    v6ro_mark_area(screen_window, x, x+p->image_width, y, y+p->image_height);
    if (update_often)
        v6ro_update_window();
}

static int v6ro_get_colour()
{
    char *p;
    int x, y;
    //os_colour *pal = (os_colour *)(v6_screen+1);

    y=wind_prop[screen_window][WPROP_Y]+wind_prop[screen_window][WPROP_Y_CURSOR]-2;
#ifdef HIRES_COORDS
    if (!hires_screen) y/=2;
#else
    if (hires_screen) y*=2;
#endif
    x=wind_prop[screen_window][WPROP_X]+wind_prop[screen_window][WPROP_X_CURSOR]-2;
    /* There is an OS_SpriteOp to do this, but it's broken in RISC OS 3.5 */

    p=(char *)v6_screen+v6_screen->image;
    p+=((v6_screen->width+1)*4)*y;
    switch (bitmap_depth)
    {
        default:
            return *(p+x);
        case 4:
            return *((unsigned short *)p + x);
        case 5:
            return *((unsigned *)p + x);
    }
}

void v6ro_add_menu(int menu_no, int entries, char *entry[], int len[])
{
    toolbox_RESOURCE_FILE_OBJECT(500) object;
    menu_object *menu;
    toolbox_o menu_o;
    menu_entry_object mainentry;
    int i;

    switchoutput(-1);

    object.class_no=class_MENU;
    object.flags=NONE;
    object.version=102;
    sprintf(object.name, "GameMenu%d", menu_no);
    object.size=toolbox_SIZEOF_RESOURCE_FILE_OBJECT(0)
                   + menu_SIZEOF_OBJECT(entries-1);
    menu=(menu_object *) &object.object;
    object.header_size=(int) menu;
    object.body_size=menu_SIZEOF_OBJECT(entries-1);

    menu->flags=NONE;
    menu->title=entry[0];
    menu->title_limit=len[0]+1;
    menu->help=NULL;
    menu->help_limit=0;
    menu->show_action=0;
    menu->hide_action=0;
    menu->entry_count=entries-1;

    for (i=0; i<entries; i++)
    {
        menu->entries[i].flags=NONE;
        menu->entries[i].cmp=i+1;
        menu->entries[i].text=entry[i+1];
        menu->entries[i].text_limit=len[i+1]+1;
        menu->entries[i].click_object_name=NULL;
        menu->entries[i].sub_menu_object_name=NULL;
        menu->entries[i].sub_menu_action=0;
        menu->entries[i].click_action=action_GameMenu;
        menu->entries[i].help=NULL;
        menu->entries[i].help_limit=0;
    }

    menu_o=toolbox_create_object(toolbox_CREATE_GIVEN_OBJECT, (toolbox_id) &object);

    mainentry.flags=menu_ENTRY_SUB_MENU;
    mainentry.cmp=menu_no;
    mainentry.text=entry[0];
    mainentry.text_limit=len[0]+1;
    mainentry.click_object_name=NULL;
    mainentry.sub_menu_object_name=0;
    mainentry.sub_menu_action=0;
    mainentry.click_action=0;
    mainentry.help=NULL;
    mainentry.help_limit=0;

    menu_add_entry(NONE, mainmenu, menu_no-1, &mainentry);
    menu_set_sub_menu_show(NONE, mainmenu, menu_no, menu_o);

    switchoutput(+1);
}

void v6ro_remove_menu(int menu_no)
{
    toolbox_o m;
    os_error *e;

    switchoutput(-1);

    e=xmenu_get_sub_menu_show(NONE, mainmenu, menu_no, &m);

    if (!e)
    {
    	menu_remove_entry(NONE, mainmenu, menu_no);
    	toolbox_delete_object(NONE, m);
    }

    switchoutput(+1);
}

void v6ro_input_place_caret(const zword_t *buffer, int left, int pos)
{
    input_x=left+wind_prop[screen_window][WPROP_X]+v6ro_text_length(buffer, pos);

    place_caret();
}

void v6ro_input_blank(const zword_t *buffer, int left)
{
    int temp_x=wind_prop[screen_window][WPROP_X_CURSOR];

    NOT_USED(buffer);

    v6ro_move_cursor(wind_prop[screen_window][WPROP_Y_CURSOR], left);

    v6ro_clear_line(temp_x-left+3/*for luck*/);

    /*v6ro_move_cursor(wind_prop[screen_window][WPROP_Y_CURSOR], temp_x);*/
}

int v6ro_input_get_left(const zword_t *buffer)
{
    return wind_prop[screen_window][WPROP_X_CURSOR]-v6ro_text_length(buffer, strlen_u(buffer));
}

int v6ro_input_will_fit(const zword_t *buffer, unsigned u_char)
{
    zword_t temp[1];
    int cwidth;

    NOT_USED(buffer);

    temp[0]=u_char;

    cwidth=v6ro_text_length(temp, 1);

    return wind_prop[screen_window][WPROP_X_CURSOR]-1+cwidth <= wind_prop[screen_window][WPROP_X_WIDTH]-wind_prop[screen_window][WPROP_R_MARGIN];
}
