#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "tboxlibs/wimp.h"
#include "tboxlibs/wimplib.h"
#include "tboxlibs/flex.h"

#include "iconbar.h"
#include "modversion.h"
#include "pinboard.h"
#include "pinsprite.h"
#include "outline.h"

#include "debug.h"

static int iconbar_opacity_supported=-1;
static int iconbar_last_scrollx=-1;
static int iconbar_stopped_scrollx=-1;
int iconbar_modified=0;

#define ALTERED_NOT 0
#define ALTERED_NO_ORIGINAL 1
#define ALTERED 2

extern int taskhandle;

typedef struct {
  int icon_number;
  int width;
  BBox bbox;
  char text[32];
} _iconbar_cache;

#define CACHE_ITEM_LENGTH ((int)sizeof(_iconbar_cache))

static char *iconbar_cache=NULL;
static int iconbar_cache_length=0;
// iterators for the cache

#define iconbar_cache_start_scan(a) (a)=0
#define iconbar_cache_next(a) (a)=(a)+CACHE_ITEM_LENGTH
#define iconbar_cache_finished(a) ((a)>=iconbar_cache_length)

void iconbar_set_image(_backdrop_state *state,int force_redo);

static void iconbar_get_indir_text(int icon,char *dest,char *buffer,int max)
{
  int msg[4];
  int icon_taskhandle;
  int offset,terminated=0;

  memset(msg,0,16);

  if (max>30) max=30;

  // find out task handle
  if (_swix(Wimp_SendMessage,_INR(0,3)|_OUT(2),19,msg,-2,icon,&icon_taskhandle)) {
    *dest=0;
    return;
  }

  // grab text
  if (_swix(Wimp_TransferBlock,_INR(0,4),icon_taskhandle,buffer,taskhandle,dest,max)) {
    *dest=0;
    return;
  }

  // now some iconbar icons have the wrong buffer size.  Eg SDFS icons (4te has max size 2)
  // so we need to check if we've got all we need to get :(
  // this is HORRIBLE

  for (offset=0;offset<max;offset++) {
    if (*(dest+offset)<32) {
      terminated=1;
      break; // we found a terminator, awesome
    }
  }

  if (!terminated) {
    do {
      if (_swix(Wimp_TransferBlock,_INR(0,4),icon_taskhandle,buffer+offset,taskhandle,dest+offset,1)) {
        *dest=0;
        return;
      }
      offset++;
    } while (*(dest+offset)>31 && offset<31);
  }

  *(dest+offset)=0;
}

/*static void iconbar_show_cache(void)
{
  int offset;
  iconbar_cache_start_scan(offset);
  while (!iconbar_cache_finished(offset)) {
    _iconbar_cache *c=(_iconbar_cache *)(iconbar_cache+offset);
    debugf("%d: :%d: %s (%d)\n",offset,c->icon_number,c->text,c->width);
    iconbar_cache_next(offset);
  }
}*/

