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

#include "akbd.h"
#include "swis.h"

#include "tboxlibs/toolbox.h"
#include "tboxlibs/iconbar.h"
#include "tboxlibs/menu.h"
#include "tboxlibs/flex.h"
#include "tboxlibs/wimp.h"
#include "tboxlibs/wimplib.h"
#include "tboxlibs/event.h"

#include "pinboard.h"
#include "indirected.h"
#include "events.h"
#include "iconize.h"
#include "drag.h"
#include "tinydirs.h"
#include "fs.h"
#include "msgtrans.h"
#include "options.h"
#include "debug.h"

extern pins *pinlist;

ObjectId tinydir_bigmenu=0;
static ObjectId tinydir_smallmenu=0;

static int tinydir_deselect_on_close=-1;

static int tinydir_filterclick(int event_code,ToolboxEvent *event,IdBlock *id,void *handle);
static int tinydir_menu_closed(int event_code,ToolboxEvent *event,IdBlock *id,void *handle);
static void tinydir_set_bigmenu_opts(void);

int tinydir_find_icon(int icon_handle)
{
  int i;
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])/* && pin_is_anyfile(&pinlist->info[i])*/) {
      if (pinlist->info[i].iconbar_icon==icon_handle) return i;
    }
  }

  return -1;
}

static int tinydir_get_empty(void)
{
  int i;
  for (i=0;i<pinlist->count;i++) {
    if (pinlist->info[i].icontype==pin_type_tinydir) return i; // tinydir but neither window nor file
  }
  
  return -1;
}

#define tinydir_present() (tinydir_count(0,1)!=0)

// count selection or icons up to 'max' entries (we may only be interested in 0,1,many)
int tinydir_count(int selected,int max)
{
  int i,count=0;
  WimpGetIconStateBlock istate;
  istate.window_handle=-2;
  
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      if (pin_is_anyfile(&pinlist->info[i])) {
        if (selected) {
          istate.icon_handle=pinlist->info[i].iconbar_icon;
          wimp_get_icon_state(&istate);
          if (istate.icon.flags & WimpIcon_Selected) count++;
        } else {
          count++;
        }
      }
      if (count==max) return max;
    }
  }
  return count;
}

static void tinydir_select_single(int icon)
{
  WimpSetIconStateBlock istate;
  WimpGetIconStateBlock getstate;
  getstate.window_handle=-1;
  istate.window_handle=-1;
  istate.clear_word=WimpIcon_Selected;
  
  // clear selection apart from 'icon' which gets selected instead
  int i;
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      if (pin_is_anyfile(&pinlist->info[i])) {
        istate.icon_handle=pinlist->info[i].iconbar_icon;
        getstate.icon_handle=pinlist->info[i].iconbar_icon;
        wimp_get_icon_state(&getstate);
        if (icon==pinlist->info[i].iconbar_icon) {
          // select this one
          if ((getstate.icon.flags & WimpIcon_Selected)==0) {
            istate.EOR_word=WimpIcon_Selected;
            wimp_set_icon_state(&istate);
          }
        } else {
          if ((getstate.icon.flags & WimpIcon_Selected)) {
            istate.EOR_word=0;
            wimp_set_icon_state(&istate);
          }
        }
      }
    }
  }
}

static int tinydir_none_selected(void)
{
  int i;
  WimpGetIconStateBlock istate;
  istate.window_handle=-1;
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      istate.icon_handle=pinlist->info[i].iconbar_icon;
      wimp_get_icon_state(&istate);
      if (istate.icon.flags & WimpIcon_Selected) return 0;
    }  
  }

  return 1;
}

static int tinydir_event_clearselection(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(id);
  IGNORE(handle);
  
  tinydir_select_single(-1);
  tinydir_set_bigmenu_opts();

  return 1;
}

static int tinydir_event_selectall(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  WimpSetIconStateBlock istate;
  WimpGetIconStateBlock getstate;
  int i;

  IGNORE(event_code);
  IGNORE(event);
  IGNORE(id);
  IGNORE(handle);

  tinydir_deselect_on_close=-1;
  
  getstate.window_handle=-1;
  istate.window_handle=-1;
  istate.EOR_word=WimpIcon_Selected;
  istate.clear_word=WimpIcon_Selected;
    
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      if (pin_is_anyfile(&pinlist->info[i])) {
        getstate.icon_handle=pinlist->info[i].iconbar_icon;
        wimp_get_icon_state(&getstate);
        if ((getstate.icon.flags & WimpIcon_Selected)==0) {
          istate.icon_handle=getstate.icon_handle;
          wimp_set_icon_state(&istate);
        }
      }
    }
  }

  tinydir_set_bigmenu_opts();

  return 1;
}

