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

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

#include "tboxlibs/toolbox.h"
#include "tboxlibs/wimp.h"
#include "tboxlibs/wimplib.h"
#include "tboxlibs/event.h"
#include "tboxlibs/flex.h"
#include "tboxlibs/window.h"
                 
#include "base.h"
#include "indirected.h"
#include "pinboard.h"
#include "tinydirs.h"
#include "drag.h"
#include "fs.h"
#include "mystring.h"
#include "options.h"
#include "msgtrans.h"
#include "fileraction.h"
#include "paths.h"
#include "doublesize.h"

#include "debug.h"

extern pins *pinlist;
extern _backdrop_state backdrop_state;

void drag_post_action(source origin,int i)
{
  WimpSetIconStateBlock istate;
  istate.window_handle=-2;
  istate.EOR_word=0;
  istate.clear_word=WimpIcon_Selected;
  
  switch (origin) {
  case SOURCE_TINYDIR_SELDRAG:
    // deselect icon
    istate.icon_handle=pinlist->info[i].iconbar_icon;
    wimp_set_icon_state(&istate);
    break;
    
  case SOURCE_PINBOARD_SELDRAG:
    // deselect
    pinlist->info[i].icondata.flags&=~WimpIcon_Selected;
    pin_redraw(&backdrop_state,i);
    break;
    
  case SOURCE_TINYDIR_ADJDRAG:
    istate.icon_handle=pinlist->info[i].iconbar_icon;
    wimp_set_icon_state(&istate);
    // delete - fall through
    
  case SOURCE_PINBOARD_ADJDRAG:
    pin_delete(&backdrop_state,i);
    break;
  }      
  
}

int drag_is_selected(source origin,int icon)
{
  switch (origin) {
  case SOURCE_PINBOARD_SELDRAG:
  case SOURCE_PINBOARD_ADJDRAG:
    if (pin_is_tiny(&pinlist->info[icon])) return 0;
    return (pinlist->info[icon].icondata.flags & WimpIcon_Selected) && (!pin_is_locked(&pinlist->info[icon]));
    
  case SOURCE_TINYDIR_SELDRAG:
  case SOURCE_TINYDIR_ADJDRAG:
    {
      WimpGetIconStateBlock istate;
      if (!pin_is_tiny(&pinlist->info[icon])) return 0;
      
      istate.window_handle=-1;
      istate.icon_handle=pinlist->info[icon].iconbar_icon;
      wimp_get_icon_state(&istate);
      return (istate.icon.flags & WimpIcon_Selected)!=0;
    } 
    break;
  }
  
  return 0;
}

int filer_is_adjust=0;

void filer_get_version(void)
{
  // we need to know if we should use Filer_Run -shift or not
  // as there is a difference in command action between adjust and RO5

  // test for the option...
  _kernel_oserror *e=_swix(OS_CLI,_IN(0),"Filer_Run -Shift ."); // filename is not valid

  // we will get a Syntax error if it's Adjust
  if (e->errnum==220) filer_is_adjust=1;
}

void filer_run(int icon)
{
  const char *insert=getenv("Pinboard$Path");
  int detail_len=strlen(indirected(pinlist->info[icon].data.filename_offset,char *));
  char *exec,*path;
  _kernel_oserror *e;

  if (!flex_alloc((flex_ptr)&exec,21+detail_len+strlen(insert)+1)) {
    msgtrans_report_error("NoMem");
    return;
  }
  
  path=indirected(pinlist->info[icon].data.filename_offset,char *);
  if (filer_is_adjust) {
    if (strncmp(path,"Pinboard:",9)==0) {
      sprintf(exec,"Filer_Run %s%s",insert,path+9);
    } else {
      sprintf(exec,"Filer_Run %s",path);
    } 
  } else {
    if (strncmp(path,"Pinboard:",9)==0) {
      sprintf(exec,"Filer_Run %s %s%s",akbd_pollsh()?"-Shift":"-NoShift",insert,path+9);
    } else {
      sprintf(exec,"Filer_Run %s %s",akbd_pollsh()?"-Shift":"-NoShift",path);
    } 
  }
  e=_swix(OS_CLI,_IN(0),exec);
  if (e) {
    wimp_report_error(e,1,msgtrans_lookup("_TaskName"));
  }
  flex_free((flex_ptr)&exec);
}

