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

#include "kernel.h"
#include "swis.h"

#include "toolbox.h"
#include "wimp.h"
#include "wimplib.h"
#include "flex.h"
#include "window.h"
#include "gadgets.h"

#include "msgtrans.h"
#include "main.h"
#include "mystring.h"
#include "defines.h"
#include "ui.h"
#include "spritegen.h"
#include "settings.h"
#include "colour.h"
#include "mystringset.h"
#include "watermark.h"
#include "path.h"

settings current_settings;
settings edited_settings;
BOOL settings_tried=FALSE;


// translation from stringset to real values

const s_match iconsize_strings[]={{"IS_Normal",ICONSIZE_NORMAL},
        {"IS_Large",ICONSIZE_LARGE},
        {"IS_Small",ICONSIZE_SMALL},
        {0,0}};

const s_match iconize_strings[]={{"IT_B",ICONIZE_BUTTON},
{"IT_IB",ICONIZE_ICONBAR},
{"IT_TL",ICONIZE_TOPLEFT},
{"IT_TR",ICONIZE_TOPRIGHT},
{"IT_BL",ICONIZE_BOTTOMLEFT},
{"IT_BR",ICONIZE_BOTTOMRIGHT},
{0,0}};

const s_match stack_strings[]={{"StackH",HORIZONTAL},
                               {"StackV",VERTICAL},
                               {0,0}};

const s_match tidy_strings[]={{"TT_TL",TIDY_TOPLEFT},
{"TT_TR",TIDY_TOPRIGHT},
{"TT_BL",TIDY_BOTTOMLEFT},
{"TT_BR",TIDY_BOTTOMRIGHT},
{0,0}};

const s_match autosave_strings[]={{"AS_Off",AUTOSAVE_OFF},
                                  {"AS_Confirm",AUTOSAVE_CONFIRM},
                                  {"AS_Always",AUTOSAVE_ALWAYS},
                                  {0,0}};

const s_match overwrite_strings[]={{"WR_Always",OVERWRITE_ALWAYS},
                                   {"WR_Rename",OVERWRITE_RENAME},
                                   {"WR_Confirm",OVERWRITE_CONFIRM},
                                   {0,0}};


static void settings_set_filename(char **ptr,char *name)
{
  if (*ptr) {
    flex_free((flex_ptr)ptr);
    *ptr=NULL;
  }

  if (!flex_alloc((flex_ptr)ptr,1+strlen(name))) {
    msgtrans_error("NoMem");
    *ptr=NULL;
  } else {
    strcpy(*ptr,name);
  }
}

void settings_set_default(settings *s)
{
  // default list of settings
  s->background_type=BACKDROP_PLAIN;
  s->lighter=TRUE;
  s->random=FALSE;
  s->standard_image_ref=1;
  s->standard_image_max=pinboard_count_standard_textures();

  if (s->custom_image_filename) {
    flex_free((flex_ptr)&s->custom_image_filename);
    s->custom_image_filename=NULL;
  }

  s->filetype=0;

  s->is_carousel=FALSE;
  s->custom_image_type=CUSTOM_FULLSCREEN;
  s->cache=TRUE;
  s->background_colour=0x44444400;

  s->icon_text_colour=0xffffff00;
  s->icon_appearance=APPEARANCE_THEME;
  s->outline_colour=0x44444400;
  s->iconsize=ICONSIZE_NORMAL;

  s->iconize_dest=ICONIZE_TOPLEFT;
  s->iconize_stack=HORIZONTAL;
  s->tidy_dest=TIDY_TOPLEFT;
  s->tidy_stack=HORIZONTAL;
  s->snap_to_grid=FALSE;

  if (s->sticky_font) {
    flex_free((flex_ptr)&s->sticky_font);
    s->sticky_font=NULL;
  }

  s->sticky_height=192;
  s->sticky_aspect=100;
  settings_set_filename(&s->sticky_filename,"<Choices$Write>.Pinboard.Stickies");

  settings_set_filename(&s->savedfiles_path,"<Choices$Write>.Pinboard.SavedFiles");
  s->autosave=AUTOSAVE_OFF;
  s->overwrite=OVERWRITE_RENAME;

  s->watermark_enabled=FALSE;
  if (s->watermark_filename) {
    flex_free((flex_ptr)&s->watermark_filename);
    s->watermark_filename=NULL;
  }
  s->x_offset=8;
  s->y_offset=8;
  
  s->showall_key=0;
  s->showall_modifiers=0;

  s->disable_adjust=0;
}


static unsigned int get_int(char *offset)
{
  if (offset==0 || offset==(char *)0x7fffffff) return 0;

  if (*offset!=0) return 0;
  
  return (unsigned int )(*(offset+1)) | (*(offset+2)<<8) | (*(offset+3)<<16) | (*(offset+4)<<24);
}