static void tinydir_set_smallmenu_opts(void)
{
  menu_set_fade(0,tinydir_smallmenu,2,tinydir_get_empty()==-1);
}

static void tinydir_set_bigmenu_opts(void)
{
  switch (tinydir_count(1,2)) {
  case 0:
    menu_set_fade(0,tinydir_bigmenu,0,tinydir_get_empty()!=-1);
    menu_set_entry_text(0,tinydir_bigmenu,2,"Remove selection");
    menu_set_fade(0,tinydir_bigmenu,1,1);
    menu_set_fade(0,tinydir_bigmenu,2,1);
    menu_set_fade(0,tinydir_bigmenu,3,1);
    menu_set_fade(0,tinydir_bigmenu,4,1);
    break;

  case 1:
    menu_set_fade(0,tinydir_bigmenu,0,0);
    menu_set_entry_text(0,tinydir_bigmenu,2,"Remove icon");
    menu_set_fade(0,tinydir_bigmenu,1,0);
    menu_set_fade(0,tinydir_bigmenu,2,0);
    menu_set_fade(0,tinydir_bigmenu,3,0);
    menu_set_fade(0,tinydir_bigmenu,4,0);
    break;

  default:
    menu_set_fade(0,tinydir_bigmenu,0,0);
    menu_set_entry_text(0,tinydir_bigmenu,2,"Remove selection");
    menu_set_fade(0,tinydir_bigmenu,1,0);
    menu_set_fade(0,tinydir_bigmenu,2,0);
    menu_set_fade(0,tinydir_bigmenu,3,0);
    menu_set_fade(0,tinydir_bigmenu,4,1);
    break;
  }

}

void tinydir_remove(_backdrop_state *state,int p,int delete_pin)
{
  // empty icon - just remove

  if (tinydir_deselect_on_close == pinlist->info[p].iconbar_icon) tinydir_deselect_on_close=-1;
  
  if ((pinlist->info[p].icontype & 0xff) == pin_type_tinydir) {
    toolbox_delete_object(0,pinlist->info[p].obj);
    if (delete_pin) {
      pin_remove(state,p);
    } else {
      pinlist->info[p].obj=0;
      pinlist->info[p].iconbar_icon=-1;
    }
    return;
  }  
  
  // if we have 1+ file icons, just do a delete  
  if (tinydir_count(0,2)>1) {
    toolbox_delete_object(0,pinlist->info[p].obj);
    if (delete_pin) {
      pin_remove(state,p);
    } else {
      pinlist->info[p].obj=0;
      pinlist->info[p].iconbar_icon=-1;
    }
  } else {
    // repurpose as an empty icon...
    if (delete_pin) {
      tinydir_select_single(-1);
      indirected_remove(pinlist->info[p].data.filename_offset);
      pinlist->info[p].data.filename_offset=0;
      pinlist->info[p].icontype=pin_type_tinydir;
      iconbar_set_text(0,pinlist->info[p].obj,"");
      iconbar_set_sprite(0,pinlist->info[p].obj,"directory");
      iconbar_set_help_message(0,pinlist->info[p].obj,msgtrans_lookup("IcT"));

      // icon number may change after this...
      iconbar_get_icon_handle(0,pinlist->info[p].obj,&pinlist->info[p].iconbar_icon);
 
      //      pin_set_spritesize(&pinlist->info[p],"directory");
    } else {
      // we need a new empty icon
      toolbox_delete_object(0,pinlist->info[p].obj);
      pinlist->info[p].obj=0;
      pinlist->info[p].iconbar_icon=-1;
      tinydir_add(state,NULL,-1);
    }
  }  
}

void tinydir_remove_command(_backdrop_state *state,char *path)
{
  int i;

  if (path==NULL) {
    // look for an empty icon
    if ((i=tinydir_get_empty())!=-1) tinydir_remove(state,i,1);
  } else {
    for (i=0;i<pinlist->count;i++) {
      if (pin_is_tiny(&pinlist->info[i])) {
        if (pin_is_fsfile(&pinlist->info[i])) {
          if (strcmp(path,indirected(pinlist->info[i].data.filename_offset,char *))==0) {
            tinydir_remove(state,i,1);
          }
        }
      }
    }
  }

  pin_remove_empties(state);
  
}

static void tinydir_run(int icon)
{
  char *detail=indirected(pinlist->info[icon].data.filename_offset,char *);
  char exec[strlen(detail)+1+10+10];
  sprintf(exec,"Filer_Run %s %s",akbd_pollsh()?"-Shift":"-NoShift",detail);
  _swix(OS_CLI,_IN(0),exec);
}

