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

#include "swis.h"

#include "tboxlibs/toolbox.h"
#include "tboxlibs/event.h"
#include "tboxlibs/flex.h"
#include "tboxlibs/wimp.h"
#include "tboxlibs/wimplib.h"
#include "tboxlibs/menu.h"
#include "tboxlibs/saveas.h"
#include "tboxlibs/colourdbox.h"
#include "tboxlibs/fontdbox.h"

#include "pinboard.h"
#include "indirected.h"
#include "iconize.h"
#include "tinydirs.h"
#include "events.h"
#include "pinfilter.h"
#include "sticky.h"
#include "localfiles.h"
#include "fs.h"
#include "options.h"
#include "msgtrans.h"
#include "buffer.h"
#include "base.h"
#include "outline.h"
#include "paths.h"
#include "drag.h"
#include "debug.h"
#include "confirm.h"
#include "mystring.h"
#include "toggle.h"
#include "watermark.h"
#include "doublesize.h"
#include "carousel.h"
#include "upcall.h"
#include "iconbar.h"
#include "hover.h"

int taskhandle=0,filer_taskhandle=0;
int pollword=0;
int flex_da=0; // we may need to free this on module exit if it's forced
int dragging_selection=0;

static int wimp_messages[]={Wimp_MDataSave,
                            Wimp_MDataSaveAck,
                            Wimp_MDataLoad,
                            Wimp_MDataLoadAck,
                            Wimp_MHelpRequest,
                            Wimp_MModeChange,
                            Wimp_MTaskCloseDown,
                            Wimp_MIconize,
                            Wimp_MWindowClosed,
                            Wimp_MWindowInfo,
                            Wimp_MFontChanged,
                            Wimp_MIconizeAt,
                            Wimp_MToggleBackdrop,
                            0};

static int toolbox_events[]={
                             TEvent_Tinydir_SelectAll,
                             TEvent_Tinydir_ClearSelection, 
                             TEvent_Tinydir_RemoveIcon,
                             TEvent_Tinydir_OpenParent,     
                             TEvent_Tinywin_Clicked,
                             TEvent_Tinydir_SetAlias,
                             
                             Toolbox_ObjectAutoCreated,
                             
                             Window_AboutToBeShown,
                             ActionButton_Selected,
                             WritableField_ValueChanged,
                             Window_HasBeenHidden,
                             
                             Menu_AboutToBeShown,
                             Menu_HasBeenHidden,
                             Menu_Selection,
                             Menu_SubMenu,

                             ColourDbox_AboutToBeShown,
                             ColourDbox_ColourSelected,
                             
                             FontDbox_AboutToBeShown,
                             FontDbox_ApplyFont,

                             SaveAs_AboutToBeShown,
                             SaveAs_DialogueCompleted,
                             SaveAs_SaveToFile,
                             SaveAs_FillBuffer,

                             TEvent_Tinydir_FilterClick, /* filter iconbar click */
                             TEvent_Sticky_CloseClick, /* filter sticky close */
                             TEvent_MenuClick,
                             TEvent_Sticky_Open,
                             0};

static IdBlock toolbox_id;
MessagesFD msgtrans;
int *tb_sprite_area;

static int null_events_wanted=0;
int filer_null_pending=0; // if we're waiting on filer sequence and need to stop nulls when over

void null_events_enable(void)
{
  if (null_events_wanted==0) {
    unsigned int oldmask;
    event_get_mask(&oldmask);
    event_set_mask(oldmask &~1);
  }
  null_events_wanted++;
}

void null_events_disable(void)
{
  null_events_wanted--;
  if (null_events_wanted==0) {
    unsigned int oldmask;
    event_get_mask(&oldmask);
    event_set_mask(oldmask | 1);
  }              
  if (null_events_wanted<0) null_events_wanted=0;
}