static const char *pinboardoptions="Grid=G/S,IconiseToIconBar=ITIB/S,IconiseToTopLeft=ITTL/S,IconiseToBottomLeft=ITBL/S,IconiseToTopRight=ITTR/S,IconiseToBottomRight=ITBR/S,IconiseStackVertical=ISV/S,TidyToTopLeft=TTTL/S,TidyToBottomLeft=TTBL/S,TidyToTopRight=TTTR/S,TidyToBottomRight=TTBR/S,TidyStackVertical=TSV/S,AutoSave=AS/S,ConfirmSave=CS/S,SmallIcons=SI/S,LargeIcons=LI/S,OverwriteConfirm=OC/S,OverwriteRename=OR/S,DisableAdjust=DA/S";

void settings_pinboard_options(settings *s,char **data,int offset)
{
  // decode command
  _kernel_oserror *e;
  int optbuffer[64];
  e=_swix(OS_ReadArgs,_INR(0,3),pinboardoptions,*data+offset,optbuffer,256);

  if (e) {
    error(e,FALSE);
    return;
  }

  s->snap_to_grid=optbuffer[0]!=0;
  if (optbuffer[1]) s->iconize_dest=ICONIZE_ICONBAR;
  if (optbuffer[2]) s->iconize_dest=ICONIZE_TOPLEFT;
  if (optbuffer[3]) s->iconize_dest=ICONIZE_BOTTOMLEFT;
  if (optbuffer[4]) s->iconize_dest=ICONIZE_TOPRIGHT;
  if (optbuffer[5]) s->iconize_dest=ICONIZE_BOTTOMRIGHT;
  if (optbuffer[6]) s->iconize_stack=VERTICAL; else s->iconize_stack=HORIZONTAL;
  if (optbuffer[7]) s->tidy_dest=TIDY_TOPLEFT;
  if (optbuffer[8]) s->tidy_dest=TIDY_BOTTOMLEFT;
  if (optbuffer[9]) s->tidy_dest=TIDY_TOPRIGHT;
  if (optbuffer[10]) s->tidy_dest=TIDY_BOTTOMRIGHT;
  if (optbuffer[11]) s->tidy_stack=VERTICAL; else s->tidy_stack=HORIZONTAL;
  if (optbuffer[12]) s->autosave=AUTOSAVE_ALWAYS;
  if (optbuffer[13]) s->autosave=AUTOSAVE_CONFIRM;
  if (optbuffer[14]) s->iconsize=ICONSIZE_SMALL;
  if (optbuffer[15]) s->iconsize=ICONSIZE_LARGE;
  if (optbuffer[16]) s->overwrite=OVERWRITE_CONFIRM;
  if (optbuffer[17]) s->overwrite=OVERWRITE_RENAME;
  s->disable_adjust=optbuffer[18]!=0;
}

static const char *backdropoptions="path,Centre=C/S,Tile=T/S,Scale=S/S,N=NoRecache/S,Remove=R/S,Colour=Col/E,TextColour=TC/E,OutlineTextColour=OTC/E,BlendedText=BT/S,CacheJPEG=CJ/S,FullScreen=F/S"; // backdrop

void settings_backdrop_options(settings *s,char **data,int offset)
{
  // decode command
  _kernel_oserror *e;
  int optbuffer[64];
  char *filename;

  e=_swix(OS_ReadArgs,_INR(0,3),backdropoptions,*data+offset,optbuffer,256);

  if (e) {
    error(e,FALSE);
    return;
  }

  filename=(char *)optbuffer[0];
  if (filename) {
    int type;
    if (pinboard_query_standard_texture(&s->standard_image_ref,s->standard_image_max,&s->lighter,filename)) {
      s->background_type=BACKDROP_STANDARD;
    } else {
      s->background_type=BACKDROP_CUSTOM;

      // import filename
      if (!flex_alloc((flex_ptr)&s->custom_image_filename,1+strlen(filename))) {
        msgtrans_error("NoMem");
        exit(1);
      }
      strcpy(s->custom_image_filename,filename);
    }
    _swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(6),23,filename,&type,&s->filetype);
    if (type!=1) s->filetype=0;
  } else {
    s->background_type=BACKDROP_PLAIN;
  }

  s->custom_image_type=CUSTOM_CENTRED;
  if (optbuffer[2]) s->custom_image_type=CUSTOM_TILED;
  if (optbuffer[3]) s->custom_image_type=CUSTOM_SCALED;
  if (optbuffer[11]) s->custom_image_type=CUSTOM_FULLSCREEN;
  if (optbuffer[6]) s->background_colour=get_int((char *)optbuffer[6]);
  if (optbuffer[7]) s->icon_text_colour=get_int((char *)optbuffer[7]);
  if (optbuffer[9]) s->icon_appearance=APPEARANCE_BLENDED;
  if (optbuffer[8]) {
    s->outline_colour=get_int((char *)optbuffer[8]);
    s->icon_appearance=APPEARANCE_OUTLINE;
  }
  if (optbuffer[10]) s->cache=TRUE; else s->cache=FALSE;
}

