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

#include "swis.h"

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

#include "pinboard.h"
#include "filermenu.h"
#include "gadgetlist.h"
#include "indirected.h"
#include "fs.h"
#include "msgtrans.h"
#include "mystring.h"
#include "drag.h"
#include "options.h"
#include "iconize.h"
#include "fileraction.h"
#include "debug.h"

ObjectId settype_showing=0;

int fileinfo_show(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  int index;
  _backdrop_state *state=(_backdrop_state *)handle;
  int found=0;
  IGNORE(event_code);
  IGNORE(event);

  // find currently selected file

  index=pin_get_selected(pin_type_fsfile | pin_type_localfile);
  if (index==-1) {
    displayfield_set_value(0,id->self_id,WFileInfo_Filename,"");
  } else {
    char *leaf_name;
    if (!fs_leafname_from_path(&leaf_name,indirected(pinlist->info[index].data.filename_offset,char *))) {
      displayfield_set_value(0,id->self_id,WFileInfo_Filename,"Selection");
    } else {
      char *path_name;

      displayfield_set_value(0,id->self_id,WFileInfo_Filename,leaf_name);
      flex_free((flex_ptr)&leaf_name);

      if (!fs_pathprefix_from_path(&path_name,indirected(pinlist->info[index].data.filename_offset,char *))) {
        displayfield_set_value(0,id->self_id,WFileInfo_Path,"");
        gadget_set_help_message(0,id->self_id,WFileInfo_Path,indirected(pinlist->info[index].data.filename_offset,char *));
      } else {
        displayfield_set_value(0,id->self_id,WFileInfo_Path,path_name);
        gadget_set_help_message(0,id->self_id,WFileInfo_Path,path_name);
        flex_free((flex_ptr)&path_name);
      }
    }

    if (pin_needs_recheck(&pinlist->info[index])) {
      pin_recheck(state,index);
    }

    if (!pin_needs_recheck(&pinlist->info[index])) {
      int type,name,load,exec,length,attr,filetype;
      if (_swix(OS_File,_INR(0,1)|_OUTR(0,6),23,indirected(pinlist->info[index].data.filename_offset,char *),&type,&name,&load,&exec,&length,&attr,&filetype)==NULL) {
        if (type!=0) {
          char access[8]={0};
          char stamp[32];
          found=1;

          button_set_value(0,id->self_id,WFileInfo_Icon,indirected(pinlist->info[index].icondata.sprite,char *));

          // access string
          if (attr & (1<<2)) strcpy(access+strlen(access),"P");
          if (attr & (1<<3)) strcpy(access+strlen(access),"L");
          if (attr & (1<<1)) strcpy(access+strlen(access),"W");
          if (attr & (1<<0)) strcpy(access+strlen(access),"R");
          if (attr & (3<<4)) strcpy(access+strlen(access),"/");
          if (attr & (1<<5)) strcpy(access+strlen(access),"w");
          if (attr & (1<<4)) strcpy(access+strlen(access),"r");
          displayfield_set_value(0,id->self_id,WFileInfo_Access,access);

          switch (filetype) {
          case -1:
            displayfield_set_value(0,id->self_id,WFileInfo_Type,msgtrans_lookup("NoType"));
            break;

          case 0x1000:
            displayfield_set_value(0,id->self_id,WFileInfo_Type,msgtrans_lookup("Dir"));
            break;

          case 0x2000:
            displayfield_set_value(0,id->self_id,WFileInfo_Type,msgtrans_lookup("App"));
            break;

          default:
            {
              char ftype[16];
              _swix(OS_FSControl,_IN(0)|_IN(2)|_OUTR(2,3),18,filetype,(int *)ftype,(int *)(ftype+4));
              sprintf(ftype+8," (&%03x)",filetype);
              displayfield_set_value(0,id->self_id,WFileInfo_Type,ftype);
              break;
            }
          }

          if (filetype==-1) {
            sprintf(stamp,"%08x %08x",load,exec);
            gadget_set_help_message(0,id->self_id,WFileInfo_Stamp,msgtrans_lookup("FIload"));
          } else {
            int timeblock[2]={exec,load};
            _swix(OS_ConvertStandardDateAndTime,_INR(0,2),timeblock,stamp,32);            
            gadget_set_help_message(0,id->self_id,WFileInfo_Stamp,msgtrans_lookup("FIdate"));
          }
          displayfield_set_value(0,id->self_id,WFileInfo_Stamp,stamp);

          sprintf(stamp,"%d",length);
          displayfield_set_value(0,id->self_id,WFileInfo_Size,stamp);

        }
      }
    }
  }


  if (!found) {
    button_set_value(0,id->self_id,WFileInfo_Icon,"file_xxx");
    displayfield_set_value(0,id->self_id,WFileInfo_Type,"");
    displayfield_set_value(0,id->self_id,WFileInfo_Size,"");
    displayfield_set_value(0,id->self_id,WFileInfo_Access,"");
    displayfield_set_value(0,id->self_id,WFileInfo_Stamp,"");
  }

  return 1;
}