static int iconbar_recache_text(void)
{
  // return 1 if a recache was needed
  int offset=0,i;
  int rv=0;
  WimpGetWindowInfoBlock win;
  WimpGetIconStateBlock icon;
  _iconbar_cache *c;
  char icon_text[32];

  if (!iconbar_cache) {
    if (!flex_alloc((flex_ptr)&iconbar_cache,4)) return 0; // unable to support
    iconbar_cache_length=0;
    rv=1;
  }

  // how many icons...?
  win.window_handle=-2;
  if (_swix(Wimp_GetWindowInfo,_IN(1),((int)&win) | 1)) return 0;

  for (i=0;i<win.window_data.nicons;i++) {
    icon.window_handle=-2;
    icon.icon_handle=i;
    if (wimp_get_icon_state(&icon)) return 0;

    // text icon?
    if (  (icon.icon.flags & (3 | (1<<8)))==(3 | (1<<8)) && (icon.icon.flags & ((1<<6)|(1<<23)))==0) {
      // we need to get the text somehow
      iconbar_get_indir_text(i,icon_text,icon.icon.data.ist.buffer,icon.icon.data.ist.buffer_size);

      // do we have this one here?
      c=(_iconbar_cache *)(iconbar_cache+offset);
      if (offset>=iconbar_cache_length || c->icon_number!=i) {
        // not known at all - insert here
        rv=1;
        if (!flex_midextend((flex_ptr)&iconbar_cache,offset,CACHE_ITEM_LENGTH)) return 0;
        c=(_iconbar_cache *)(iconbar_cache+offset);
        iconbar_cache_length+=CACHE_ITEM_LENGTH;
        c->icon_number=i;
        strcpy(c->text,icon_text);
        _swix(Wimp_TextOp,_INR(0,2)|_OUT(0),1,icon_text,0,&c->width);
        offset+=CACHE_ITEM_LENGTH;
      } else {
        // if we do, is it the same as last time?
        if (memcmp(&c->bbox,&icon.icon.bbox,sizeof(BBox))!=0) {
          c->bbox=icon.icon.bbox;
          rv=1;
        }
        if (strcmp(icon_text,c->text)!=0) {
          // no - update the text
          strcpy(c->text,icon_text);
          _swix(Wimp_TextOp,_INR(0,2)|_OUT(0),1,icon_text,0,&c->width);
          rv=1;
        }
        offset+=CACHE_ITEM_LENGTH;
      }

    } else {
      // remove this one from the pile?
      if (offset<iconbar_cache_length) {
        // maybe
        c=(_iconbar_cache *)(offset+iconbar_cache);
        if (c->icon_number==i) {
          // yes...
          rv=1;
          flex_midextend((flex_ptr)&iconbar_cache,offset+CACHE_ITEM_LENGTH,-(CACHE_ITEM_LENGTH));
          iconbar_cache_length-=CACHE_ITEM_LENGTH;
        }
      }
    }
  }

  // we are out of icons... but do we have more in store?
  if (offset<iconbar_cache_length) {
    rv=1;
    flex_midextend((flex_ptr)&iconbar_cache,iconbar_cache_length,-(iconbar_cache_length-offset));
    iconbar_cache_length=offset;
  }


  return rv;
}



void iconbar_opacity_init(void)
{
  iconbar_opacity_supported=module_version("WindowManager")>=58100;
}

static int *iconbar_find_tile_sprite(int **wimp_area,int bpp)
{
  // try to find a tile sprite
  char name[13];
  int *sprite_ptr;
                                       
  sprintf(name,"tile_1-%d",1<<bpp);
  if ((sprite_ptr=find_sprite(name,wimp_area))!=NULL) return sprite_ptr;
  return find_sprite("tile_1",wimp_area);
}

#define XEIG 0
#define YEIG 1
#define SCREENWIDTH 2
#define SCREENHEIGHT 3
#define SCREENBPP 4

void iconbar_redraw(void)
{
  int vars[]={4,5,11,12,-1};
  _swix(OS_ReadVduVariables,_INR(0,1),vars,vars);

  wimp_force_redraw(-1,0,0,vars[2]<<vars[0],ICONBAR_HEIGHT);
}

static void iconbar_plot(int x,int y,_backdrop_state *state,int *scale)
{
  // plot fragment of backdrop image
  int *area=(int *)state->bd_image;
  int opacity=(255*state->iconbar_opacity)/100;

  switch (BD_IMAGE_TYPE(state->bd_flags)) {
    case BD_IMAGE_SPRITE:
      _swix(OS_SpriteOp,_INR(0,7),52+512,area,((char *)area)+*(area+2),x,y,opacity<<8,scale,state->colourtrans);
      break;

    case BD_IMAGE_JPEG:
      _swix(JPEG_PlotScaled,_INR(0,6),state->bd_image,x,y,scale,state->image_length,3 | (opacity<<4),0);
      break;
  }
}