static void dataload_adjust_path(WimpMessage *msg)
{
  // tweak pinboard: path
  const char *insert=getenv("Pinboard$Path");

  if (strlen(msg->data.data_load.leaf_name)>211-strlen(insert)) return; // not enough space in message block

  memmove(msg->data.data_load.leaf_name+strlen(insert),msg->data.data_load.leaf_name+9,strlen(msg->data.data_load.leaf_name+9));
  memcpy(msg->data.data_load.leaf_name,insert,strlen(insert));
}

static void filer_send_dataloads(_backdrop_state *state,WimpGetPointerInfoBlock *mouse,source drag_origin)
{
  // send message_dataload to the target, one for each icon
  int i;
  WimpMessage dataload;
  int filetype;
  char *filename;
  
  memset(&dataload,0,sizeof(WimpMessage));
  
  dataload.hdr.action_code=Wimp_MDataLoad;  
  dataload.data.data_load.destination_window=mouse->window_handle;
  dataload.data.data_load.destination_icon=mouse->icon_handle;
  dataload.data.data_load.destination_x=mouse->x;
  dataload.data.data_load.destination_y=mouse->y;
  
  for (i=0;i<pinlist->count;i++) {
    if (drag_is_selected(drag_origin,i)) {
      filename=indirected(pinlist->info[i].data.filename_offset,char *);
      _swix(OS_File,_INR(0,1)|_OUT(4)|_OUT(6),23,filename,&dataload.data.data_load.estimated_size,&filetype);
      
      dataload.data.data_load.file_type=filetype;
      if (strlen(filename)>211) {
        strncpy(dataload.data.data_load.leaf_name,filename,211);
      } else {
        strcpy(dataload.data.data_load.leaf_name,filename);
        if (strncmp(filename,"Pinboard:",9)==0) dataload_adjust_path(&dataload);
      }
      dataload.hdr.size=((44+strlen(dataload.data.data_load.leaf_name)+1)+3) &~3;
      wimp_send_message(17,&dataload,mouse->window_handle,mouse->icon_handle,0);
      
      drag_post_action(drag_origin,i);
    }
  }
  
  // tidy up...
  if (drag_origin==SOURCE_TINYDIR_ADJDRAG || drag_origin==SOURCE_PINBOARD_ADJDRAG) {
    pin_remove_empties(state);
  }
}

static void drag_run_selection(_backdrop_state *state)
{
  // do a filer_run of the various icons
  int i;
  
  for (i=0;i<pinlist->count;i++) {
    if (drag_is_selected(state->drag_origin,i)) {
      filer_run(i);
      drag_post_action(state->drag_origin,i);      
    }
  }
  
  // tidy up...
  if (state->drag_origin==SOURCE_TINYDIR_ADJDRAG || state->drag_origin==SOURCE_PINBOARD_ADJDRAG) {
    pin_remove_empties(state);  
  }
}

static int filer_datasaveack(WimpMessage *message,void *handle);

static int filer_datasave_bounced(int event_code,WimpPollBlock *poll,IdBlock *id_block,void *handle)
{
  IGNORE(id_block);
  
  if (event_code!=Wimp_EUserMessageAcknowledge) return 0;
  if (poll->user_message_acknowledge.hdr.action_code!=Wimp_MDataSave) return 0;
  event_deregister_message_handler(Wimp_MDataSaveAck,filer_datasaveack,handle);
  event_deregister_wimp_handler(-1,Wimp_EUserMessageAcknowledge,filer_datasave_bounced,handle);
  return 1; 
}

static int filer_datasaveack(WimpMessage *message,void *handle)
{
  _backdrop_state *state=(_backdrop_state *)handle;
  char *destpath;
  
  event_deregister_message_handler(Wimp_MDataSaveAck,filer_datasaveack,handle);
  event_deregister_wimp_handler(-1,Wimp_EUserMessageAcknowledge,filer_datasave_bounced,handle);
  
  // we now have a destination folder for the files

  if (fs_pathprefix_from_path(&destpath,message->data.data_save.leaf_name)) {
    fileraction_run(state->drag_origin,akbd_pollsh()?FILERACTION_MOVE:FILERACTION_COPY,FILERACTION_RUN_DO_POSTDRAG,destpath,state);
    flex_free((flex_ptr)&destpath);
  } else {
    msgtrans_report_error("NoMem");
  }
  
  return 1;
}