static const char *watermarkoptions="On/S,Off/S,TopLeft=TL/S,TopCentre=TC/S,TopRight=TR/S,Left=L/S,Centre=C/S,Right=R/S,BottomLeft=BL/S,BottomCentre=BC/S,BottomRight=BR/S,path,Xoffset=X/E,Yoffset=Y/E";

void settings_watermark_options(settings *s,char **data,int offset)
{
  // decode command
  _kernel_oserror *e;
  int optbuffer[64];
  char *filename;

  _swix(260,0); printf("%s\n",*data+offset);
  
  e=_swix(OS_ReadArgs,_INR(0,3),watermarkoptions,*data+offset,optbuffer,256);

  if (e) {
    error(e,FALSE);
    return;
  }

  if (optbuffer[0]) s->watermark_enabled=TRUE;
  if (optbuffer[1]) s->watermark_enabled=FALSE;
  if (optbuffer[2]) s->watermark_location=WATERMARK_TOPLEFT;
  if (optbuffer[3]) s->watermark_location=WATERMARK_TOPCENTRE;
  if (optbuffer[4]) s->watermark_location=WATERMARK_TOPRIGHT;
  if (optbuffer[5]) s->watermark_location=WATERMARK_LEFT;
  if (optbuffer[6]) s->watermark_location=WATERMARK_CENTRE;
  if (optbuffer[7]) s->watermark_location=WATERMARK_RIGHT;
  if (optbuffer[8]) s->watermark_location=WATERMARK_BOTTOMLEFT;
  if (optbuffer[9]) s->watermark_location=WATERMARK_BOTTOMCENTRE;
  if (optbuffer[10]) s->watermark_location=WATERMARK_BOTTOMRIGHT;

  if ((filename=(char *)optbuffer[11])!=NULL) {
    printf("filename '%s'\n",filename);
    if (!flex_alloc((flex_ptr)&s->watermark_filename,1+strlen(filename))) {
      msgtrans_error("NoMem");
      exit(1);
    }
    strcpy(s->watermark_filename,filename);
    printf("-> '%s'\n",s->watermark_filename);
  }

  if (optbuffer[12]) s->x_offset=get_int((char *)optbuffer[12]);
  if (optbuffer[13]) s->y_offset=get_int((char *)optbuffer[13]);
}

static const char *stickyoptions="Font,File";

void settings_sticky_options(settings *s,char **data,int offset)
{
  // decode command
  _kernel_oserror *e;
  int optbuffer[64];

  e=_swix(OS_ReadArgs,_INR(0,3),stickyoptions,*data+offset,optbuffer,256);

  if (e) {
    error(e,FALSE);
    return;
  }

  if (optbuffer[0]) {
    char *fontname=(char *)optbuffer[0];
    char *xbase,*ybase;
    int x,y;

    // separate out font name and size
    if ((xbase=strchr(fontname,','))!=NULL) {
      // we have at least 1 size
      x=atoi(xbase+1);
      if ((ybase=strchr(xbase+1,','))!=NULL) {
        // we have two sizes
        y=atoi(ybase+1);
      } else {
        y=x;
      }
    } else {
      // we have no size; obtain desktop default
      int fonth;
      _swix(Wimp_ReadSysInfo,_IN(0)|_OUT(0),8,&fonth);
      if (fonth!=0) {
        int namelen;
        _swix(Font_ReadDefn,_INR(0,1)|_IN(3)|_OUT(2),fonth,0,0x4c4c5546,&namelen);
        char fn[1+namelen];
        _swix(Font_ReadDefn,_INR(0,1)|_OUTR(2,3),fonth,fn,&x,&y);
      } else {
        // no font, so set 12 point.
        x=y=12*16;
      }

    }
    if (xbase) *xbase=0;

    if (!flex_alloc((flex_ptr)&s->sticky_font,1+strlen(fontname))) {
      msgtrans_error("NoMem");
      exit(1);
    }
    strcpy(s->sticky_font,fontname);
    s->sticky_height=y;
    s->sticky_aspect=(100*x)/y;
  }
  if (optbuffer[1]) {
    char *filename=(char *)optbuffer[1];
    if (!flex_alloc((flex_ptr)&s->sticky_filename,1+strlen(filename))) {
      msgtrans_error("NoMem");
      exit(1);
    }
    strcpy(s->sticky_filename,filename);
  }
}