static const char *rmensure[]={
  "%RMEnsure Toolbox     0.00 %RMLoad System:Modules.Toolbox.Toolbox",
  "%RMEnsure Toolbox     0.00 %Error Pinboard requires Toolbox 1.58 or later to run",
  "%RMEnsure Menu        0.00 %RMLoad System:Modules.Toolbox.Menu",
  "%RMEnsure Menu        0.00 %Error Pinboard requires the Toolbox Menu module to run",
  "%RMEnsure Window      0.00 %RMLoad System:Modules.Toolbox.Window",
  "%RMEnsure Window      0.00 %Error Pinboard requires the Toolbox Window module to run",
  "%RMEnsure TextGadgets 0.00 %RMLoad System:Modules.Toolbox.TextGadget",
  "%RMEnsure TextGadgets 0.00 %Error Pinboard requires the Toolbox TextGadget module 0.44 or later to run",
  "%RMEnsure IconBar     0.00 %RMLoad System:Modules.Toolbox.IconBar",
  "%RMEnsure IconBar     0.00 %Error Pinboard requires the Toolbox IconBar module to run",
  "%RMEnsure ColourDbox  0.00 %RMLoad System:Modules.Toolbox.ColourDBox",
  "%RMEnsure ColourDbox  0.00 %Error Pinboard requires the Toolbox ColourDbox module to run",
  "%RMEnsure FontDbox    0.00 %RMLoad System:Modules.Toolbox.FontDbox",
  "%RMEnsure FontDbox    0.00 %Error Pinboard requires the Toolbox FontDbox module to run",
  "%RMEnsure SaveAs      0.00 %RMLoad System:Modules.Toolbox.SaveAs",
  "%RMEnsure SaveAs      0.00 %Error Pinboard requires the Toolbox SaveAs module to run",
  NULL};

static void ensure_toolbox(void)
{
  int i;
  _kernel_oserror *e;
  for (i=0;rmensure[i];i++) {
    e=_swix(OS_CLI,_IN(0),rmensure[i]);
    if (e) {
      wimp_report_error(e,1,"Pinboard",0,0,0); // in case messagetrans failed to load resources
      exit(1); // fatal
    }
  }
}

/*static _kernel_oserror *pinboard_pathinit(void)
{
  _kernel_oserror *e=NULL;
  char *filename,*path;
  int size;


  // ensure we have a choices directory
  e=_swix(OS_File,_INR(0,1)|_IN(4),8,CHOICES,0);
  if (e==NULL) {
    // set up the path for local file saves
    // canonicalise the path so it is possible to check against upcalls

    // if we've already set Pinboard$Path, skip a chunk
    path=getenv("Pinboard$Path");
    if (path) {
      // if it's set to Resources:... then we need to set our own
      // old Pinboard sets this
      if (strncasecmp(path,"Resources:",10)==0) {
        path=NULL;
      } else {
        // ensure it is canonicalised
        int len;
        e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,"Pinboard:",0,0,0,0,&len);
        if (e) {
          e=NULL;
          path=NULL; // failed so fall back to default path
        } else {
          char newpath[2-len];
          e=_swix(OS_FSControl,_INR(0,5),37,"Pinboard:",newpath,0,0,2-len);
          if (e) {
            e=NULL;
            path=NULL;
          } else {
            strcpy(newpath+strlen(newpath),".");
            e=_swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",newpath,strlen(newpath),0,0);
          }
        }
      }
    }

    if (path) {
      // see if this path is valid at all
      path=getenv("Pinboard$Path"); // refresh as may have changed above
      char truncpath[strlen(path)];
      strcpy(truncpath,path);
      truncpath[strlen(truncpath)-1]=0; // remove trailing .
      e=_swix(OS_File,_INR(0,1),5,truncpath);

      if (e) {
        // path not valid as couldn't access.  If we had a file not found, we would just get 0 returned in type.
        // so use a default path
        path=NULL;
      }
    }


    if (path) {
      filename=(char *)malloc(1+strlen(path));
      if (filename) {
        strcpy(filename,path);
        *(filename+strlen(filename)-1)=0;
        e=_swix(OS_File,_INR(0,1)|_IN(4),8,filename,0); // ensure store directory present
        free(filename);
      }
    } else {
      e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,LOCAL_DATA_DEFAULT,NULL,NULL,NULL,0,&size);
      if (e==NULL) {
        filename=(char *)malloc(1+2-size);
        if (filename) {
          e=_swix(OS_FSControl,_INR(0,5),37,LOCAL_DATA_DEFAULT,filename,NULL,NULL,2-size);
          if (e==NULL) {
            e=_swix(OS_File,_INR(0,1)|_IN(4),8,LOCAL_DATA_DEFAULT,0); // ensure store directory present
            if (e==NULL) {
              strcpy(filename+strlen(filename),".");
              e=_swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",filename,strlen(filename),0,0);
            }
          }
          free(filename);
        }
      }
    }
  }

  return e;  
}*/