static void filer_send_datasave(WimpGetPointerInfoBlock *mouse,_backdrop_state *state)
{
  WimpMessage save;
  memset(&save,0,sizeof(save));
  save.hdr.size=48;
  save.hdr.action_code=Wimp_MDataSave;
  save.data.data_save.destination_window=mouse->window_handle;
  save.data.data_save.destination_icon=mouse->icon_handle;
  save.data.data_save.destination_x=mouse->x;
  save.data.data_save.destination_y=mouse->y;
  save.data.data_save.estimated_size=1;
  //save.data.data_save.file_type=0;
  save.data.data_save.leaf_name[0]='A'; // same as original Pinboard sends
  event_register_message_handler(Wimp_MDataSaveAck,filer_datasaveack,state);
  event_register_wimp_handler(-1,Wimp_EUserMessageAcknowledge,filer_datasave_bounced,state); // catch bounces
  wimp_send_message(18,&save,mouse->window_handle,0,0);
}

static int drag_end(int event_code,WimpPollBlock *event,IdBlock *id,void *handle)
{
  WimpGetPointerInfoBlock mouse;
  int th;
  unsigned int flags;
  int pinboard_window;
  _backdrop_state *state=(_backdrop_state *)handle;
  IGNORE(event_code);
  IGNORE(id);
  
  event_deregister_wimp_handler(-1,Wimp_EUserDrag,drag_end,handle);
  
  // decide what to do with the icons.  Depends on the drag destination, really.
  flags=options_get();
  if (OPTIONS_DRAGASPRITE(flags)) {
    _swix(DragASprite_Stop,0);  
  }

  wimp_get_pointer_info(&mouse);
  
  if (mouse.window_handle==-2) {
    // is it an empty space?
    if (mouse.icon_handle==-1) {
      // yes, so try to run the entities
      drag_run_selection(state);
    }
    
    // is it an icon of ours?
    if (tinydir_find_icon(mouse.icon_handle)!=-1) {
      // yes - need to move the icons
      if (state->drag_origin==SOURCE_PINBOARD_SELDRAG || state->drag_origin==SOURCE_PINBOARD_ADJDRAG) {
        tinydir_xfer_selection_from_pinboard(&mouse,state);
      } else {
        tinydir_move_icons(&mouse);
      }
    } else {
      // no - send a batch of dataloads to the target icon
      filer_send_dataloads(state,&mouse,state->drag_origin);
    }
    return 1;
  }
  
  window_get_wimp_handle(0,state->obj,&pinboard_window);  
  if (mouse.window_handle==pinboard_window) {
    if (state->drag_origin==SOURCE_PINBOARD_SELDRAG || state->drag_origin==SOURCE_PINBOARD_ADJDRAG) {
      pinboard_move_selected_icons(state,mouse.x-state->drag_x,mouse.y-state->drag_y);
    } else {
      // relocate these icons to the pinboard
      pinboard_move_icons_from_tinydir(state,mouse.x,mouse.y);
    }
    return 1;
  }
  
  // get window's owner
  wimp_send_message(0,event,mouse.window_handle,0,&th);
  if (th==filer_taskhandle) {
    // copy/move time...
    filer_send_datasave(&mouse,state);
  } else {
    // otherwise... send dataloads again.  
    filer_send_dataloads(state,&mouse,state->drag_origin);
  }
  
  return 1;
}