static void iconbar_draw_textblob(int width, int icon_width, int xmin)
{
  // adjust alignment
  int offset=(icon_width-width)/2;

  wimp_set_colour(1);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset,0);
  _swix(OS_Plot,_INR(0,2),0,-8,0);
  _swix(OS_Plot,_INR(0,2),201,8,16);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset,0);
  _swix(OS_Plot,_INR(0,2),96+1,width,16);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset,ICONBAR_HEIGHT);
  _swix(OS_Plot,_INR(0,2),0,-8,0);
  _swix(OS_Plot,_INR(0,2),201,8,16);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset+width,0);
  _swix(OS_Plot,_INR(0,2),0,-8,0);
  _swix(OS_Plot,_INR(0,2),201,8,16);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset+width,ICONBAR_HEIGHT);
  _swix(OS_Plot,_INR(0,2),0,-8,0);
  _swix(OS_Plot,_INR(0,2),201,8,16);

  _swix(OS_Plot,_INR(0,2),4,xmin+offset,ICONBAR_HEIGHT-16);
  _swix(OS_Plot,_INR(0,2),96+1,width,16);
}

static void iconbar_draw_outline(_backdrop_state *state,_iconbar_cache *c,int xmin,int *vars)
{
  // generate and plot
  int width=c->width;

  int offset=(c->bbox.xmax-c->bbox.xmin-width)/2;
  offset=offset&~((1<<vars[XEIG])-1);
  xmin=xmin &~((1<<vars[XEIG])-1);

  outline_generate(state,0,0xdddddd00,c->text,8);

  _swix(OS_SpriteOp,_INR(0,7),256+52,state->text_sprite,"1",xmin+offset-2,-16,(1<<3),0,0);
  _swix(OS_SpriteOp,_INR(0,7),256+52,state->text_sprite,"1",xmin+offset-2,ICONBAR_HEIGHT-16,(1<<3),0,0);

  state->cached_pin=-1; // invalid now
}