static void app_init(void)
{
  _kernel_oserror *e;
  
  if ((e=toolbox_initialise(0,310,wimp_messages,toolbox_events,"Resources:$.Resources.Pinboard",&msgtrans,&toolbox_id,0,&taskhandle,(void **)&tb_sprite_area)) !=NULL) {
    wimp_report_error(e,1,"Pinboard",0,0,0); // in case messagetrans failed to load resources
    exit(1);
  }
  
/*  e=pinboard_pathinit();
  if (e) wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);*/

  flex_da=flex_init("Pinboard",(int *)&msgtrans,128*1024*1024);
  flex_set_deferred_compaction(1); // we don't want to do every free time as it's expensive

  event_initialise(&toolbox_id);
  
  // start event handling
  event_set_mask(Wimp_Poll_NullMask | Wimp_Poll_PollWord);
  
  pinfilter_start(taskhandle);

  confirm_init();
}

static int filer_get_taskhandle(void)
{
  // look for the filer
  int call=0;
  int info[4];
  
  while (call>=0) {
    _swix(TaskManager_EnumerateTasks,_INR(0,2)|_OUT(0),call,info,16,&call);
    if (strcmp((char *)info[1],"Filer")==0) return info[0];
  } 
  
  return 0;
}

static int quit_message(WimpMessage *message,void *handle)
{
  int *quit=(int *)handle;
  IGNORE(message);
  
  *quit=1;
  return 1;
}

static int overwrite_find_name(char *name)
{
  // update name...

  int index=1;
  int offset;
  int type;

  offset=strlen(name);
  if (offset>211) offset=211-5;

  do {
    sprintf(name+offset,"_%d",index);
    _swix(OS_File,_INR(0,1)|_OUT(0),5,name,&type);
    if (type==0) return 1;
    offset++;
  } while (offset<1000);

  msgtrans_report_error("OverFail");
  return 0;
}

static int message_datasave(WimpMessage *message,void *handle)
{
  _backdrop_state *state=(_backdrop_state *)handle;
  char *path=getenv("Pinboard$Path");
  char *dot;
  IGNORE(handle);
  
  WimpMessage datasaveack;
  datasaveack.data.data_save_ack=message->data.data_save;


  // is it going to a sticky?
  if (sticky_find_from_handle(message->data.data_save.destination_window)!=-1) {
    // make it a temporary file instead
    strcpy(datasaveack.data.data_save_ack.leaf_name,"<Wimp$Scrap>");
    datasaveack.data.data_save_ack.estimated_size=-1; // unsafe
  } else {
    // we convert to a local path for saving
    int type;

    if ((dot=strrchr(message->data.data_save.leaf_name,'.'))!=NULL) {
      if (strlen(dot+1)+strlen(path)>211) return 1;
      sprintf(datasaveack.data.data_save_ack.leaf_name,"%s%s",path,dot+1);
    } else {
      if (strlen(message->data.data_save.leaf_name)+strlen(path)>211) return 1; // overflow

      sprintf(datasaveack.data.data_save_ack.leaf_name,"%s%s",path,message->data.data_save.leaf_name);
    }

    // do we already have a file by this name?
    fs_get_types(datasaveack.data.data_save_ack.leaf_name,&type,NULL);
    if (type!=0) {
      // already present.  Do we care?
      switch (state->overwrite) {
        case OVERWRITE_ALWAYS:
          break;

        case OVERWRITE_RENAME:
          // find an unused name
          if (!overwrite_find_name(datasaveack.data.data_save_ack.leaf_name)) return 1;
          break;

        case OVERWRITE_CONFIRM:
          // ask user to intervene
          {
            char buttons[96];
            int response;
            strcpy(buttons,msgtrans_lookup("OverAbt"));
            strcat(buttons,",");
            strcat(buttons,msgtrans_lookup("OverRep"));
            strcat(buttons,",");
            strcat(buttons,msgtrans_lookup("OverRen"));
            response=wimp_report_error(msgtrans_errorlookup("Overmsg"),(4<<9) | (1<<8),msgtrans_lookup("_TaskName"),0,0,buttons);
            if (response==3) return 1; // abort
            if (response==5) {
              if (!overwrite_find_name(datasaveack.data.data_save_ack.leaf_name)) return 1;
            }
          }
          break;
      }
    }

  }

  datasaveack.hdr.size=((strlen(datasaveack.data.data_save_ack.leaf_name)+1+44)+3) &~3;
  datasaveack.hdr.your_ref=message->hdr.my_ref;
  datasaveack.hdr.action_code=Wimp_MDataSaveAck;
  wimp_send_message(17,&datasaveack,message->hdr.sender,0,0);
  return 1;
}