void drag_box_init(int window_handle,BBox *icon_bbox,char *sprite,_backdrop_state *state)
{
  WimpGetWindowStateBlock win;
  WimpGetPointerInfoBlock mouse;
  WimpDragBox drag;
  unsigned int flags;
  int sprite_x,sprite_y,mode,scale=1;

  // get window scroll offset etc
  win.window_handle=window_handle;
  wimp_get_window_state(&win);
  wimp_get_pointer_info(&mouse);

  state->drag_x=mouse.x;
  state->drag_y=mouse.y;  


  if (state->iconsize==ICONSIZE_DOUBLE && window_handle!=-2) scale=2;

  // make drag box fit the sprite properly
  drag.dragging_box.ymax=icon_bbox->ymax;
  _swix(Wimp_SpriteOp,_IN(0)|_IN(2)|_OUT(3)|_OUT(4)|_OUT(6),40,sprite,&sprite_x,&sprite_y,&mode);
  sprite_x<<=bbc_modevar(mode,bbc_XEigFactor);
  sprite_y<<=bbc_modevar(mode,bbc_YEigFactor);
  drag.dragging_box.ymin=drag.dragging_box.ymax-sprite_y*scale;
  drag.dragging_box.xmin=(icon_bbox->xmax-icon_bbox->xmin)/2-sprite_x*scale/2+icon_bbox->xmin;
  drag.dragging_box.xmax=drag.dragging_box.xmin+sprite_x*scale;

  // convert work area to screen area coords
  if (window_handle==-2) {
    WimpGetWindowStateBlock win;
    win.window_handle=window_handle;
    wimp_get_window_state(&win);
    drag.dragging_box.xmin=drag.dragging_box.xmin-win.xscroll+win.visible_area.xmin;
    drag.dragging_box.xmax=drag.dragging_box.xmax-win.xscroll+win.visible_area.xmin;
    drag.dragging_box.ymin=drag.dragging_box.ymin-win.yscroll+win.visible_area.ymax;
    drag.dragging_box.ymax=drag.dragging_box.ymax-win.yscroll+win.visible_area.ymax;
  }

  // sort out parent box
  drag.parent_box.xmin=drag.dragging_box.xmin-mouse.x;
  drag.parent_box.xmax=(bbc_modevar(-1,11)<<bbc_modevar(-1,4))+drag.dragging_box.xmax-mouse.x;
  drag.parent_box.ymin=drag.dragging_box.ymin-mouse.y;
  drag.parent_box.ymax=(bbc_modevar(-1,12)<<bbc_modevar(-1,5))+drag.dragging_box.ymax-mouse.y;

  drag.wimp_window=window_handle;
  drag.drag_type=Wimp_DragBox_DragFixedDash;

  flags=options_get();
  if (OPTIONS_DRAGASPRITE(flags)) {
    switch (state->iconsize) {
    case ICONSIZE_DOUBLE:
      {
        int *spritearea=NULL,free_area=0;
        int *drag_area=(int *)1;
        if (sprite_doublesize(state,sprite,&spritearea,&free_area)) {
          drag_area=spritearea;
        }
        _swix(DragASprite_Start,_INR(0,4),1 | (1<<2) | (2<<4) | (1<<6) | (1<<7),drag_area,sprite,&drag.dragging_box,&drag.parent_box);
        if (free_area && spritearea) flex_free((flex_ptr)&spritearea);
      }
      break;

    case ICONSIZE_NORMAL:
      _swix(DragASprite_Start,_INR(0,4),1 | (1<<2) | (2<<4) | (1<<6) | (1<<7),1,sprite,&drag.dragging_box,&drag.parent_box);
      break;

    case ICONSIZE_SMALL:
      {
        char spritename[13];
        int hsflag;
        sprite_halfsize(sprite,spritename,&hsflag);
        _swix(DragASprite_Start,_INR(0,4),1 | (1<<2) | (2<<4) | (1<<6) | (1<<7),1,spritename,&drag.dragging_box,&drag.parent_box);
      }
      break;
        
    }
  } else {
    wimp_drag_box(&drag);
  }
  
  if (window_handle==-2) {
    if (mouse.button_state==Wimp_MouseButtonSelect || state->disable_adjust) {
      state->drag_origin=SOURCE_TINYDIR_SELDRAG;
    } else {
      state->drag_origin=SOURCE_TINYDIR_ADJDRAG;
    }
  } else {
    if (mouse.button_state==Wimp_MouseButtonSelect || state->disable_adjust) {
      state->drag_origin=SOURCE_PINBOARD_SELDRAG;
    } else {
      state->drag_origin=SOURCE_PINBOARD_ADJDRAG;
    }
  } 
  event_register_wimp_handler(-1,Wimp_EUserDrag,drag_end,state);
}