static int tinydir_event_openparent(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  char *detail;
  char *newpath;
  char *ptr;
  int p=pin_find_object(id->parent_id);

  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);
  
  if (p==-1) return 0;
  
  detail=indirected(pinlist->info[p].data.filename_offset,char *);  
  
  if (!flex_alloc((flex_ptr)&newpath,16+strlen(detail))) {
    msgtrans_report_error("NoMem");
    return 1;
  }
  sprintf(newpath,"Filer_OpenDir %s",detail);
  if ((ptr=strrchr(newpath,'.'))!=NULL) {
    *ptr=0;  
  }
  _swix(OS_CLI,_IN(0),newpath);
  flex_free((flex_ptr)&newpath);
  
  return 1;
}

static void tinydir_remove_range(_backdrop_state *state,int min,int max)
{
  WimpGetIconStateBlock istate;
  int fa_task=0;
  unsigned int flags=options_get();
  int slot_next,slot_free;
  int p,deleted;
  
  // 'normal' remove
  istate.window_handle=-2;

  // check configuration - interactive or not?
  if (!OPTIONS_FILERACTION(flags)) {
    fa_task=-1;
  }
  
  // check free space
  wimp_slot_size(-1,-1,NULL,&slot_next,&slot_free);
  if (slot_next+slot_free<65536) fa_task=-1;
  
  do {
    deleted=0;
    for (p=min;p<max;p++) {
      if (pin_is_tiny(&pinlist->info[p])) {
        istate.icon_handle=pinlist->info[p].iconbar_icon;
        wimp_get_icon_state(&istate);
        if (istate.icon.flags & WimpIcon_Selected) {
         
          if (pin_is_localfile(&pinlist->info[p])) {
            char *sourcepath,*sourceleaf;
            if (fs_pathprefix_from_path(&sourcepath,indirected(pinlist->info[p].data.filename_offset,char *))) {
              if (fs_leafname_from_path(&sourceleaf,indirected(pinlist->info[p].data.filename_offset,char *))) {
                if (fa_task==0) {
                  if (wimp_start_task("Filer_Action",&fa_task)!=NULL) fa_task=-1; // resort to manual delete
                }
                if (fa_task>0) {
                  _swix(FilerAction_SendSelectedDirectory,_INR(0,1),fa_task,sourcepath);
                  _swix(FilerAction_SendSelectedFile,_INR(0,1),fa_task,sourceleaf);
                } else {
                  // manual delete
                  char *cmdbuf;
                  if (flex_alloc((flex_ptr)&cmdbuf,14+strlen(indirected(pinlist->info[p].data.filename_offset,char *)))) {
                    wimp_command_window((int)"Pinboard: delete files");
                    sprintf(cmdbuf,"Wipe %s %sC%sF%sV",
                            indirected(pinlist->info[p].data.filename_offset,char *),
                            OPTIONS_CONFIRMDELETE(flags)?"":"~",
                            OPTIONS_FORCE(flags)?"":"~",
                            OPTIONS_VERBOSE(flags)?"":"~");
                    _swix(OS_CLI,_IN(0),cmdbuf);
                    flex_free((flex_ptr)&cmdbuf);
                  } else {
                    msgtrans_report_error("NoMem");
                  }
                }
                flex_free((flex_ptr)&sourceleaf);
              }
              flex_free((flex_ptr)&sourcepath);
            }
          } else {
            tinydir_remove(state,p,1);
            p=pinlist->count;
            deleted=1;
          }
        }
      }
    }
  } while (deleted);  

  if (fa_task>0) {
    // complete the request
    // docs different to source headers, so set both confirm bits just in case?
    _swix(FilerAction_SendStartOperation,_INR(0,4),fa_task,2,
          (OPTIONS_VERBOSE(flags)<<0) | (OPTIONS_CONFIRMDELETE(flags)<<1) | (OPTIONS_FORCE(flags)<<2) | (OPTIONS_NEWER(flags)<<3) | (OPTIONS_CONFIRMDELETE(flags)<<5),
          0,0);
  }
  if (fa_task==-1) {
    wimp_command_window(0);
  }
}

static int tinydir_event_remove(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  // delete icon(s), unless it's the last one with a filename
  int p;
  _backdrop_state *state=(_backdrop_state *)handle;
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(id);
  

  // are we removing the very last null icon?  If so, then different handling.
  //  p=pin_find_object(id->parent_id);
  //  if (p==-1) return 0;
  if ((p=tinydir_get_empty())!=-1) {
    // ie tiny, but not allocated
    tinydir_remove(state,p,1);
    tinydir_set_bigmenu_opts();
    tinydir_set_smallmenu_opts();
    return 1;
  }

  tinydir_remove_range(state,0,pinlist->count);
  
  tinydir_set_bigmenu_opts();
  tinydir_set_smallmenu_opts();
  
  return 1;
}