void settings_load(settings *s)
{
  int type,length;
  char *data;
  int offset=0;
  char *savedloc;

  // attempt to load the settings files

  settings_set_default(s);

  // read files...
  error(_swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(4),5,PINSETUP_FILE,&type,&length),TRUE);

  if (type!=1) return; // file not found, not a problem - use defaults
  if (!flex_alloc((flex_ptr)&data,length+1)) {
    msgtrans_error("NoMem");
    exit(1);
  }

  error(_swix(OS_File,_INR(0,3),255,PINSETUP_FILE,data,0),TRUE);
  *(data+length)=0; // ensure termination

  // process the data
  for (offset=0;offset<length;offset++) if (*(data+offset)<32) *(data+offset)=0;  
  
  for (offset=0;offset<length;offset+=1+strlen(data+offset)) {
    if (strstr(data+offset,"Textures.RTexture")!=NULL) s->random=TRUE;

    if (strncasecmp(data+offset,"X ",2)==0) {
      offset+=2;
    }
    if (strncasecmp(data+offset,"PinboardOptions ",16)==0) {
      settings_pinboard_options(s,&data,offset+16);
    }
    if (strncasecmp(data+offset,"Backdrop ",9)==0) {
      settings_backdrop_options(s,&data,offset+9);
    }
    if (strncasecmp(data+offset,"PinboardWatermark ",18)==0) {
      settings_watermark_options(s,&data,offset+18);
    }
    if (strncasecmp(data+offset,"PinboardStickies ",17)==0) {
      settings_sticky_options(s,&data,offset+17);
    }
  }

  savedloc=getenv("Pinboard$Path");
  if (path_readonly(savedloc)) {
    savedloc="<Choices$Write>.Pinboard.SavedFiles."; // as it's expecting a path below
  }
  if (!flex_alloc((flex_ptr)&s->savedfiles_path,1+strlen(savedloc))) {
    msgtrans_error("NoMem");
    exit(1);
  }
  strcpy(s->savedfiles_path,savedloc);
  *(s->savedfiles_path+strlen(s->savedfiles_path)-1)=0;
}

void settings_update_windows(pager *p, settings *s)
{
  // import the data from a settings array into the window
  ObjectId h;

  // background
  h=pager_get_page(p,Page_Background);
  switch (s->background_type) {
    case BACKDROP_STANDARD:
      radiobutton_set_state(0,h,BG_Standard,1);
      break;

    case BACKDROP_CUSTOM:
      radiobutton_set_state(0,h,BG_Custom,1);
      break;

    case BACKDROP_PLAIN:
      radiobutton_set_state(0,h,BG_Plain,1);
      break;
  }

  optionbutton_set_state(0,h,BG_Lighter,s->lighter==TRUE);
  optionbutton_set_state(0,h,BG_Random,s->random==TRUE);
  
  switch (s->custom_image_type) {
    case CUSTOM_SCALED:
      radiobutton_set_state(0,h,BG_Scaled,1);
      break;

    case CUSTOM_CENTRED:
      radiobutton_set_state(0,h,BG_Centred,1);
      break;

    case CUSTOM_TILED:
      radiobutton_set_state(0,h,BG_Tiled,1);
      break;

    case CUSTOM_FULLSCREEN:
      radiobutton_set_state(0,h,BG_Fullscreen,1);
      break;

  }

  optionbutton_set_state(0,h,BG_Cache,s->cache==TRUE);

  colour_show(h,BG_BGcolour);
  background_show_texture(s,h);
  background_show_custom(s,h);
  window_set_fades(Page_Background,h,s);

  // Pin appearance

  h=pager_get_page(p,Page_PinAppear);

  colour_show(h,PA_TextColour);
  colour_show(h,PA_OutlineColour);
  switch (s->icon_appearance) {
    case APPEARANCE_THEME:
      radiobutton_set_state(0,h,PA_Theme,1);
      break;

    case APPEARANCE_BLENDED:
      radiobutton_set_state(0,h,PA_Blend,1);
      break;

    case APPEARANCE_OUTLINE:
      radiobutton_set_state(0,h,PA_Outline,1);
      break;
  }

  stringset_set_value(h,PA_Size,iconsize_strings,s->iconsize);

  window_set_fades(Page_PinAppear,h,s);

  // Pin arrangement
  h=pager_get_page(p,Page_PinArrange);
  stringset_set_value(h,PA_Iconize,iconize_strings,s->iconize_dest);
  stringset_set_value(h,PA_IconStack,stack_strings,s->iconize_stack);
  stringset_set_value(h,PA_Tidy,tidy_strings,s->tidy_dest);
  stringset_set_value(h,PA_TidyStack,stack_strings,s->tidy_stack);
  optionbutton_set_state(0,h,PA_Grid,s->snap_to_grid);
  window_set_fades(Page_PinArrange,h,s);
  
  // Stickies
  h=pager_get_page(p,Page_Sticky);
  if (s->sticky_font==NULL) {
    displayfield_set_value(0,h,ST_Font,msgtrans("Deskfont"));
    displayfield_set_value(0,h,ST_Height,"");
    displayfield_set_value(0,h,ST_Aspect,"");
  } else {
    char text[16];
    displayfield_set_value(0,h,ST_Font,s->sticky_font);
    sprintf(text,"%d pt",s->sticky_height/16);
    displayfield_set_value(0,h,ST_Height,text);
    sprintf(text,"%d %%",s->sticky_aspect);
    displayfield_set_value(0,h,ST_Aspect,text);
  }
  if (s->sticky_filename==NULL) {
    writablefield_set_value(0,h,ST_SaveTo,"Choices:Pinboard.Stickies");
  } else {
    writablefield_set_value(0,h,ST_SaveTo,s->sticky_filename);
  }

  // Storage
  h=pager_get_page(p,Page_Storage);
  if (s->savedfiles_path) {
    writablefield_set_value(0,h,SF_Location,s->savedfiles_path);
  } else {
    writablefield_set_value(0,h,SF_Location,"Choices:Pinboard.SavedFiles");
  }
  stringset_set_value(h,SF_Autosave,autosave_strings,s->autosave);
  stringset_set_value(h,SF_Overwrite,overwrite_strings,s->overwrite);

  // watermark
  h=pager_get_page(p,Page_Watermark);
  optionbutton_set_state(0,h,WM_Enable,s->watermark_enabled);
  radiobutton_set_state(0,h,watermark_component(s->watermark_location),1);
  numberrange_set_value(0,h,WM_Xoffset,s->x_offset);
  numberrange_set_value(0,h,WM_Yoffset,s->y_offset);
  window_set_fades(Page_Watermark,h,s);

  if (s->watermark_filename) {
    watermark_load_file(s->watermark_filename,s,h,FALSE);
  }

  // misc
  h=pager_get_page(p,Page_Misc);
  optionbutton_set_state(0,h,Misc_NoAdjust,s->disable_adjust);
}