static void iconbar_icon_texts(_backdrop_state *state,char *tile_name,int *vars,int wrap,int scrollx)
{
  // get number of icons that exist...
  int r[4];
  int *sprite_area,*sprite_pointer;
  int save_area_size,*save_area;
  char clip[9];
  int screen_width=vars[SCREENWIDTH]<<vars[XEIG];
  int offset;

  sprite_pointer=find_sprite(tile_name,&sprite_area);
  if (!sprite_pointer) return;

  if (_swix(OS_SpriteOp,_INR(0,2)|_OUT(3),512+62,sprite_area,sprite_pointer,&save_area_size)) return;

  if (!flex_alloc((flex_ptr)&save_area,save_area_size)) return;

  save_area[0]=0;
  if (_swix(OS_SpriteOp,_INR(0,3)|_OUTR(0,3),60+512,sprite_area,sprite_pointer,save_area,&r[0],&r[1],&r[2],&r[3])) goto finish2;

  for (iconbar_cache_start_scan(offset);!iconbar_cache_finished(offset);iconbar_cache_next(offset)) {
    _iconbar_cache *c=(_iconbar_cache *)(iconbar_cache+offset);

    // in view?
    if (c->bbox.xmax>scrollx && c->bbox.xmin<screen_width+scrollx) {
      // render it
      clip[0]=24;
      clip[3]=0;
      clip[4]=0; // Ymin
      clip[7]=(ICONBAR_HEIGHT-1) &0xff;
      clip[8]=(ICONBAR_HEIGHT-1) >>8; // Ymax

      // does the icon straddle the wrap-around border?
      // if so, we need to plot it twice

      // no, so just decide if it needs clipping...

      if ((c->bbox.xmin % screen_width) > (c->bbox.xmax % screen_width)) {
        // this one straddles the boundary, so we have to do it twice
        // first the right-hand side
        if (((scrollx % screen_width)-1) >0) {
          clip[1]=0;
          clip[2]=0;
          clip[5]=((scrollx % screen_width)-1) &0xff;
          clip[6]=((scrollx % screen_width)-1) >>8; // Xmax
          _swix(OS_WriteN,_INR(0,1),clip,9);
          if (BD_TEXT_STYLE(state->bd_flags)==BD_TEXT_BLEND || BD_TEXT_STYLE(state->bd_flags)==BD_TEXT_OUTLINE) {
            iconbar_draw_outline(state,c,(c->bbox.xmax % screen_width)-(c->bbox.xmax-c->bbox.xmin),vars);
          } else {
            iconbar_draw_textblob(c->width,c->bbox.xmax-c->bbox.xmin,(c->bbox.xmax % screen_width)-(c->bbox.xmax-c->bbox.xmin));
          }
        }

        // then, the left-hand side (this way round so we can use the shared call to draw_textblob at the end)
        clip[1]=(scrollx % screen_width) &0xff;
        clip[2]=(scrollx % screen_width) >>8; // Xmin

        clip[5]=(screen_width) &0xff;
        clip[6]=(screen_width) >>8; // Xmax
        _swix(OS_WriteN,_INR(0,1),clip,9);

      } else if (c->bbox.xmin<scrollx) {
        // icon falls off the left of the screen
        // clip the redraw
        clip[1]=(scrollx % screen_width) &0xff;
        clip[2]=(scrollx % screen_width) >>8; // Xmin

        clip[5]=(screen_width-2) &0xff;
        clip[6]=(screen_width-2) >>8; // Xmax
        _swix(OS_WriteN,_INR(0,1),clip,9);

      } else if (c->bbox.xmax>scrollx+screen_width) {
        // icon falls off the right of the screen
        // clip the redraw
        clip[1]=0;
        clip[2]=0;
        clip[5]=((scrollx % screen_width)-1) &0xff;
        clip[6]=((scrollx % screen_width)-1) >>8; // Xmax
        _swix(OS_WriteN,_INR(0,1),clip,9);
      } else {
        // icon is in the middle
        clip[1]=0;
        clip[2]=0;
        clip[5]=screen_width &0xff;
        clip[6]=screen_width >>8; // Xmax
        _swix(OS_WriteN,_INR(0,1),clip,9);
      }

      if (BD_TEXT_STYLE(state->bd_flags)==BD_TEXT_BLEND || BD_TEXT_STYLE(state->bd_flags)==BD_TEXT_OUTLINE) {
        iconbar_draw_outline(state,c,c->bbox.xmin % screen_width,vars);
      } else {
        iconbar_draw_textblob(c->width,c->bbox.xmax-c->bbox.xmin,c->bbox.xmin % screen_width);
      }
    }
  }

  _swix(OS_SpriteOp,_INR(0,3),r[0],r[1],r[2],r[3]);
finish2:
  flex_free((flex_ptr)&save_area);

}