void tinydir_init(_backdrop_state *state)
{
  toolbox_create_object(0,"tinydirmini",&tinydir_smallmenu);
  event_register_toolbox_handler(tinydir_smallmenu,TEvent_Tinydir_RemoveIcon,tinydir_event_remove,state);
  toolbox_create_object(0,"tinydirmenu",&tinydir_bigmenu);
  event_register_toolbox_handler(tinydir_bigmenu,TEvent_Tinydir_RemoveIcon,tinydir_event_remove,state);    
  event_register_toolbox_handler(tinydir_bigmenu,TEvent_Tinydir_SelectAll,tinydir_event_selectall,state);    
  event_register_toolbox_handler(tinydir_bigmenu,TEvent_Tinydir_ClearSelection,tinydir_event_clearselection,state);    
  event_register_toolbox_handler(tinydir_bigmenu,TEvent_Tinydir_OpenParent,tinydir_event_openparent,state);    
  event_register_toolbox_handler(-1,TEvent_Tinydir_FilterClick,tinydir_filterclick,state);
  event_register_toolbox_handler(tinydir_smallmenu,Menu_HasBeenHidden,tinydir_menu_closed,NULL);
  event_register_toolbox_handler(tinydir_bigmenu,Menu_HasBeenHidden,tinydir_menu_closed,NULL);
}

static void tinydir_set_button_type(int icon)
{
  WimpSetIconStateBlock istate_set;
  WimpGetIconStateBlock istate;
  
  // adjust the icon button type
  istate.icon_handle=icon;
  istate.window_handle=-1;
  wimp_get_icon_state(&istate);
  
  istate_set.window_handle=istate.window_handle;
  istate_set.icon_handle=istate.icon_handle;
  istate_set.EOR_word=ButtonType_DoubleClickDrag *WimpIcon_ButtonType;
  istate_set.clear_word=ButtonType_Writable *WimpIcon_ButtonType;
  wimp_set_icon_state(&istate_set);
}

static int tinywin_clicked(_backdrop_state *state,int index)
{
  // click on tinywin - open and remove
  iconize_restore(pinlist->info[index].data.window.handle);
  toolbox_delete_object(0,pinlist->info[index].obj);
  pin_remove(state,index);
  
  return 1;
}

static int tinydir_menu_closed(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  WimpSetIconStateBlock istate;
  unsigned int menustate;

  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);
  
  // if we click Menu on an already open icon menu then we get a menu_close event.  We don't want that one.
  toolbox_get_object_state(0,id->self_id,&menustate);
  if (menustate==1) return 1; // menu is still open, don't deselect anything
 
  if (tinydir_deselect_on_close!=-1) {
    istate.window_handle=-1;
    istate.icon_handle=tinydir_deselect_on_close;
    istate.EOR_word=0;
    istate.clear_word=WimpIcon_Selected;
    wimp_set_icon_state(&istate);
    tinydir_deselect_on_close=-1;
  }
  //  event_deregister_toolbox_handler(id->self_id,Menu_HasBeenHidden,tinydir_menu_closed,handle);
  return 1;
}