int rename_show(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  int index;
  char *leaf_name;
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);

  // find currently selected file

  index=pin_get_selected(pin_type_fsfile | pin_type_localfile);
  if (index==-1) {
    writablefield_set_value(0,id->self_id,WRename_Name,"");
    return 1;
  }

  // get the current leafname
  if (!fs_leafname_from_path(&leaf_name,indirected(pinlist->info[index].data.filename_offset,char *))) {
    writablefield_set_value(0,id->self_id,WRename_Name,"");
  } else {
    writablefield_set_value(0,id->self_id,WRename_Name,leaf_name);
    flex_free((flex_ptr)&leaf_name);
  }

  return 1;
}

int rename_action(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  int index=pin_get_selected(pin_type_fsfile | pin_type_localfile);
  IGNORE(handle);
  IGNORE(event_code);

  switch (id->self_component) {
    case WRename_OK:
      // rename file
      if (index!=-1) {
        char *dest_filename;
        int dest_leaf_length;
        char *source_filename=indirected(pinlist->info[index].data.filename_offset,char *);
        int i;

        // path length
        for (i=strlen(source_filename)-1;i>0 && *(source_filename+i)!=':' && *(source_filename+i)!='.';i--);
        if (*(source_filename+i)==':' || *(source_filename+i)=='.') i++;

        writablefield_get_value(0,id->self_id,WRename_Name,NULL,0,&dest_leaf_length);
        if (dest_leaf_length<2) {
          msgtrans_report_error("ENoName");
          return 1;
        }

        if (!flex_alloc((flex_ptr)&dest_filename,i+dest_leaf_length+1)) {
          msgtrans_report_error("NoMem");
        } else {
          strncpy(dest_filename,indirected(pinlist->info[index].data.filename_offset,char *),i);
          writablefield_get_value(0,id->self_id,WRename_Name,dest_filename+i,i+dest_leaf_length+1,NULL);
          _swix(OS_FSControl,_INR(0,2),25,indirected(pinlist->info[index].data.filename_offset,char *),dest_filename);
          flex_free((flex_ptr)&dest_filename);
        }
      }
      // fall through
    case WRename_Cancel:
      if ((event->hdr.flags & ActionButton_Selected_Adjust)==0) {
        toolbox_hide_object(0,menu_mainmenu);
      }
      break;

  }
  return 1;
}