static void autosave_check_update(void)
{
  // if *Pinboard was run from an obey file, use this filename as a future save point
  // filename is in Obey$File
  // check if it starts 'Pinboard'
  char *testname=getenv("Obey$File");
  FILE *in=fopen(testname,"r");
  char buf[9]={0};

  if (in) {
    fread(buf,1,9,in);
    fclose(in);
    if (strncasecmp(buf,"Pinboard\n",9)==0) {
      // yes, update the destination
      saveas_set_file_name(0,saveas_win,testname);
    }
  }
}

/*void edump(buffer_entry *e)
{
  char line[256];

  debugf("Entry size %d type %d\n",e->hdr.size,(int)e->hdr.type);
  int i,j;
  for (i=0;i<e->hdr.size;i+=16) {
    sprintf(line,"%d : ",i);
    for (j=0;j<16;j++) {
      if (i+j<e->hdr.size) {
        char c=*(i+j+(char *)e);
        sprintf(line+strlen(line),"%c",c>32 && c!=127?c:'.');
      } else {
        sprintf(line+strlen(line)," ");
      }
    }
    sprintf(line+strlen(line)," : ");
    for (j=0;j<16;j++) {
      if (i+j<e->hdr.size) {
        sprintf(line+strlen(line),"%02x ",*(i+j+(char *)e));
      }
    }
    debugf("%s\n",line);
  }
}*/