#define settings_realloc(d,s)   if ((d)) {       \
    (d)=NULL;                                    \
    if (!flex_alloc((flex_ptr)&(d),1+strlen(s))) { \
      msgtrans_error("NoMem");                   \
    } else {                                     \
      strcpy(d,s);                               \
    }                                            \
  }

void settings_copy(settings *dest,settings *source)
{
  // free anything from dest
  if (dest->custom_image_filename) flex_free((flex_ptr)&dest->custom_image_filename);
  if (dest->sticky_font) flex_free((flex_ptr)&dest->sticky_font);
  if (dest->sticky_filename) flex_free((flex_ptr)&dest->sticky_filename);
  if (dest->savedfiles_path) flex_free((flex_ptr)&dest->savedfiles_path);
  if (dest->watermark_filename) flex_free((flex_ptr)&dest->watermark_filename);

  memcpy(dest,source,sizeof(settings));

  // realloc anything in dest
  settings_realloc(dest->custom_image_filename,source->custom_image_filename);
  settings_realloc(dest->sticky_font,source->sticky_font);
  settings_realloc(dest->sticky_filename,source->sticky_filename);
  settings_realloc(dest->savedfiles_path,source->savedfiles_path);
  settings_realloc(dest->watermark_filename,source->watermark_filename);
}

void settings_init(pager *p)
{
  memset(&current_settings,0,sizeof(settings));
  memset(&edited_settings,0,sizeof(settings));

  settings_load(&current_settings);
  settings_copy(&edited_settings,&current_settings);

  settings_update_windows(p,&edited_settings);
}

/* two pass append_printf.  If format contains pointers to flex data then it may move when
   we extend the save store which leads to unpleasantness.
   Flags bit 0 - allocate memory
   Flags bit 1 - do the writing */

static int data_append_printf(int flags,char **store,char *format,...)
{
  va_list ap;
  int blocksize;
    
  va_start(ap,format);
  blocksize=vsnprintf(NULL,0,format,ap);
  va_end(ap);

  if (flags & 1) {  
    if (!flex_extend((flex_ptr)store,flex_size((flex_ptr)store)+blocksize)) {
      return 0;
    }
  }

  if (flags & 2) {
    int append_loc=flex_size((flex_ptr)store)-blocksize-1;
    va_start(ap,format);
    vsnprintf(*store+append_loc,blocksize+1,format,ap);
    va_end(ap);
  }

  return 1;
}