int copyas_action(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  int index=pin_get_selected(pin_type_fsfile | pin_type_localfile);
  _backdrop_state *state=(_backdrop_state *)handle;

  switch (event_code) {
    case SaveAs_AboutToBeShown:
      if (index==-1) {
        toolbox_hide_object(0,id->self_id);
      } else {
        char *source=indirected(pinlist->info[index].data.filename_offset,char *);
        char *leaf_name;
        int filetype;
        _swix(OS_File,_INR(0,1)|_OUT(6),23,source,&filetype);
        saveas_set_file_type(0,id->self_id,filetype);
        if (!fs_leafname_from_path(&leaf_name,indirected(pinlist->info[index].data.filename_offset,char *))) {
          saveas_set_file_name(0,id->self_id,"");
        } else {
          saveas_set_file_name(0,id->self_id,leaf_name);
          flex_free((flex_ptr)&leaf_name);
        }

      }
      break;

    case SaveAs_SaveToFile:
      if (index!=-1) {
        SaveAsSaveToFileEvent *sa=(SaveAsSaveToFileEvent *)event;
        _kernel_oserror *e=NULL;

        // copy file to given destination
        // we don't do a filer_action if it's a non-safe destination
        if (strcasecmp(sa->filename,"<Wimp$Scrap>")!=0 && strncasecmp(sa->filename,"Pinboard:",9)!=0) {
//        if (sa->hdr.flags & SaveAs_DestinationSafe) { // always reporting nonsafe???

          saveas_file_save_completed(1,id->self_id,sa->filename);

          fileraction_run(SOURCE_PINBOARD_SELDRAG,FILERACTION_COPYLOCAL,0,sa->filename,state);

        } else {
          e=_swix(OS_FSControl,_INR(0,3),26,indirected(pinlist->info[index].data.filename_offset,char *),sa->filename,0);
          if (e) wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
          saveas_file_save_completed(e==NULL,id->self_id,sa->filename);
        }
      }
      break;

    case SaveAs_DialogueCompleted:
      toolbox_hide_object(0,menu_mainmenu);
      break;
    }

    return 1;
}

static int settype_menu_choice(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  char *buffer;
  int len;
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);

  menu_get_entry_text(0,id->self_id,id->self_component,NULL,0,&len);
  if (flex_alloc((flex_ptr)&buffer,1+len)) {
    char sprite[13];
    menu_get_entry_text(0,id->self_id,id->self_component,buffer,len+1,NULL);
    writablefield_set_value(0,id->parent_id,WSetType_Type,buffer+6);
    flex_free((flex_ptr)&buffer);
    sprintf(sprite,"file_%03x",id->self_component);
    if (wimp_sprite_exists(sprite)) {
      button_set_value(0,id->parent_id,WSetType_Icon,sprite);
    } else {
      button_set_value(0,id->parent_id,WSetType_Icon,"file_xxx");
    }
  } else {
    msgtrans_report_error("NoMem");
  }
  return 1;
}

static void settype_menu_add_handlers(ObjectId menu)
{
  event_register_toolbox_handler(menu,Menu_Selection,settype_menu_choice,NULL);
}

static void settype_menu_remove_handlers(ObjectId menu)
{
   event_deregister_toolbox_handler(menu,Menu_Selection,settype_menu_choice,NULL);
}