static int iconbar_do_render(_backdrop_state *state,int *sprite_area,int *sprite_pointer,int *vars,int wrap,int xscroll)
{
  int save_area_size;
  int *save_area=NULL;
  int *coltrans=NULL;
  int coltrans_size;
  int r[4];
  int switched=0;
  int *wimptile_ptr;
  int *wimp_area;
  int scale[4];

  // get save area size
  if (_swix(OS_SpriteOp,_INR(0,2)|_OUT(3),512+62,sprite_area,sprite_pointer,&save_area_size)) return 0;

  if (!flex_alloc((flex_ptr)&save_area,save_area_size)) return 0;

  // switch output
  save_area[0]=0;
  if (_swix(OS_SpriteOp,_INR(0,3)|_OUTR(0,3),60+512,sprite_area,sprite_pointer,save_area,&r[0],&r[1],&r[2],&r[3])) goto abort;
  switched=1;

  // find the existing tile background sprite if possible...
  // but only if we'll see it

  if (state->iconbar_opacity!=0) {
    wimptile_ptr=iconbar_find_tile_sprite(&wimp_area,vars[SCREENBPP]);

    if (wimptile_ptr) {
      int scalefactors[4];
      // tile the existing background
      if (_swix(ColourTrans_SelectTable,_INR(0,5)|_OUT(4),wimp_area,wimptile_ptr,-1,-1,0,1,&coltrans_size)) goto abort;
      if (coltrans_size) {
        if (!flex_alloc((flex_ptr)&coltrans,coltrans_size)) goto abort;
        if (_swix(ColourTrans_SelectTable,_INR(0,5),wimp_area,wimptile_ptr,-1,-1,coltrans,1)) goto abort;
      }

      // get tile size
      int tile_width,tile_height,tile_mode,x,tile_xeig,tile_yeig;
      if (_swix(OS_SpriteOp,_INR(0,2)|_OUTR(3,4)|_OUT(6),40+512,wimp_area,wimptile_ptr,&tile_width,&tile_height,&tile_mode)) goto abort;
      if (_swix(OS_ReadModeVariable,_INR(0,1)|_OUT(2),tile_mode,4,&tile_xeig)) goto abort;
      if (_swix(OS_ReadModeVariable,_INR(0,1)|_OUT(2),tile_mode,5,&tile_yeig)) goto abort;

      scalefactors[0]=1<<tile_xeig;
      scalefactors[1]=1<<vars[XEIG];
      scalefactors[2]=1<<tile_yeig;
      scalefactors[3]=1<<vars[YEIG];

      _swix(OS_SpriteOp,_INR(0,7),65+512,wimp_area,wimptile_ptr,0,0,0,scalefactors,coltrans);
      if (coltrans) {
        flex_free((flex_ptr)&coltrans);
        coltrans=NULL;
      }
    } else {
      // plain colour background
      _swix(Wimp_SetColour,_IN(0),1);
      _swix(OS_Plot,_INR(0,2),4,0,0);
      _swix(OS_Plot,_INR(0,2),96+5,(1+vars[SCREENWIDTH])<<vars[XEIG],ICONBAR_HEIGHT);
    }
  }

  // overlay our backdrop image if we need to
  if (state->iconbar_opacity!=100) {
    scale[0]=(1+vars[SCREENWIDTH])<<vars[XEIG];
    scale[1]=(1+vars[SCREENHEIGHT])<<vars[YEIG];
    scale[2]=state->image_width*state->pixel_x;
    scale[3]=state->image_height*state->pixel_y;

    char clip[9];
    // set clip rectangle
    clip[0]=24;
    clip[1]=(xscroll % scale[0]) &0xff;
    clip[2]=(xscroll % scale[0]) >>8; // Xmin
    clip[3]=(ICONBAR_HEIGHT-wrap) &0xff;
    clip[4]=(ICONBAR_HEIGHT-wrap) >>8; // Ymin
    clip[5]=(scale[0]-(1<<vars[XEIG])) &0xff;
    clip[6]=(scale[0]-(1<<vars[XEIG])) >>8; // Xmax
    clip[7]=(ICONBAR_HEIGHT-1) &0xff;
    clip[8]=(ICONBAR_HEIGHT-1) >>8; // Ymax
    _swix(OS_WriteN,_INR(0,1),clip,9);

    // plot bottom bit of backdrop to top of sprite
    iconbar_plot((xscroll % scale[0])-(1<<vars[XEIG]),ICONBAR_HEIGHT-wrap,state,scale);
    if (xscroll) {
      clip[5]=clip[1];
      clip[6]=clip[2];
      clip[1]=0;
      clip[2]=0;
      _swix(OS_WriteN,_INR(0,1),clip,9);

      iconbar_plot(((xscroll % scale[0])-(1<<vars[XEIG]))-scale[0],ICONBAR_HEIGHT-wrap,state,scale);
      clip[1]=(xscroll % scale[0]) &0xff;
      clip[2]=(xscroll % scale[0]) >>8; // Xmin
    }

    // update clip rectangle
    clip[3]=0;
    clip[4]=0; // new Ymin
    clip[5]=(scale[0]-1) &0xff;
    clip[6]=(scale[0]-1)>>8; // Xmax
    clip[7]=(ICONBAR_HEIGHT-wrap-(1<<vars[YEIG])) &0xff;
    clip[8]=(ICONBAR_HEIGHT-wrap-(1<<vars[YEIG])) >>8;  // new Ymax
    _swix(OS_WriteN,_INR(0,1),clip,9);

    // plot next bit of backdrop to bottom bit of sprite
    iconbar_plot((xscroll % scale[0])-(1<<vars[XEIG]),-wrap,state,scale);
    if (xscroll) {
      clip[5]=clip[1];
      clip[6]=clip[2];
      clip[1]=0;
      clip[2]=0;
      _swix(OS_WriteN,_INR(0,1),clip,9);

      iconbar_plot(((xscroll % scale[0])-(1<<vars[XEIG]))-scale[0],-wrap,state,scale);
    }
  }

  // reset to normal screen output
  _swix(OS_SpriteOp,_INR(0,3),r[0],r[1],r[2],r[3]);

  flex_free((flex_ptr)&save_area);

  return 1;

abort:
  if (switched) _swix(OS_SpriteOp,_INR(0,3),r[0],r[1],r[2],r[3]);
  if (coltrans) flex_free((flex_ptr)&coltrans);
  if (save_area) flex_free((flex_ptr)&save_area);

  return 0;
}