static int event_pollword(int event_code,WimpPollBlock *event,IdBlock *id,void *handle)
{
  buffer_entry *entry;
  _kernel_oserror *e;
  _backdrop_state *state=(_backdrop_state *)handle;
  int *buffer_size_ptr;
  IGNORE(id);
  IGNORE(event);
  IGNORE(event_code);
    
  if (pollword & 2) {
    pollword &= ~2;

    sticky_check_furniture(state);
  }

  if (pollword==0) return 1;

  pollword &= ~1;

  entry=buffer_get_entries(&buffer_size_ptr);
  if (!entry) return 1;

  do {
      switch (entry->hdr.type) {
      case BUFFER_ADD_TINYDIR:
        if (entry->data.add_tinydir.path[0]==0) {
          tinydir_add(state,NULL,-1);
        } else {
          int type,filetype;
          fs_get_types(entry->data.add_tinydir.path,&type,&filetype);
          if (type!=0) {
            if (filetype==0x2000) fs_boot_application(entry->data.add_tinydir.path);
            tinydir_add(state,entry->data.add_tinydir.path[0]?entry->data.add_tinydir.path:NULL,-1);
          }
        }
        break;
        
      case BUFFER_ADD_XTINYDIR:
        upcall_disable_diskname();
        tinydir_xadd(state,entry->data.add_tinydir.path,-1);
        upcall_enable_diskname();
        break;

      case BUFFER_REMOVE_TINYDIR:
        tinydir_remove_command(state,entry->data.remove_tinydir.path[0]?entry->data.remove_tinydir.path:NULL);
        break;
        
      case BUFFER_ADD_PIN:
        backdrop_add_pin_command(state,entry->data.add_pin.path,entry->data.add_pin.x,entry->data.add_pin.y,entry->data.add_pin.locked);
        break;

      case BUFFER_ADD_XPIN:
        upcall_disable_diskname();
        backdrop_add_xpin_command(state,entry->data.add_pin.path,entry->data.add_pin.x,entry->data.add_pin.y,entry->data.add_pin.locked);
        upcall_enable_diskname();
        break;
        
      case BUFFER_SET_OPTIONS:
        state->snap_to_grid=entry->data.set_options.snap_to_grid;
        state->disable_adjust=entry->data.set_options.disable_adjust;
        state->setting_iconize=entry->data.set_options.iconise_corner;
        state->setting_iconize_stack=entry->data.set_options.iconise_stack;
        state->setting_tidy=entry->data.set_options.tidy_corner;
        state->setting_tidy_stack=entry->data.set_options.tidy_stack;
        state->autosave=entry->data.set_options.autosave;
        state->overwrite=entry->data.set_options.overwrite;
        pin_update_iconsize(state,entry->data.set_options.iconsize);
        break;

      case BUFFER_SET_OPTIONS2:
        if (entry->data.set_options2.carousel_time!=-1) carousel_set_interval(entry->data.set_options2.carousel_time);
        if (entry->data.set_options2.disable_adjust!=-1) state->disable_adjust=entry->data.set_options2.disable_adjust;
        if (entry->data.set_options2.autosave!=AUTOSAVE_UNSPEC) state->autosave=entry->data.set_options2.autosave;
        if (entry->data.set_options2.overwrite!=OVERWRITE_UNSPEC) state->overwrite=entry->data.set_options2.overwrite;
        if (entry->data.set_options2.iconsize!=ICONSIZE_UNSPEC) pin_update_iconsize(state,entry->data.set_options2.iconsize);
        if (entry->data.set_options2.dclick_hold!=-1) state->doubleclick_hold=entry->data.set_options2.dclick_hold;
        if (entry->data.set_options2.confirm_delete!=-1) state->delete_always_confirm=entry->data.set_options2.confirm_delete;
        if (entry->data.set_options2.menu_info!=-1) {
          state->menu_info=entry->data.set_options2.menu_info;
          menu_show_info(state,menu_mainmenu);
        }
        if (entry->data.set_options2.iconbar_opacity!=-1) {
          state->iconbar_opacity=entry->data.set_options2.iconbar_opacity;
          iconbar_reset(state);
          if (BD_PAINT(state->bd_flags)==BD_FULLSCREEN) iconbar_set_image(state,1);
          iconbar_redraw();
        }
        if (entry->data.set_options2.toggle_keycode!=-2) {
          state->toggle_keycode=entry->data.set_options2.toggle_keycode;
          toggle_keycode=entry->data.set_options2.toggle_keycode;
        }
        if (entry->data.set_options2.hover_time!=-1) {
          hover_set_time(entry->data.set_options2.hover_time);
        }
        break;
        
      case BUFFER_SET_BACKDROP:
        if (entry->data.set_backdrop.colour!=-1) {
          state->bd_colour=entry->data.set_backdrop.colour;
          state->bg_colour=entry->data.set_backdrop.outline_colour;
        }
        if (entry->data.set_backdrop.text_colour!=-1) state->fg_colour=entry->data.set_backdrop.text_colour;

        state->bd_flags=(state->bd_flags &~ BD_TEXT_RUBOUT);
        if (entry->data.set_backdrop.rubout!=-1) state->bd_flags |= (BD_TEXT_RUBOUT*entry->data.set_backdrop.rubout);

        switch (entry->data.set_backdrop.render_type) {
        case TEXT_TYPE_DEFAULT:
          state->bd_flags=(state->bd_flags &~BD_TEXT_STYLE_MASK) | BD_TEXT_THEME;
          break;
          
        case TEXT_TYPE_BLEND:
          state->bd_flags=(state->bd_flags &~BD_TEXT_STYLE_MASK) | BD_TEXT_BLEND;
          outline_init(state);
          outline_clear_cache(state);
          break;
          
        case TEXT_TYPE_OUTLINE:
          state->bd_flags=(state->bd_flags &~BD_TEXT_STYLE_MASK) | BD_TEXT_OUTLINE;
          state->bg_colour=entry->data.set_backdrop.outline_colour;
          outline_init(state);
          outline_clear_cache(state);
          break;
        }

        carousel_stop(state);
        iconbar_reset(state);

        if (entry->data.set_backdrop.remove) {
          backdrop_load_image(state,0,NULL,1,0);
          state->bd_flags &=~ BD_IMAGE_CACHE;
        } else {
          if (entry->data.set_backdrop.path[0]) {
            backdrop_load_image(state,entry->data.set_backdrop.style,entry->data.set_backdrop.path,1,entry->data.set_backdrop.cacheJPEG);
          } else {
            backdrop_redraw_all(state);
          }
        }

        break;

      case BUFFER_SET_STICKY:
        {
          char *font,*file;
          int i;

          if (entry->data.set_sticky.save) {
            sticky_save(state);
          }

          font=entry->data.set_sticky.data;
          file=font+1+strlen(font);

          if (*font!=0) {
            if (state->stickyfont) {
              flex_free((flex_ptr)&state->stickyfont);
              state->stickyfont=NULL;
            }
            if (!flex_alloc((flex_ptr)&state->stickyfont,1+strlen(font))) {
              msgtrans_report_error("NoMem");
            } else {
              strcpy(state->stickyfont,font);
            }
          }

          if (*file!=0) {
            if (entry->data.set_sticky.relocate) {
              // remove sticky file and update
              // delete
              if (sticky_filename) _swix(OS_File,_INR(0,1),6,sticky_filename);

              // update name
              if (!flex_extend((flex_ptr)&sticky_filename,1+strlen(file))) {
                msgtrans_report_error("NoMem");
              } else {
                strcpy(sticky_filename,file);
              }
              sticky_save(state);
            } else {
              // load new file
              if (!flex_extend((flex_ptr)&sticky_filename,1+strlen(file))) {
                msgtrans_report_error("NoMem");
              } else {
                strcpy(sticky_filename,file);

                // clear existing
                for (i=0;i<pinlist->count;i++) {
                  if (pin_is_sticky(&pinlist->info[i])) sticky_delete(state,i,1);
                }

                sticky_load(state);
              }
            }
          }

        }
        break;
        
      case BUFFER_SET_WATERMARK:
        if (entry->data.watermark.enable) {
          state->watermark_location=entry->data.watermark.location;
          state->wm_x_offset=entry->data.watermark.x_offset;
          state->wm_y_offset=entry->data.watermark.y_offset;
          state->watermark_x_scale=entry->data.watermark.x_scale;
          state->watermark_y_scale=entry->data.watermark.y_scale;
          watermark_load(state,entry->data.watermark.filename);
          backdrop_redraw_all(state);
        } else {
          if (state->watermark_data) {
            flex_free((flex_ptr)&state->watermark_data);
            state->watermark_data=NULL;
            state->watermark_length=0;
            backdrop_redraw_all(state);
          }
        }
        break;

      case BUFFER_CLEAR_PINBOARD:
        {
          int i;
          for (i=0;i<pinlist->count;i++) {
            if (!pin_is_sticky(&pinlist->info[i])) {
              if (pin_is_tiny(&pinlist->info[i])) {
                if (pin_is_fsfile(&pinlist->info[i])) {
                  toolbox_delete_object(0,pinlist->info[i].obj);
                  indirected_remove(pinlist->info[i].data.filename_offset);
                  pinlist->info[i].icontype=pin_type_empty;
                }
              } else {
                if (pin_is_fsfile(&pinlist->info[i])) {
                  pin_delete(state,i);
                }
              }
            }
          }
          pin_remove_empties(state);
//          backdrop_load_image(state,0,NULL,1,0);
          state->snap_to_grid=entry->data.clear_pinboard.snap_to_grid;
//          if (entry->data.clear_pinboard.iconise_to_iconbar) state->setting_iconize=ICONIZE_TO_ICONBAR; else state->setting_iconize=ICONIZE_TO_BUTTON;

          // if we were called from an obey file, then use this filename for future
          // save or autosave
          autosave_check_update();
        }
        break;

      case BUFFER_ICONSPRITES:
        pin_update_filetypeicons(state);
        hires_iconsprites(entry->data.iconsprites.filename);
        break;
        
      case BUFFER_TWEAK_PIN:
        {
//        edump(entry);
          int index=pin_find_filename(entry->data.tweak_pin.path,0);
          if (index!=-1) {
            // we have a matching pinboard pin.  Alter it as requested
            if (entry->data.tweak_pin.lock) pinlist->info[index].icontype|=pin_locked;
            if (entry->data.tweak_pin.unlock) pinlist->info[index].icontype&=~pin_locked;
          } else {
            index=pin_find_filename(entry->data.tweak_pin.path,1);
          }

          if (index!=-1 &&  entry->data.tweak_pin.alias) {
            // set an alias for this pin...
debugf("alias %s\n",entry->data.tweak_pin.path+1+strlen(entry->data.tweak_pin.path));
            char *alias=entry->data.tweak_pin.path+1+strlen(entry->data.tweak_pin.path);
            if (alias[0]) {
              if (pinlist->info[index].data.pin.alias==INDIRECT_FREEI) {
                // try allocating some store
                pinlist->info[index].data.pin.alias=indirected_add(alias,1+strlen(alias));
              } else {
                indirected_update(pinlist->info[index].data.pin.alias,alias,1+strlen(alias));
              }
            } else {
              // remove alias
              if (pinlist->info[index].data.pin.alias!=INDIRECT_FREEI) {
                indirected_remove(pinlist->info[index].data.pin.alias);
                pinlist->info[index].data.pin.alias=INDIRECT_FREEI;
              }
            }
            pin_info_updated(state,index);
          }
        }
        break;

      case BUFFER_UPCALL_INFO:
        {
          char fs[32];
          int p;
          e=_swix(OS_FSControl,_INR(0,3),33,entry->data.upcall_info.fsnumber,fs,32);
          if (e) {
            wimp_report_error(e,1,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
          } else {
            // do we know this one at all?
            options_test_flrsetup(fs,entry->data.upcall_info.path); // in case it's a settings file
            
            p=pin_locate_upcall(state,fs,entry->data.upcall_info.path,1);
            if (p!=-1) {
              // update the validation string and redraw
              pin_info_updated(state,p);
            }
            carousel_check_update(state,fs,entry->data.upcall_info.path);
          }
        }
        break;
        
      case BUFFER_UPCALL_DELETE:
        {
          char fs[32];
          int p,type;

          e=_swix(OS_FSControl,_INR(0,3),33,entry->data.upcall_info.fsnumber,fs,32);
          if (e) {
            wimp_report_error(e,1,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
          } else {
            p=pin_locate_upcall(state,fs,entry->data.upcall_delete.path,0);
            if (p!=-1) {
              // we have a reference
              _swix(OS_File,_INR(0,1)|_OUT(0),5,indirected(pinlist->info[p].data.filename_offset,char *),&type);
              if (type==0) {
                // really was deleted
                // remove the pin
                
                pin_delete(state,p);
                pin_remove_empties(state);
              }
            }
            carousel_check_update(state,fs,entry->data.upcall_delete.path);
          }
        }
        break;
        
      case BUFFER_UPCALL_RENAME:
        {
          int p,dest_p;
          char fs[32];
          char *newname;

          if (strcmp(entry->data.upcall_rename.path,entry->data.upcall_rename.path+1+strlen(entry->data.upcall_rename.path))==0) {
            // nothing to do here
            break;
          }

          e=_swix(OS_FSControl,_INR(0,3),33,entry->data.upcall_rename.fsnumber,fs,32);
          if (e) {
            wimp_report_error(e,1,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
          } else {
            carousel_check_update(state,fs,entry->data.upcall_rename.path);
            carousel_check_update(state,fs,entry->data.upcall_rename.path+1+strlen(entry->data.upcall_rename.path));

            outline_clear_cache(state); // ensure we redraw the text with the new name later
            p=pin_locate_upcall(state,fs,entry->data.upcall_rename.path,0);
            dest_p=pin_locate_upcall(state,fs,1+strlen(entry->data.upcall_rename.path)+entry->data.upcall_rename.path,0);

            if (p!=-1) {
              // is it a saved file?

              if (dest_p!=-1) {
                // we have a pin for the destination too.
                // may result from a create/delete/rename sequence
                // so copy file info from source and remove the 'original'
                pin_delete(state,p);
                pin_info_updated(state,dest_p);
              } else {
                // if a saved file, and the destination isn't still the pinboard, delete the icon
                if (upcall_make_filename(fs,1+strlen(entry->data.upcall_rename.path)+entry->data.upcall_rename.path,&newname)) {
                  int del=0;

                  if (pin_is_localfile(&pinlist->info[p])) {
                    if (strncmp(newname,"Pinboard:",9)!=0) del=1;
                  }
                  if (del) {
                    flex_free((flex_ptr)&newname);
                    pin_delete(state,p);
                    pin_remove_empties(state);
                  } else {
                    // update the filename entry
                    indirected_remove((int)pinlist->info[p].data.filename_offset);
                    pinlist->info[p].data.filename_offset=(int)indirected_add_fromflex((flex_ptr)&newname,1+strlen(newname));
                    flex_free((flex_ptr)&newname);
                    pin_info_updated(state,p);
                  }
                }
              }
            } else {
              // file not known.  Is it going to end up in saved though?
              if (upcall_make_filename(fs,1+strlen(entry->data.upcall_rename.path)+entry->data.upcall_rename.path,&newname)) {
                if (strncmp(newname,"Pinboard:",9)==0) {
                  // yes - make an icon
                  pin_locate_upcall(state,fs,1+strlen(entry->data.upcall_rename.path)+entry->data.upcall_rename.path,1);
                }
                flex_free((flex_ptr)&newname);
              }
            }

          }
        }
        break;
      }

    entry=buffer_get_next(entry,buffer_size_ptr);
  } while (entry);
  
  return 1;
}

static int autosave_confirm(void)
{
  _kernel_oserror e;
  e.errnum=256;
  strcpy(e.errmess,msgtrans_lookup("AutoSave"));
  return wimp_report_error(&e,3 | (1<<8) | (4<<9),msgtrans_lookup("_TaskName"),0,0,0)==1;
}


#ifdef NULLCHECK
static int nullcount=0;
#endif

int main(int argc, char *argv[])
{
  WimpPollBlock pollblock;
  int quit=0,event_code,interval;
  _backdrop_state *state;
  unsigned int current_time,next_hover,next_poll_time;

  debugf("\n");

  filer_get_version();
  iconbar_opacity_init();

  ensure_toolbox();

  app_init();

  event_register_message_handler(Wimp_MQuit,quit_message,&quit);
  
  state=backdrop_init();

  indirected_init();
  pins_init();
  sticky_init(state);
  iconize_init(state);
  tinydir_init(state);
  hover_init(state);
  
  sticky_load(state); // restore sticky state

  // get Filer's task handle (we can do this via Service Calls if we start before the Filer/Desktop)
  if (filer_taskhandle==0) filer_taskhandle=filer_get_taskhandle();  
  
  event_register_message_handler(Wimp_MDataSave,message_datasave,state);
  event_register_wimp_handler(-1,Wimp_EPollWordNonZero,event_pollword,state);
  event_register_message_handler(Wimp_MFontChanged,message_fontchanged,state);
  event_register_message_handler(Wimp_MHelpRequest,message_help,state);
  event_register_message_handler(Wimp_MToggleBackdrop,message_togglebd,state);
  localfiles_import(state);

  while (!quit) {
    interval=carousel_get_interval();
    next_hover=hover_next_poll_time();

    if (interval==-1 && next_hover==0x7fffffff && !iconbar_modified) {
      event_poll(&event_code,&pollblock,&pollword);
    } else {
      null_events_enable();
      if (dragging_selection || filer_base_column!=-1) {
        event_poll(&event_code,&pollblock,&pollword);
      } else {
        next_poll_time=next_hover;
        if (iconbar_modified) {
          _swix(OS_ReadMonotonicTime,_OUT(0),&current_time);
          if (current_time+ICONBAR_POLL_INTERVAL < next_poll_time) next_poll_time=current_time+ICONBAR_POLL_INTERVAL;
        }
        if (interval!=-1) {
          if (interval+carousel_last_change < next_poll_time) next_poll_time=interval+carousel_last_change;
        }
        event_poll_idle(&event_code,&pollblock,next_poll_time,&pollword);
      }
      null_events_disable();
      _swix(OS_ReadMonotonicTime,_OUT(0),&current_time);
      if (iconbar_modified && current_time>=interval+ICONBAR_POLL_INTERVAL) iconbar_poll(state);
      if (current_time>=interval+carousel_last_change) carousel_next(state);
      if (current_time>=next_hover) hover_poll(current_time,state);
    }

#ifdef NULLCHECK
    if (event_code==0) {
      nullcount++;
      _swix(260,0);
      _swix(286,0);
      printf("%d nulls \n",nullcount);
      _swix(261,0);
    }
#endif

    // reset filer column
    if (event_code<17) {
      if (filer_null_pending) {
        filer_base_column=-1;
        filer_base_row=-1;
        null_events_disable();
        filer_null_pending=0;
      }
    }
  }

  switch (state->autosave) {
    case AUTOSAVE_NONE:
      break;

    case AUTOSAVE_CONFIRM:
      if (autosave_confirm()) {
        char filename[256];
        if (saveas_get_file_name(0,saveas_win,filename,256,NULL)==NULL)
          pinboard_save(state,filename);
      }
      break;

    case AUTOSAVE_AUTO:
      {
        char filename[256];
        if (saveas_get_file_name(0,saveas_win,filename,256,NULL)==NULL)
          pinboard_save(state,filename);
      }
      break;
  }
  
  iconbar_reset(state);

  pin_restore_all_windows(state);
  
  event_finalise();
  
  pinfilter_stop(taskhandle);
  
  taskhandle=0;

  flex_da=0;

  return 0;
}