static int tinydir_clicked(int index,int buttons,_backdrop_state *state)
{
  WimpSetIconStateBlock istate;
  // actions:
  // select: select single
  // adjust: toggle selection
  // double-select: open and clear that icon's selection
  // double-adjust: open and remove icon
  // menu: if nothing selected, select icon and clear the selection on menu close; if something selected, don't touch  
  // drag select: select single icon and drag it
  // drag adjust: toggle and drag
  
  if (pin_needs_recheck(&pinlist->info[index])) {
    pin_recheck(state,index);
  }

  switch (buttons) {
    case Wimp_MouseButtonMenu:
    {
      TopLeft pos;
      WimpGetPointerInfoBlock mouse;
      // if nothing selected, select this icon
      if (tinydir_none_selected() || tinydir_get_empty()!=-1) {
        istate.window_handle=-1;
        istate.icon_handle=pinlist->info[index].iconbar_icon;
        istate.EOR_word=WimpIcon_Selected;
        istate.clear_word=WimpIcon_Selected;
        wimp_set_icon_state(&istate);
        tinydir_deselect_on_close=pinlist->info[index].iconbar_icon;
      }
      
      wimp_get_pointer_info(&mouse);
      pos.x=mouse.x-64;
      if (pinlist->info[index].icontype==pin_type_tinydir) {
        menu_get_height(0,tinydir_smallmenu,&pos.y);
        pos.y+=96;
        toolbox_show_object(0,tinydir_smallmenu,Toolbox_ShowObject_TopLeft,&pos,pinlist->info[index].obj,0);
        tinydir_set_smallmenu_opts();
        //        if (clear_on_exit) event_register_toolbox_handler(tinydir_smallmenu,Menu_HasBeenHidden,tinydir_menu_closed,NULL);
      } else {
        tinydir_set_bigmenu_opts();
        menu_get_height(0,tinydir_bigmenu,&pos.y);
        pos.y+=96;
        toolbox_show_object(0,tinydir_bigmenu,Toolbox_ShowObject_TopLeft,&pos,pinlist->info[index].obj,0);
        //        if (clear_on_exit) event_register_toolbox_handler(tinydir_bigmenu,Menu_HasBeenHidden,tinydir_menu_closed,NULL); 
        toolbox_set_client_handle(0,tinydir_bigmenu,(void *)pinlist->info[index].obj);
      }
      return 1;
    }
    
    case Wimp_MouseButtonAdjust*256:
    // toggle icon
    {
      istate.window_handle=-1;
      istate.icon_handle=pinlist->info[index].iconbar_icon;
      istate.EOR_word=WimpIcon_Selected;
      istate.clear_word=0;
      wimp_set_icon_state(&istate);
    }
    break;
    
    case Wimp_MouseButtonSelect*256:
    // select only this icon, if it isn't already selected
    {
      WimpGetIconStateBlock getstate;
      getstate.window_handle=-1;
      getstate.icon_handle=pinlist->info[index].iconbar_icon;
      wimp_get_icon_state(&getstate);
      if ((getstate.icon.flags & WimpIcon_Selected)==0) tinydir_select_single(pinlist->info[index].iconbar_icon);
    }
    break;
      
    case Wimp_MouseButtonSelect:
      // open and deselect
      {
        istate.window_handle=-1;
        istate.icon_handle=pinlist->info[index].iconbar_icon;
        istate.clear_word=WimpIcon_Selected;
        istate.EOR_word=0;
        wimp_set_icon_state(&istate);
        if (pin_filetype(&pinlist->info[index])==0x2000) {
          if (mouse_dbl_drag(state)) filer_opendir(index);
          else tinydir_run(index);
        } else {
          tinydir_run(index);
        }
      }
      break;
      
      case Wimp_MouseButtonAdjust:
        // open and remove
        
        istate.window_handle=-1;
        istate.icon_handle=pinlist->info[index].iconbar_icon;
        istate.clear_word=WimpIcon_Selected;
        istate.EOR_word=WimpIcon_Selected;
        wimp_set_icon_state(&istate); // ensure selected so we pick it up for deletion
        tinydir_run(index);
        
        tinydir_remove_range(state,index,index+1);
        
        break;
      
      case Wimp_MouseButtonSelect*16:
      // drag
      {
        WimpGetIconStateBlock getstate;
        char spritename[13];
        iconbar_get_sprite(0,pinlist->info[index].obj,spritename,13,0);
        
        getstate.window_handle=-1;
        getstate.icon_handle=pinlist->info[index].iconbar_icon;
        wimp_get_icon_state(&getstate);
        
        drag_box_init(-2,&getstate.icon.bbox,tinydir_count(1,2)>1?"package":spritename,state);
      }
  }
  
  return 0;
}

int tinydir_filterclick(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  // to mimic TinyDirs, we need to be able to select, double-click and drag iconbar icons
  // the Toolbox system doesn't allow this.
  // so we do something horrible with an extra filter to pass these clicks down here
  // idblock is not set up by the filter
  
  FilterEvent *f=(FilterEvent *)event;  
  int i;
  _backdrop_state *state=(_backdrop_state *)handle;
  IGNORE(event_code);
  IGNORE(id);
  

  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      if (f->icon_handle==pinlist->info[i].iconbar_icon) {
        // found it!
        if (pin_is_anywin(&pinlist->info[i])) {
          if (f->buttons==Wimp_MouseButtonSelect) tinywin_clicked(state,i);
          return 1;
        } else {
          return tinydir_clicked(i,f->buttons,state);
        }
      }
    }
  }  
  
  return 0;
}

void tinywin_add(int window,int task,char *title,char *sprite)
{
  _kernel_oserror *e;
  pin newpin;
  int index;
  
  memset(&newpin,0,sizeof(pin));
    
  if ((e=toolbox_create_object(0,"tinydir",&newpin.obj))!=NULL) {
    wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
    return;
  }
  iconbar_set_text(0,newpin.obj,title);
  iconbar_set_sprite(0,newpin.obj,sprite);
  //        pin_set_spritesize(&newpin,sprite);
  
  newpin.icontype=pin_type_tinydir | pin_type_window;
  newpin.data.window.handle=window;
  newpin.data.window.task_handle=task;
  newpin.icondata.buffer=INDIRECT_FREEI;
  newpin.data.pin.alias=INDIRECT_FREEI;
  newpin.icondata.sprite=INDIRECT_FREEI;
  if ((index=pin_add(&newpin))==-1) {
    toolbox_delete_object(0,newpin.obj);
    return;  
  }
  
  iconbar_set_event(Iconbar_SetEvent_Select,pinlist->info[index].obj,TEvent_Tinywin_Clicked,0);
  iconbar_set_help_message(0,pinlist->info[index].obj,msgtrans_lookup("IcW"));
  
  toolbox_show_object(0,pinlist->info[index].obj,0,NULL,0,0);   
  iconbar_get_icon_handle(0,pinlist->info[index].obj,&pinlist->info[index].iconbar_icon);
}