int iconbar_check_tile_sprite(char *tile_name,int wanted_width,int wanted_height)
{
  int *sprite_ptr;
  int *wimp_area;
  int w,h,m;
  int vars[]={4,5,9,-1};
  int var_mode,var_sprite,i;

  if ((sprite_ptr=find_sprite(tile_name,&wimp_area))==NULL) return 0;

  if (_swix(OS_SpriteOp,_INR(0,2)|_OUTR(3,4)|_OUT(6),40+256,wimp_area,tile_name,&w,&h,&m)) return 0;

  // the screen mode provided may by using a selector block...
  // so we compare xeig/yeig/bpp
  for (i=0;vars[i]!=-1;i++) {
    if (_swix(OS_ReadModeVariable,_INR(0,1)|_OUT(2),-1,vars[i],&var_mode)) return 0;
    if (_swix(OS_ReadModeVariable,_INR(0,1)|_OUT(2),m,vars[i],&var_sprite)) return 0;
    if (var_mode!=var_sprite) return 0;
  }

  debugf("Want size %dx%d got %dx%d\n",wanted_width,wanted_height,w,h);

  if (wanted_width!=w || wanted_height!=h) return 0;

  return 1;
}

void iconbar_reset(_backdrop_state *state)
{
  // undo any changes to the iconbar
  // we can't simply delete a sprite from the Wimp area
  // as this results in unpleasantness.

  // iterate each known bit depth and retile with 100% opacity...
  int depth;
  int old_opacity=state->iconbar_opacity;
  char tile_name[13];
  int *sprite_ptr,*wimp_area;

  int vars[]={4,5,11,12,9,-1};
  if (_swix(OS_ReadVduVariables,_INR(0,1),vars,vars)) return;

  state->iconbar_opacity=100;

  for (depth=1;depth<=32;depth*=2) {
    // if it exists, redo it
    sprintf(tile_name,"tile_i-%d",1<<vars[SCREENBPP]);

    if ((sprite_ptr=find_sprite(tile_name,&wimp_area))!=NULL) {
      iconbar_do_render(state,wimp_area,sprite_ptr,vars,0,0);
    }
    
  }

  state->iconbar_opacity=old_opacity;
}