static void settype_generate_menu(ObjectId win,int current)
{
  _kernel_swi_regs r;
  // iterate existing filetypes and add to the menu
  char *buffer;
  int buflen,nameptr=0;
  int filetype;

  // empty menu first
  ObjectId menu;
  MenuTemplateEntry entry;

  popup_get_menu(0,win,WSetType_Popup,&menu);
  if (menu) {
    settype_menu_remove_handlers(menu);
    toolbox_delete_object(0,menu);
  }

  // make a new one
  if (toolbox_create_object(0,"FTMenu",&menu)) return;

  if (!flex_alloc((flex_ptr)&buffer,buflen=20)) return;

  entry.click_show=NULL;
  entry.submenu_show=NULL;
  entry.submenu_event=0;
  entry.click_event=0;
  entry.help_message="Click to choose this file type";
  entry.max_entry_help=1+strlen(entry.help_message);
 
  // iterate the available filetypes
  r.r[0]=(int)"File$Type_*";
  r.r[1]=0;
  r.r[2]=-1;
  r.r[3]=nameptr;
  r.r[4]=0;
  _kernel_swi(OS_ReadVarVal,&r,&r);
  while (r.r[2]!=0) {

    r.r[2]=~r.r[2];
    if (r.r[2]+8>buflen) {
      buflen=r.r[2]+8;
      if (!flex_extend((flex_ptr)&buffer,buflen)) {
        flex_free((flex_ptr)&buffer);
        settype_menu_remove_handlers(menu);
        toolbox_delete_object(0,menu);
        return;
      }
    }
    filetype=(int)strtol((char *)(r.r[3]+10),NULL,16);
    sprintf(buffer,"&%03x: ",filetype);
    nameptr=r.r[3];
    r.r[0]=r.r[3];
    r.r[1]=(int)(buffer+6);
    r.r[3]=0;
    r.r[4]=0;
    _kernel_swi(OS_ReadVarVal,&r,&r);
    *(buffer+6+r.r[2])=0;
    
    entry.flags=(current==filetype)?1:0;
    entry.component_id=filetype;
    entry.max_text=1+strlen(buffer);
    entry.text=buffer;

    menu_add_entry(0,menu,-2,(char *)&entry,NULL);
    r.r[0]=(int)"File$Type_*";
    r.r[1]=0;
    r.r[2]=-1;
    r.r[3]=nameptr;
    r.r[4]=0;
    _kernel_swi(OS_ReadVarVal,&r,&r);
  }

  flex_free((flex_ptr)&buffer);

  popup_set_menu(0,win,WSetType_Popup,menu);
  settype_menu_add_handlers(menu);
}

int settype_show(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  int index;
  _backdrop_state *state=(_backdrop_state *)handle;
  char buffer[9];
  _kernel_oserror *e;
  int filetype,i;
  IGNORE(event);
  IGNORE(event_code);

  if (pin_count_nodir(1,2,pin_type_localfile | pin_type_fsfile)==1) {
    index=pin_get_selected_nodir(pin_type_fsfile | pin_type_localfile);
    // find out details for current selection
    if (index==-1) {
      toolbox_hide_object(0,id->self_id);
      return 1;
    }

    if (pin_needs_recheck(&pinlist->info[index])) {
      pin_recheck(state,index);
    }
    if (pin_needs_recheck(&pinlist->info[index])) {
      toolbox_hide_object(0,id->self_id);
      return 1;
    }

    button_set_value(0,id->self_id,WSetType_Icon,indirected(pinlist->info[index].icondata.sprite,char *));

    // textual filetype
    if ((e=_swix(OS_File,_INR(0,1)|_OUT(6),23,indirected(pinlist->info[index].data.filename_offset,char *),&filetype))!=NULL) {
      wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
      toolbox_hide_object(0,id->self_id);
      return 1;   
    }
    
    _swix(OS_FSControl,_IN(0)|_IN(2)|_OUTR(2,3),18,filetype,(int *)buffer,(int *)(buffer+4));
    for (i=7;i>0 && buffer[i]==' ';i--);
    buffer[i+1]=0;

    writablefield_set_value(0,id->self_id,WSetType_Type,buffer);
  } else {
    button_set_value(0,id->self_id,WSetType_Icon,"file_xxx");
    writablefield_set_value(0,id->self_id,WSetType_Type,"");
  }
  settype_generate_menu(id->self_id,filetype);

  settype_showing=id->self_id;

  return 1;
}

int settype_hide(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  // destroy the popup menu
  ObjectId menu;
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);

  popup_get_menu(0,id->self_id,WSetType_Popup,&menu);
  toolbox_delete_object(0,menu);
  settype_menu_remove_handlers(menu);

  popup_set_menu(0,id->self_id,WSetType_Popup,0);

  settype_showing=0;

  return 1;
}