static int tinydir_create_object(char *sprite,char *text,ObjectId *obj,int iconref,int set_button_type)
{
  _kernel_oserror *e;
  int icon;
    
  e=toolbox_create_object(0,"tinydirpos",obj);
 
  if (e!=NULL) {
    wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
    return -1;
  }
  iconbar_set_text(0,*obj,text);
  iconbar_set_sprite(0,*obj,sprite);
  toolbox_show_object(0,*obj,Toolbox_ShowObject_FullSpec,&iconref,0,0);

  iconbar_get_icon_handle(0,*obj,&icon);

  if (set_button_type) tinydir_set_button_type(icon);
  
  return icon;
}

void tinydir_move_icons(WimpGetPointerInfoBlock *mouse)
{
  // move the selection to the left of the target icon...
  int i,newicon;
  ObjectId obj;
  WimpGetIconStateBlock istate;
  char text[32],sprite[13];
  
  istate.window_handle=-2;
  for (i=0;i<pinlist->count;i++) {
    if (pin_is_tiny(&pinlist->info[i])) {
      istate.icon_handle=pinlist->info[i].iconbar_icon;
      wimp_get_icon_state(&istate);
      if (istate.icon.flags & WimpIcon_Selected) {
        // recreate and then delete

        iconbar_get_text(0,pinlist->info[i].obj,text,32,0);
        iconbar_get_sprite(0,pinlist->info[i].obj,sprite,13,0);

        if ((newicon=tinydir_create_object(sprite,text,&obj,mouse->icon_handle,pin_is_anyfile(&pinlist->info[i])))!=-1) {
          toolbox_delete_object(0,pinlist->info[i].obj);
          pinlist->info[i].obj=obj;
          if (tinydir_deselect_on_close==pinlist->info[i].iconbar_icon) tinydir_deselect_on_close=newicon;
          pinlist->info[i].iconbar_icon=newicon;
          //          pin_set_spritesize(&pinlist->info[i],sprite);
        }
      }
    }
  }
}

void tinydir_xfer_selection_from_pinboard(WimpGetPointerInfoBlock *mouse,_backdrop_state *state)
{
  // move the selection from the pinboard to the icon bar at a given insert point
  int i,newicon;
  ObjectId obj;
  
  for (i=0;i<pinlist->count;i++) {
    if (!pin_is_tiny(&pinlist->info[i])) {
      if (pinlist->info[i].icondata.flags & WimpIcon_Selected) {
        // move this one...
        if ((newicon=tinydir_create_object(indirected(pinlist->info[i].icondata.sprite,char *),indirected(pinlist->info[i].icondata.buffer,char *),&obj,mouse->icon_handle,pin_is_anyfile(&pinlist->info[i])))!=-1) {
          //          pin_set_spritesize(&pinlist->info[i],sprite);
          pinlist->info[i].obj=obj;
          pinlist->info[i].iconbar_icon=newicon;
          pinlist->info[i].icontype|=pin_type_tinydir;
          indirected_remove(pinlist->info[i].icondata.sprite);
          indirected_remove(pinlist->info[i].icondata.buffer);
          pinlist->info[i].icondata.sprite=INDIRECT_FREEI;
          pinlist->info[i].icondata.buffer=INDIRECT_FREEI;
          pin_redraw(state,i);
        }
      }    
    }
  }

  if ((i=tinydir_get_empty())!=-1) {
    // need to remove the leftover empty one
    tinydir_remove(state,i,1);
  }
  
}

void tinydir_set_sprite(ObjectId obj,int filetype,char *leaf_name)
{
  switch (filetype) {
  case 0x1000:
    iconbar_set_sprite(0,obj,"directory");
    break;
  
  case 0x2000:
    if (strlen(leaf_name)<=12) {
      iconbar_set_sprite(0,obj,leaf_name);
    } else {
      char spritename[13];
      memcpy(spritename,leaf_name,12);
      spritename[12]=0;
      iconbar_set_sprite(0,obj,spritename);
    }
    break;
    
  default:
    {
      char spritename[12];
      sprintf(spritename,"file_%03x",filetype);
      iconbar_set_sprite(0,obj,spritename);
    }
  }

}