char *settings_iconstack_text(stack_type s)
{
  if (s==HORIZONTAL) return "";
  return "-ISV";
}

char *settings_tidystack_text(stack_type s)
{
  if (s==HORIZONTAL) return "";
  return "-TSV";
}

char *settings_iconize_text(iconize_type s)
{
  switch (s) {
  case ICONIZE_BUTTON:      return "";
  case ICONIZE_ICONBAR:     return "-ITIB";
  case ICONIZE_TOPLEFT:     return "-ITTL";
  case ICONIZE_TOPRIGHT:    return "-ITTR";
  case ICONIZE_BOTTOMLEFT:  return "-ITBL";
  case ICONIZE_BOTTOMRIGHT: return "-ITBR";
  }
  return "";
}

char *settings_tidy_text(tidy_type s)
{
  switch (s) {
  case TIDY_TOPLEFT:     return "-TTTL";
  case TIDY_TOPRIGHT:    return "-TTTR";
  case TIDY_BOTTOMLEFT:  return "-TTBL";
  case TIDY_BOTTOMRIGHT: return "-TTBR";
  }
  return "";
}

int _settings_export(char **pinsetup,char **storage,settings *s)
{
  // export to memory in case we're trying the settings out
  // storage is separate as we need to put it in PreDesk before the 
  // wimp part of the pinboard starts up

  if (!flex_alloc((flex_ptr)pinsetup,1)) return 0;

  *(*pinsetup)=0;
  
  if (!flex_alloc((flex_ptr)storage,1)) return 0;

  *(*storage)=0;

  // export the settings :)
  // Backdrop
  if (s->background_type==BACKDROP_STANDARD && s->random) {
    if (!data_append_printf(3,pinsetup,"Run BootResources:Configure.Textures.RTexture%s\n",s->lighter?" L":"")) return 0;
  }

  if (!data_append_printf(3,pinsetup,"Backdrop -Col &%08x -TC &%08x ",s->background_colour,s->icon_text_colour)) return 0;
  switch (s->icon_appearance) {
    case APPEARANCE_THEME:
      break;
    case APPEARANCE_BLENDED:
      if (!data_append_printf(3,pinsetup,"-BT ")) return 0;
      break;
    case APPEARANCE_OUTLINE:
      if (!data_append_printf(3,pinsetup,"-OTC &%08x ",s->outline_colour)) return 0;
      break;
  }
  switch (s->background_type) {
    case BACKDROP_PLAIN:
      if (!data_append_printf(3,pinsetup,"-R\n")) return 0;
      break;
    case BACKDROP_CUSTOM:
      switch (s->custom_image_type) {
        case CUSTOM_SCALED:
          if (!data_append_printf(3,pinsetup,"-S ")) return 0;
          break;
        case CUSTOM_CENTRED:
          if (!data_append_printf(3,pinsetup,"-C ")) return 0;
          break;
        case CUSTOM_TILED:
          if (!data_append_printf(3,pinsetup,"-T ")) return 0;
          break;
        case CUSTOM_FULLSCREEN:
          if (!data_append_printf(3,pinsetup,"-F ")) return 0;
          break;
      }
      if (s->filetype==0xc85 && s->cache) {
        if (!data_append_printf(3,pinsetup,"-CJ ")) return 0;
      }
      if (!data_append_printf(1,pinsetup,"%s\n",s->custom_image_filename)) return 0;
      if (!data_append_printf(2,pinsetup,"%s\n",s->custom_image_filename)) return 0;
      break;
    case BACKDROP_STANDARD:
      if (s->random==FALSE) {
        char filename[128];
        pinboard_get_tile_filename(s->standard_image_ref,s->standard_image_max,s->lighter,filename);
        if (!data_append_printf(3,pinsetup,"-Tile %s\n",filename)) return 0;
      } else {
        if (!data_append_printf(3,pinsetup,"\n")) return 0;
      }
      break;
  }

  // Pinboardoptions
  if (!data_append_printf(3,pinsetup,"X PinboardOptions ")) return 0;
  if (s->snap_to_grid) {
    if (!data_append_printf(3,pinsetup,"-G ")) return 0;
  }

  if (s->disable_adjust) {
    if (!data_append_printf(3,pinsetup,"-DA ")) return 0;
  }

  switch (s->iconsize) {
    case ICONSIZE_NORMAL:
      break;
    case ICONSIZE_LARGE:
      if (!data_append_printf(3,pinsetup,"-LI ")) return 0;
      break;
    case ICONSIZE_SMALL:
      if (!data_append_printf(3,pinsetup,"-SI ")) return 0;
      break;
  }

  if (!data_append_printf(3,pinsetup,"%s %s %s %s ",settings_iconize_text(s->iconize_dest),
                                            settings_iconstack_text(s->iconize_stack),
                                            settings_tidy_text(s->tidy_dest),
                                            settings_tidystack_text(s->tidy_stack))) return 0;

  switch (s->autosave) {
    case AUTOSAVE_OFF:
      break;
    case AUTOSAVE_CONFIRM:
      if (!data_append_printf(3,pinsetup,"-CS ")) return 0;
      break;
    case AUTOSAVE_ALWAYS:
      if (!data_append_printf(3,pinsetup,"-AS ")) return 0;
      break;
  }

  switch (s->overwrite) {
    case OVERWRITE_ALWAYS:
      if (!data_append_printf(3,pinsetup,"\n")) return 0;
      break;
    case OVERWRITE_CONFIRM:
      if (!data_append_printf(3,pinsetup,"-OC\n")) return 0;
      break;
    case OVERWRITE_RENAME:
      if (!data_append_printf(3,pinsetup,"-OR\n")) return 0;
      break;
  }

  

  // watermark
  // preserve settings so it's easily turn off and onable
  if (s->watermark_filename) {
    if (!data_append_printf(3,pinsetup,"X PinboardWatermark %s ",s->watermark_enabled?"-on":"-off")) return 0;
    if (!data_append_printf(1,pinsetup,"%s -X %d -Y %d %s\n",watermark_get_cmd(s->watermark_location),s->x_offset,s->y_offset,s->watermark_filename?s->watermark_filename:"")) return 0;
    if (!data_append_printf(2,pinsetup,"%s -X %d -Y %d %s\n",watermark_get_cmd(s->watermark_location),s->x_offset,s->y_offset,s->watermark_filename?s->watermark_filename:"")) return 0;
  }

  // stickydefaults
  if (!data_append_printf(3,pinsetup,"X PinboardStickies ")) return 0;
  if (s->sticky_font) {
    if (!data_append_printf(1,pinsetup,"-Font %s,%d,%d ",s->sticky_font,s->sticky_height,(s->sticky_height*s->sticky_aspect)/100)) return 0;
    if (!data_append_printf(2,pinsetup,"-Font %s,%d,%d ",s->sticky_font,s->sticky_height,(s->sticky_height*s->sticky_aspect)/100)) return 0;
  }
  if (s->sticky_filename) {
    if (!data_append_printf(1,pinsetup,"-File %s",s->sticky_filename)) return 0;
    if (!data_append_printf(2,pinsetup,"-File %s",s->sticky_filename)) return 0;
  }
  if (!data_append_printf(3,pinsetup,"\n")) return 0;

  // storage
  if (s->savedfiles_path) {
    if (!data_append_printf(1,storage,"Set Pinboard$Path %s.\n",s->savedfiles_path)) return 0;
    if (!data_append_printf(2,storage,"Set Pinboard$Path %s.\n",s->savedfiles_path)) return 0;
  }


  return 1;
}