int settype_valuechanged(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  char buf[10];
  char curspr[13],newspr[13];
  int filetype;
  IGNORE(handle);
  IGNORE(event);
  IGNORE(event_code);

  if (id->self_component!=WSetType_Type) return 0;

  writablefield_get_value(0,id->self_id,WSetType_Type,buf,10,NULL);

  if (_swix(OS_FSControl,_INR(0,1)|_OUT(2),31,buf,&filetype)!=NULL) filetype=0;

  sprintf(newspr,"file_%03x",filetype);
  if (!wimp_sprite_exists(newspr)) strcpy(newspr,"file_xxx");

  button_get_value(0,id->self_id,WSetType_Icon,curspr,13,NULL);
  if (strcmp(curspr,newspr)!=0) button_set_value(0,id->self_id,WSetType_Icon,newspr);

  return 1;
}

int settype_action(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  _backdrop_state *state=(_backdrop_state *)handle;
  IGNORE(event_code);

  switch (id->self_component) {
  case WSetType_Set:
    // interpret the filetype if possible
    {
      char buf[10];
      _kernel_oserror *e;
      int filetype;

      writablefield_get_value(0,id->self_id,WSetType_Type,buf,10,NULL);

      if ((e=_swix(OS_FSControl,_INR(0,1)|_OUT(2),31,buf,&filetype))!=NULL) {
        wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
        return 1;
      }

      fileraction_run(SOURCE_PINBOARD_SELDRAG,FILERACTION_SETTYPE,FILERACTION_RUN_NO_DIRS,&filetype,state);
    }

    // fall through
  case WSetType_Cancel:
    if ((event->hdr.flags & ActionButton_Selected_Adjust)==0) {
      toolbox_hide_object(0,id->self_id);
    }
    break;
  }
  return 1;
}

int cdir_show(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  IGNORE(event_code);
  IGNORE(event);
  IGNORE(handle);

  writablefield_set_value(0,id->self_id,WCdir_Name,"");

  return 1;
}

static int cdir_do(_backdrop_state *state,ObjectId cdir_win)
{
  _kernel_oserror *e;
  char *name,*leaf;
  int len;
  WimpGetPointerInfoBlock mouse;

  writablefield_get_value(0,cdir_win,WCdir_Name,NULL,0,&len);
  if (len<2) {
    msgtrans_report_error("ENoName");
    return 0;
  }

  if (!flex_alloc((flex_ptr)&name,len+9)) {
    msgtrans_report_error("NoMem");
    return 0;
  }

  writablefield_get_value(0,cdir_win,WCdir_Name,name+9,len,NULL);
  memcpy(name,"Pinboard:",9); // no terminator!
    
  e=_swix(OS_File,_INR(0,1)|_IN(4),8,name,0);
  if (e) {
    flex_free((flex_ptr)&name);
    wimp_report_error(e,0,msgtrans_lookup("_TaskName"),NULL,NULL,NULL);
    return 0;
  }

  if (!flex_alloc((flex_ptr)&leaf,len)) {
    flex_free((flex_ptr)&name);
    msgtrans_report_error("NoMem");
    return 0;
  }   

  strcpy(leaf,name+9);

  // create a pin at the current mouse position
  wimp_get_pointer_info(&mouse);

  backdrop_add_pin(state,pin_type_localfile,mouse.x,mouse.y,PIN_ADD_CENTRED,0x1000,&leaf,&leaf,&name,0,0);

  flex_free((flex_ptr)&leaf);
  flex_free((flex_ptr)&name);

  return 1;
}

int cdir_action(int event_code,ToolboxEvent *event,IdBlock *id,void *handle)
{
  IGNORE(event_code);
  _backdrop_state *state=(_backdrop_state *)handle;

  switch (id->self_component) {
  case WCdir_OK:
    // make a new directory on the pinboard if possible
    if (!cdir_do(state,id->self_id)) return 1;

    // fall through

  case WCdir_Cancel:
    if ((event->hdr.flags & ActionButton_Selected_Adjust)==0) {
      toolbox_hide_object(0,id->self_id);
    }
    break;
  }
  return 1;
}