void tinydir_xadd(_backdrop_state *state,char *path,int pos)
{
  // add a tinydir for which the path may or may not exist
  // if we can confirm it though...
  pin newpin;
  char *canon_name=NULL;
  char *leaf_name=NULL;
  char *truncated=NULL;
  int index;
  int type;
  _kernel_oserror *e;
  
  if (!fs_canon_nomoan(path,&canon_name)) {
    return;
  }

  // look up file details, in case
  if (_swix(OS_File,_INR(0,1)|_OUT(0),5,canon_name,&type)==NULL) {
    if (type!=0) {
      // it works!  So add a conventional tinydir
      char newpath[1+strlen(canon_name)];
      strcpy(newpath,canon_name);
      flex_free((flex_ptr)&canon_name);
      tinydir_add(state,newpath,pos);
      return;
    }
  }

  // add a tinydir representing the mystery file

  if (!fs_leafname_from_path(&leaf_name,canon_name)) {
    flex_free((flex_ptr)&canon_name);
    return;
  }
  
  memset(&newpin,0,sizeof(pin));
  newpin.icondata.buffer=INDIRECT_FREEI;
  newpin.data.pin.alias=INDIRECT_FREEI;
  newpin.icondata.sprite=INDIRECT_FREEI;
  newpin.data.filename_offset=INDIRECT_FREEI;

  // already present?
  if (pin_find_filename(canon_name,1)!=-1) {
    flex_free((flex_ptr)&canon_name);
    flex_free((flex_ptr)&leaf_name);
    return;
  }

  // replace or new?
  newpin.data.filename_offset=indirected_add_fromflex((flex_ptr)&canon_name,1+strlen(canon_name));
  if (newpin.data.filename_offset==INDIRECT_FREEI) {
    // failed
    flex_free((flex_ptr)&canon_name);
    flex_free((flex_ptr)&leaf_name);
    return;
  }
  
  if ((index=tinydir_get_empty())==-1) {
    // create a new pin
    newpin.icontype=pin_type_tinydir | (strncmp(canon_name,"Pinboard:",9)==0?pin_type_localfile:pin_type_fsfile);
    
   if ((index=pin_add(&newpin))==-1) {
     // failed
     indirected_remove(newpin.data.filename_offset);
     flex_free((flex_ptr)&canon_name);
     flex_free((flex_ptr)&leaf_name);
     return;
   }

   if (pos<0) {
     e=toolbox_create_object(0,"tinydir",&pinlist->info[index].obj);
     if (e) {
       wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
     }
   } else {
     e=tinydir_create_object("","",&pinlist->info[index].obj,pos,1)?NULL:(_kernel_oserror *)1;
   }
   
   if (e) {
     flex_free((flex_ptr)&canon_name);
     flex_free((flex_ptr)&leaf_name);
     pin_remove(state,index);
     return;
   }
   
  }
  
  // update this one
  pinlist->info[index].icontype=pin_type_tinydir | (strncmp(canon_name,"Pinboard:",9)==0?pin_type_localfile|pin_type_recheck:pin_type_fsfile|pin_type_recheck);
  pinlist->info[index].data.filename_offset=newpin.data.filename_offset;
 
  // truncate the text if need be
  if (truncate(&leaf_name,&truncated)) {
    iconbar_set_text(0,pinlist->info[index].obj,truncated);
    flex_free((flex_ptr)&truncated);
  } else {
    iconbar_set_text(0,pinlist->info[index].obj,leaf_name);
  }
  
  iconbar_set_sprite(0,pinlist->info[index].obj,"file_xxx");

  flex_free((flex_ptr)&canon_name);
  flex_free((flex_ptr)&leaf_name);

  // set help text accordingly
  iconbar_set_help_message(0,pinlist->info[index].obj,msgtrans_lookup("IcF"));

  toolbox_show_object(0,pinlist->info[index].obj,0,NULL,0,0);
  iconbar_get_icon_handle(0,pinlist->info[index].obj,&pinlist->info[index].iconbar_icon);
  
  tinydir_set_button_type(pinlist->info[index].iconbar_icon); 

}