int settings_export(char **pinsetup,char **storage,settings *s)
{
  int r;

  *pinsetup=NULL;
  *storage=NULL;

  r=_settings_export(pinsetup,storage,s);

  if (r==0) {
    msgtrans_error("NoMem");

    if (*pinsetup) {
      flex_free((flex_ptr)pinsetup);
      *pinsetup=NULL;
    }
    if (*storage) {
      flex_free((flex_ptr)storage);
      *storage=NULL;
    }
  }
  return r;
}

void settings_try(settings *s)
{
  // create an in-memory bit and run it
  char *pinsetup=NULL,*storage=NULL;
  int offset,length;
  _kernel_oserror *e;

  if (!settings_export(&pinsetup,&storage,s)) return;
  if (storage) flex_free((flex_ptr)&storage); // we don't mess with this bit

  if (pinsetup==NULL) return;

  settings_tried=TRUE;

  length=flex_size((flex_ptr)&pinsetup);

//  _swix(OS_File,_INR(0,5),10,"RAM:$.out",0xfff,0,pinsetup,pinsetup+length-1);
//  _swix(OS_CLI,_IN(0),"Filer_Run RAM:$.out");
  for (offset=0;offset<length;offset++) if (*(pinsetup+offset)<32) *(pinsetup+offset)=0;
  for (offset=0;offset<length;offset+=1+strlen(pinsetup+offset)) {
    if (strncmp(pinsetup+offset,"X PinboardStickies",18)!=0) {
      if (strncmp(pinsetup+offset,"Run ",4)==0) {
        e=wimp_start_task(pinsetup+offset,NULL);
      } else {
        e=_swix(OS_CLI,_IN(0),offset+pinsetup);
      }
    } else {
      e=NULL;
    }
    if (e) error(e,FALSE);
  }
  flex_free((flex_ptr)&pinsetup);
}