void iconbar_set_image(_backdrop_state *state,int force_redo)
{
  WimpGetWindowInfoBlock info;
  int wrap;
  int *sprite_area=NULL;
  int sprite_area_size;
  int *sprite_pointer;
  char tile_name[13];
  int screen_mode;

  if (!iconbar_opacity_supported) return;

  if (state->iconbar_opacity==100) {
    // ensure we aren't messing and bail out
    return;
  }

  int vars[]={4,5,11,12,9,-1};
  if (_swix(OS_ReadVduVariables,_INR(0,1),vars,vars)) return;

  info.window_handle=-2;
  if (_swix(Wimp_GetWindowInfo,_IN(1),1|((int)&info))) return;
  wrap=-(info.window_data.extent.ymin);
  iconbar_stopped_scrollx=info.window_data.xscroll;
  iconbar_last_scrollx=info.window_data.xscroll;

  sprintf(tile_name,"tile_i-%d",1<<vars[SCREENBPP]);

  iconbar_recache_text();
//  iconbar_show_cache();

  // can we use the existing tile sprite or do we need a new one?
  // if screen mode is same, all good
  // if X/Y size is same, all good
  if (force_redo || !iconbar_check_tile_sprite(tile_name,1+vars[SCREENWIDTH],ICONBAR_HEIGHT>>vars[YEIG])) {
    // this is a new setup.  We need a new sprite file completely.
    if (_swix(OS_ScreenMode,_IN(0)|_OUT(1),1,&screen_mode)) return;

debugf("Create sprite with mode %x\n",screen_mode);

    sprite_area_size=128+4*(1+vars[SCREENWIDTH])*(ICONBAR_HEIGHT>>vars[YEIG]);

    if (!flex_alloc((flex_ptr)&sprite_area,sprite_area_size)) return;

    // make our sprite    
    sprite_area[0]=sprite_area_size;
    sprite_area[1]=0;
    sprite_area[2]=16;
    sprite_area[3]=16;
    if (_swix(OS_SpriteOp,_INR(0,6),15+256,sprite_area,tile_name,0,1+vars[SCREENWIDTH],ICONBAR_HEIGHT>>vars[YEIG],screen_mode)) {
      flex_free((flex_ptr)&sprite_area);
      return;
    }

    if (_swix(OS_SpriteOp,_INR(0,2)|_OUT(2),24+256,sprite_area,tile_name,&sprite_pointer)) {
      flex_free((flex_ptr)&sprite_area);
      return;
    }

    if (iconbar_do_render(state,sprite_area,sprite_pointer,vars,wrap,iconbar_stopped_scrollx)) {
      // save the sprite file
      if (_swix(OS_SpriteOp,_INR(0,2),12+256,sprite_area,"pipe:$.tile")==NULL) {
        // and transfer to the wimp area
        if (_swix(OS_CLI,_IN(0),"%IconSprites pipe:$.tile")==NULL) {
          // success!
          iconbar_redraw();
          iconbar_modified=1;
        }
      }
    }

    flex_free((flex_ptr)&sprite_area);
  } else {
    // we've done this before
    // locate the sprite so we can write directly into it
    sprite_pointer=find_sprite(tile_name,&sprite_area);
    if (sprite_pointer) {
      if (iconbar_do_render(state,sprite_area,sprite_pointer,vars,wrap,iconbar_stopped_scrollx)) {
        iconbar_redraw();
        iconbar_modified=1;
      }
    }
  }

  iconbar_icon_texts(state,tile_name,vars,wrap,iconbar_stopped_scrollx);
}

void iconbar_poll(_backdrop_state *state)
{
  // if the icon bar has scrolled then we'll need to redo the image
  // to reduce system load we'll only update once the scrolling has been completed.  Otherwise it'll be very
  // messy with the repeated image refresh and complete iconbar redraws

  WimpGetWindowStateBlock info;
  if (iconbar_recache_text()) {
    iconbar_set_image(state,0);
    return;
  }

  info.window_handle=-2;
  wimp_get_window_state(&info);

  if (iconbar_last_scrollx!=info.xscroll) {
    // scroll has changed.  Make a note.
    iconbar_last_scrollx=info.xscroll;
    return;
  }

  // scroll has not changed, but is it the same as the last redraw?
  if (iconbar_stopped_scrollx!=iconbar_last_scrollx) {
    iconbar_set_image(state,0);
  }
}