void tinydir_add(_backdrop_state *state,char *path,int pos)
{
  // if pos>=0 then it refers to an icon for positioning the new one (unless there's an empty)
  _kernel_oserror *e;
  pin newpin;
  char *canon_name=NULL,*leaf_name=NULL,*truncated;
  int index;
  int filetype,type;
  
  memset(&newpin,0,sizeof(pin));
  newpin.icondata.buffer=INDIRECT_FREEI;
  newpin.data.pin.alias=INDIRECT_FREEI;
  newpin.icondata.sprite=INDIRECT_FREEI;
  newpin.data.filename_offset=INDIRECT_FREEI;

  if (path==NULL) {
    // add an empty icon?
    if (tinydir_present()) return;
    
    // add an empty one
    if ((e=toolbox_create_object(0,"tinydir",&newpin.obj))!=NULL) {
      wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
      return;
    }
    e=iconbar_set_text(0,newpin.obj,"");
    e=iconbar_set_sprite(0,newpin.obj,"directory");

    iconbar_set_help_message(0,newpin.obj,msgtrans_lookup("IcT"));
    
    // add a pin for this one
    newpin.icontype=pin_type_tinydir;
    if ((index=pin_add(&newpin))==-1) {
      toolbox_delete_object(0,newpin.obj);  
    } else {
      toolbox_show_object(0,newpin.obj,0,NULL,0,0);
      
      iconbar_get_icon_handle(0,pinlist->info[index].obj,&pinlist->info[index].iconbar_icon);
    }
    return;
  }
  
  // sort the filename
  if (!fs_canon(path,&canon_name)) return; // failed

  if (!fs_leafname_from_path(&leaf_name,canon_name)) {
    flex_free((flex_ptr)&canon_name);
    return;
  }
  
  // already present?
  if (pin_find_filename(canon_name,1)!=-1) {
    flex_free((flex_ptr)&canon_name);
    flex_free((flex_ptr)&leaf_name);
    return;
  }

  _swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(6),23,canon_name,&type,&filetype);
  if (type==0) {
    flex_free((flex_ptr)&canon_name);
    flex_free((flex_ptr)&leaf_name);
    msgtrans_report_error("TinyNF");
    return;
  }

  pin_set_filetype(&newpin,filetype);

  // replace or new?
  newpin.data.filename_offset=indirected_add_fromflex((flex_ptr)&canon_name,1+strlen(canon_name));
  if (newpin.data.filename_offset==INDIRECT_FREEI) {
    // failed
    flex_free((flex_ptr)&canon_name);
    flex_free((flex_ptr)&leaf_name);
    return;
  }
  
  if ((index=tinydir_get_empty())==-1) {
    // create a new pin
    newpin.icontype=pin_type_tinydir | (strncmp(canon_name,"Pinboard:",9)==0?pin_type_localfile:pin_type_fsfile);
    
    if ((index=pin_add(&newpin))==-1) {
      // failed
      indirected_remove(newpin.data.filename_offset);
      flex_free((flex_ptr)&canon_name);
      flex_free((flex_ptr)&leaf_name);
      return;
    }

    if (pos<0) {
      e=toolbox_create_object(0,"tinydir",&pinlist->info[index].obj);
      if (e) {
        wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
      }
      
    } else {
      e=tinydir_create_object("","",&pinlist->info[index].obj,pos,1)?NULL:(_kernel_oserror *)1;
    }
    
    if (e) {
      flex_free((flex_ptr)&canon_name);
      flex_free((flex_ptr)&leaf_name);
      pin_remove(state,index);
      return;
    }
   
  }
  
  // update this one
  pinlist->info[index].icontype=pin_type_tinydir | (strncmp(canon_name,"Pinboard:",9)==0?pin_type_localfile:pin_type_fsfile);
  pin_set_filetype(&pinlist->info[index],filetype);

  pinlist->info[index].data.filename_offset=newpin.data.filename_offset;
 
  // truncate the text if need be
  if (truncate(&leaf_name,&truncated)) {
    iconbar_set_text(0,pinlist->info[index].obj,truncated);
    flex_free((flex_ptr)&truncated);
  } else {
    iconbar_set_text(0,pinlist->info[index].obj,leaf_name);
  }
  
  tinydir_set_sprite(pinlist->info[index].obj,filetype,leaf_name);
 
  flex_free((flex_ptr)&canon_name);
  flex_free((flex_ptr)&leaf_name);

  // set help text accordingly
  switch (filetype) {
  case 0x1000:
    iconbar_set_help_message(0,pinlist->info[index].obj,msgtrans_lookup("IcD"));
    break;

  case 0x2000:
    iconbar_set_help_message(0,pinlist->info[index].obj,msgtrans_lookup("IcA"));
    break;

  default:
    iconbar_set_help_message(0,pinlist->info[index].obj,msgtrans_lookup("IcF"));
    break;
  }

  toolbox_show_object(0,pinlist->info[index].obj,0,NULL,0,0);
  iconbar_get_icon_handle(0,pinlist->info[index].obj,&pinlist->info[index].iconbar_icon);
  
  tinydir_set_button_type(pinlist->info[index].iconbar_icon);  
}