static int strnullcmp(char *a,char *b)
{
  if (a==NULL && b==NULL) return 0;
  if ((a==NULL && b!=NULL) || (a!=NULL && b==NULL)) return 1;

  return strcasecmp(a,b);
}

static int settings_move_stickies(settings *current,settings *edited)
{
  char *buffer;
  _kernel_oserror *e;

  if (!flex_alloc((flex_ptr)&buffer,32+strlen(current->sticky_filename)+1)) {
    msgtrans_error("NoMem");
    return 0;                
  }

  if (current->sticky_filename) {
    sprintf(buffer,"PinboardStickies -File %s",current->sticky_filename);
    e=_swix(OS_CLI,_IN(0),buffer);
    if (e) {
      flex_free((flex_ptr)&buffer);
      error(e,FALSE);
      return 0;
    }

    // file is now saved and up-to-date.  Copy it over.
    e=_swix(OS_FSControl,_INR(0,3),26,current->sticky_filename,edited->sticky_filename,(1<<1));
    if (e) {
      flex_free((flex_ptr)&buffer);
      error(e,FALSE);
      return 0;
    }
  }

  flex_free((flex_ptr)&buffer);

  return 1;
}

#define _TEMP_PATH " - not a valid path - "

static int settings_move_savedfiles(settings *current,settings *edited)
{
  _kernel_oserror *e;
  char *newpath;

  if (!flex_alloc((flex_ptr)&newpath,2+strlen(edited->savedfiles_path))) {
    msgtrans_error("NoMem");
    return 0;
  }

  sprintf(newpath,"%s.",edited->savedfiles_path);

  // need to adjust the pinboard path to stop the system thinking the files are moving
  e=_swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",_TEMP_PATH,strlen(_TEMP_PATH),0,0);
  if (e) {
    flex_free((flex_ptr)&newpath);
    error(e,FALSE);
    return 0;
  }

  _swix(Hourglass_On,0);

  // move the files
  e=_swix(OS_FSControl,_INR(0,3),26,current->savedfiles_path,edited->savedfiles_path,(1<<1) | (1<<0) | (1<<7));
  if (e) {
    flex_free((flex_ptr)&newpath);
    _swix(Hourglass_Off,0);
    error(e,FALSE);
    return 0;
  }

  // update the path to the new one
  _swix(OS_SetVarVal,_INR(0,4),"Pinboard$Path",newpath,strlen(newpath),0,0);

  flex_free((flex_ptr)&newpath);

  _swix(Hourglass_Off,0);

  return 1;
}

int settings_write(settings *current,settings *edited)
{
  // sort out the settings
  char *pinsetup,*storage;
  _kernel_oserror *e;

  if (path_readonly(edited->sticky_filename)) {
    msgtrans_error("PathRO");
    return 0;
  }

  if (path_readonly(edited->savedfiles_path)) {
    msgtrans_error("PathRO");
    return 0;
  }
  
  // relocate the stickies if need be
  if (strnullcmp(current->sticky_filename,edited->sticky_filename)) {
    if (settings_move_stickies(current,edited)==0) return 0;
  }

  // produce settings files
  if (settings_export(&pinsetup,&storage,edited)==0) return 0;
  
  // save them
  e=_swix(OS_File,_INR(0,2)|_INR(4,5),10,"<Choices$Write>.Boot.Tasks.PinSetup",0xfeb,pinsetup,pinsetup+flex_size((flex_ptr)&pinsetup)-1);
  if (e) {
    error(e,FALSE);
    flex_free((flex_ptr)&pinsetup);
    flex_free((flex_ptr)&storage);
    return 0;
  }

  flex_free((flex_ptr)&pinsetup);

  // now we need to look into the storage moving
  if (strnullcmp(current->savedfiles_path,edited->savedfiles_path)) {
    if (settings_move_savedfiles(current,edited)==0) {
      flex_free((flex_ptr)&storage);
      return 0;
    }
  }

  e=_swix(OS_File,_INR(0,2)|_INR(4,5),10,"<Choices$Write>.Boot.PreDesk.!PinSetup",0xfeb,storage,storage+flex_size((flex_ptr)&storage)-1);
  if (e) {
    error(e,FALSE);
    flex_free((flex_ptr)&storage);
    return 0;
  }
  flex_free((flex_ptr)&storage);

  // execute them to make them live
  _swix(OS_CLI,_IN(0),"Filer_Run <Choices$Write>.Boot.Tasks.PinSetup");

  return 1;
}
