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

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

#include "buffer.h"
#include "pinboard.h"
#include "base.h"
#include "module.h"
#include "pinfilter.h"
#include "upcall.h"
#include "veneers.h"
#include "mystring.h"
#include "paths.h"

/* This function is autogenerated by ResGen, and returns a pointer to a
 * ResourceFS resource file data block */
extern void *Resources(void);

#define TASK 0x4b534154

#define ICONSPRITES "Pinboard_IconSprites %0"
#define OBEYRUN "%Set Obey$File %0|M%Obey %*0"

static int messagetrans[5]={0,0,0,0,0};

static _kernel_oserror *path_length(char *path,int *length)
{
  _kernel_oserror *e;
  
  if (strncmp(path,"Pinboard:",9)==0) {
    *length=strlen(path);
    return NULL;
  }
  
  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,path,0,0,0,0,length);
  
  *length=1-(*length);
  
  return e;
}

static _kernel_oserror *path_expand(char *source,char *dest,int dest_size)
{
  if (strncmp(source,"Pinboard:",9)==0) {
    strcpy(dest,source);
    return NULL;
  } else {
    return _swix(OS_FSControl,_INR(0,5),37,source,dest,0,0,dest_size);
  }
}

void *module_pw;

_kernel_oserror *module_init(const char *cmd_tail, int podule_base, void *pw)
{
  _kernel_oserror *e;
  int size;
  char *filename,*path;
  
  IGNORE(cmd_tail);
  IGNORE(podule_base);
  IGNORE(pw);
  
  debug_initialise(Module_Title, "", "");
  debug_set_device(REPORTER_OUTPUT);
//  debug_set_device(FILE_OUTPUT);
//  debug_set_unbuffered_files(1);
  debugf("\n");

  module_pw = pw;

  if ((e=buffer_init())!=NULL) return e;
  
#ifndef ROM_MODULE
  if ((e=_swix(ResourceFS_RegisterFiles, _IN(0), Resources()))!=NULL) return e;
#endif
  
  if (messagetrans[4]==0) {
    e=_swix(MessageTrans_OpenFile,_INR(0,2), messagetrans, Module_MessagesFile ,0);
    if (e) {
      _swix(ResourceFS_DeregisterFiles,_IN(0), Resources());
      return e;
    }
    messagetrans[4]=1;
  }

  // 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) {
      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);
        }
      }
    }
  }

  if (e==NULL) {
    e=_swix(OS_Claim,_INR(0,2),29,upcall_entry,pw);
  }

  _swix(OS_SetVarVal,_INR(0,4),"Alias$IconSprites",ICONSPRITES,sizeof(ICONSPRITES),0,0); // not a disaster if this doesn't go
  _swix(OS_SetVarVal,_INR(0,4),"Alias$@RunType_FEB",OBEYRUN,sizeof(OBEYRUN),0,0); // not a disaster if this doesn't go
  
  if (e) {
    _swix(MessageTrans_CloseFile,_IN(0),messagetrans);
    messagetrans[4]=0;
    _swix(ResourceFS_DeregisterFiles,_IN(0), Resources());      
    return e;
  }
  
  taskhandle=0;
  
  return NULL;
}

_kernel_oserror *module_final(int fatal, int podule, void *pw)
{
  IGNORE(fatal);
  IGNORE(podule);
  IGNORE(pw);
  
  if (taskhandle!=0 && taskhandle!=-1) {
    // need to terminate the task abruptly
    // and remove the filter
    pinfilter_stop(taskhandle);
    _swix(Wimp_CloseDown,_INR(0,1),taskhandle,TASK);
  }
  taskhandle=0;

  if (flex_da!=0) {
    _swix(OS_DynamicArea,_INR(0,1),1,flex_da);
    flex_da=0;
  }
  
  buffer_final();
  
  _swix(OS_SetVarVal,_INR(0,4),"Alias$IconSprites",NULL,-1,0,0);
  _swix(OS_SetVarVal,_INR(0,4),"Alias$@RunType_FEB",NULL,-1,0,0);

  _swix(OS_Release,_INR(0,2),29,upcall_entry,pw);
  
  if (messagetrans[4]) {
    _swix(MessageTrans_CloseFile,_IN(0),messagetrans);
    messagetrans[4]=0;
  }
#ifndef ROM_MODULE
  _swix(ResourceFS_DeregisterFiles,_IN(0),Resources());
#endif
  
  return NULL;
}

void module_service(int service_number, _kernel_swi_regs *r, void *pw)
{
  IGNORE(pw);
  
  // resfs start/stop
  switch (service_number) {
  case 0x11: // Service_Memory
    if (r->r[2]==(int)Image__RO_Base) r->r[1]=0;
    break;
    
  case 0x27: // PostReset
    taskhandle=0;
    break;
    
  case 0x49: // StartWimp
    if (taskhandle==0) { 
      r->r[0]=(int)"Desktop_Pinboard";
      r->r[1]=0;
      taskhandle=-1;
    }
    break;
    
  case 0x4a: //StartedWimp
    if (taskhandle==-1) taskhandle=0;
    break;
    
  case 0x4b: //StartFiler
    filer_taskhandle=r->r[0];
    break;
    
#ifndef ROM_MODULE    
  case 0x60: // ResourceFSStarting
    ((struct {void (*fn)(void *, void *, void *, void *);}*)&r->r[2])->fn(Resources(), 0, 0, (void *)r->r[3]);
    break;
#endif    
  }
}


static const char *readargs[]={NULL, // desktop_pinboard
                               "path", // addtinydir
                               "path", // removetinydir
                               "Grid=G/S,IconiseToIconBar=ITIB/S", //pinboard
                               "path/A,X/E/A,Y/E/A,Locked=L/S", //pin
                               "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
                               "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,OverwriteRename=OR/S,OverwriteConfirm=OC/S,DisableAdjust=DA/S", //pinboardoptions
                               "path/A",
                               "path/A,X/E/A,Y/E/A,Locked=L/S", //xpin
                               "path/A", // xaddtinydir
                               "Font,File,Relocate/S", // pinboardstickies
                               "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", // pinboardwatermark

};

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

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

_kernel_oserror *module_commands(const char *arg_string, int argc, int cmd_no, void *pw)
{
  _kernel_oserror *e=NULL;
  int optbuffer[64];
  
  buffer_entry *newentry;
  char *sourcepath;
  int pathlength;

  IGNORE(argc);
  IGNORE(pw);
  
  if (readargs[cmd_no]) {
    e=_swix(OS_ReadArgs,_INR(0,3),readargs[cmd_no],arg_string,optbuffer,256);
    if (e) return e;
  }
  
  switch (cmd_no) {
  case CMD_Desktop_Pinboard: //Desktop_Pinboard
    switch (taskhandle) {
    case -1:
      _swix(Wimp_SlotSize,_INR(0,1),32*1024,-1);
      _swix(OS_Module,_INR(0,2),2,"Pinboard",0);
      break;
      
    case 0:
      e=(_kernel_oserror *)"\000\000\000\000Desktop";
      return _swix(MessageTrans_ErrorLookup,_INR(0,3),e,messagetrans,0,0);

    default:
      e=(_kernel_oserror *)"\000\000\000\000TaskRun";
      return _swix(MessageTrans_ErrorLookup,_INR(0,3),e,messagetrans,0,0);
    }
    break;
    
  case CMD_AddTinyDir: //AddTinyDir
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.add_tinydir.path,pathlength);
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1,&newentry);
      if (e) return e;
      newentry->data.add_tinydir.path[0]=0;
    }
    newentry->hdr.type=BUFFER_ADD_TINYDIR;
    pollword|=1;
    break;
    
  case CMD_RemoveTinyDir: //RemoveTinyDir
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.remove_tinydir.path,pathlength);
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1,&newentry);
      if (e) return e;
      newentry->data.remove_tinydir.path[0]=0;
    }
    newentry->hdr.type=BUFFER_REMOVE_TINYDIR;
    pollword|=1;
    break;
    
  case CMD_Pinboard: // Pinboard
    e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_clear_pinboard),&newentry);
    if (e) return e;
    newentry->hdr.type=BUFFER_CLEAR_PINBOARD;
    newentry->data.clear_pinboard.snap_to_grid=optbuffer[0]!=0;
    newentry->data.clear_pinboard.iconise_to_iconbar=optbuffer[1]!=0;
    pollword|=1;
    break;
    
  case CMD_Pin: //pin
    sourcepath=(char *)optbuffer[0];
    e=path_length(sourcepath,&pathlength);
    if (e) return e;
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_add_pin),&newentry);
    if (e) return e;
    path_expand(sourcepath,newentry->data.add_pin.path,pathlength);
    
    newentry->hdr.type=BUFFER_ADD_PIN;
    newentry->data.add_pin.x=get_int((char *)optbuffer[1]);
    newentry->data.add_pin.y=get_int((char *)optbuffer[2]);
    newentry->data.add_pin.locked=optbuffer[3]!=0;
    pollword|=1;
    break;
    
  case CMD_BackDrop: //backdrop
    sourcepath=(char *)optbuffer[0];
    if (sourcepath) {
      e=path_length(sourcepath,&pathlength);
      if (e) return e;
      e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_set_backdrop),&newentry);
      if (e) return e;
      path_expand(sourcepath,newentry->data.set_backdrop.path,pathlength);
    } else {
      e=buffer_push(sizeof(_buffer_hdr)+1+sizeof(_buffer_set_backdrop),&newentry);
      if (e) return e;
      newentry->data.set_backdrop.path[0]=0;
    }
    
    newentry->hdr.type=BUFFER_SET_BACKDROP;
    newentry->data.set_backdrop.style=BD_CENTRED;
    //if (optbuffer[1]) newentry->data.set_backdrop.style=BD_CENTRED;
    if (optbuffer[2]) newentry->data.set_backdrop.style=BD_TILED;
    if (optbuffer[3]) newentry->data.set_backdrop.style=BD_SCALED;
    if (optbuffer[11]) newentry->data.set_backdrop.style=BD_FULLSCREEN;
    newentry->data.set_backdrop.remove=optbuffer[5]!=0;
    newentry->data.set_backdrop.colour=optbuffer[6]?get_int((char *)optbuffer[6]):-1;
    newentry->data.set_backdrop.text_colour=optbuffer[7]?get_int((char *)optbuffer[7]):-1;

    // text render type
    newentry->data.set_backdrop.render_type=TEXT_TYPE_DEFAULT;
    if (optbuffer[9]) newentry->data.set_backdrop.render_type=TEXT_TYPE_BLEND;
    if (optbuffer[8]) {
      newentry->data.set_backdrop.render_type=TEXT_TYPE_OUTLINE;
      newentry->data.set_backdrop.outline_colour=get_int((char *)optbuffer[8]);
    }

    // cache jpeg option
    newentry->data.set_backdrop.cacheJPEG=optbuffer[10]!=0;
    
    pollword|=1;
    break;
    
  case CMD_PinboardOptions: //pinboardoptions
    {
      e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_options),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_SET_OPTIONS;
      newentry->data.set_options.snap_to_grid=optbuffer[0]!=0;
      newentry->data.set_options.iconise_corner=ICONIZE_TO_BUTTON;
      if (optbuffer[1]) newentry->data.set_options.iconise_corner=ICONIZE_TO_ICONBAR;
      if (optbuffer[2]) newentry->data.set_options.iconise_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[3]) newentry->data.set_options.iconise_corner=ICONIZE_TO_BOTTOMLEFT;
      if (optbuffer[4]) newentry->data.set_options.iconise_corner=ICONIZE_TO_TOPRIGHT;
      if (optbuffer[5]) newentry->data.set_options.iconise_corner=ICONIZE_TO_BOTTOMRIGHT;
      newentry->data.set_options.iconise_stack=optbuffer[6]?ICONIZE_VERTICAL:ICONIZE_HORIZONTAL;
      newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[7]) newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPLEFT;
      if (optbuffer[8]) newentry->data.set_options.tidy_corner=ICONIZE_TO_BOTTOMLEFT;
      if (optbuffer[9]) newentry->data.set_options.tidy_corner=ICONIZE_TO_TOPRIGHT;
      if (optbuffer[10]) newentry->data.set_options.tidy_corner=ICONIZE_TO_BOTTOMRIGHT;
      
      newentry->data.set_options.tidy_stack=optbuffer[11]?ICONIZE_VERTICAL:ICONIZE_HORIZONTAL;
      newentry->data.set_options.autosave=AUTOSAVE_NONE;
      if (optbuffer[12]) newentry->data.set_options.autosave=AUTOSAVE_AUTO;
      if (optbuffer[13]) newentry->data.set_options.autosave=AUTOSAVE_CONFIRM;
      newentry->data.set_options.iconsize=ICONSIZE_NORMAL;
      if (optbuffer[14]) newentry->data.set_options.iconsize=ICONSIZE_SMALL;
      if (optbuffer[15]) newentry->data.set_options.iconsize=ICONSIZE_DOUBLE;

      newentry->data.set_options.overwrite=OVERWRITE_ALWAYS;
      if (optbuffer[16]) newentry->data.set_options.overwrite=OVERWRITE_RENAME;
      if (optbuffer[17]) newentry->data.set_options.overwrite=OVERWRITE_CONFIRM;

      newentry->data.set_options.disable_adjust=optbuffer[18]!=0;
      pollword|=1;
    }
    break;

  case CMD_Pinboard_IconSprites: // pinboard_iconsprites
    {
//      int len;
      char cmd[16+strlen((char *)optbuffer[0])];

      e=buffer_push(sizeof(_buffer_hdr)+1+strlen((char *)optbuffer[0]),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_ICONSPRITES;
      strcpy(newentry->data.iconsprites.filename,(char *)optbuffer[0]);
//      newentry->data.iconsprites.filename[len]=0;
      pollword|=1;
      
      // now pass it on to the Wimp properly
      sprintf(cmd,"%%IconSprites %s",(char *)optbuffer[0]);
      e=_swix(OS_CLI,_IN(0),cmd);
    }
    break;

  case CMD_XPin: // xpin
    sourcepath=(char *)optbuffer[0];
    pathlength=1+strlen(sourcepath);
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1+sizeof(_buffer_add_pin),&newentry);
    if (e) return e; // this one is fatal
    strcpy(newentry->data.add_pin.path,sourcepath);
    
    newentry->hdr.type=BUFFER_ADD_XPIN;
    newentry->data.add_pin.x=get_int((char *)optbuffer[1]);
    newentry->data.add_pin.y=get_int((char *)optbuffer[2]);
    newentry->data.add_pin.locked=optbuffer[3]!=0;
    pollword|=1;
    break;

  case CMD_XAddTinyDir: // XAddTinyDir
    sourcepath=(char *)optbuffer[0];
    pathlength=1+strlen(sourcepath);
    e=buffer_push(sizeof(_buffer_hdr)+pathlength+1,&newentry);
    if (e) return e; // this one is fatal
    strcpy(newentry->data.add_tinydir.path,sourcepath);
    newentry->hdr.type=BUFFER_ADD_XTINYDIR;
    pollword|=1;
    break;

  case CMD_PinboardStickies:
    {
      char *font=(char *)optbuffer[0];
      sourcepath=(char *)optbuffer[1];
      if (font==NULL) font="";
      if (sourcepath==NULL) sourcepath="";
      e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_sticky)+1+strlen(font)+1+strlen(sourcepath),&newentry);
      if (e) return e;
      newentry->hdr.type=BUFFER_SET_STICKY;
      strcpy(newentry->data.set_sticky.data,font);
      strcpy(newentry->data.set_sticky.data+1+strlen(font),sourcepath);
      newentry->data.set_sticky.relocate=optbuffer[2]!=0;
      pollword|=1;
      break;
    }

  case CMD_PinboardWatermark:
    sourcepath=(char *)optbuffer[11];
    if (sourcepath==NULL) sourcepath="";
    e=buffer_push(sizeof(_buffer_hdr)+sizeof(_buffer_set_watermark)+1+strlen(sourcepath),&newentry);
    if (e) return e;
    newentry->hdr.type=BUFFER_SET_WATERMARK;
    if (optbuffer[0]) newentry->data.watermark.enable=1; else newentry->data.watermark.enable=0;
    newentry->data.watermark.location=WATERMARK_BOTTOMRIGHT;
    if (optbuffer[2]) newentry->data.watermark.location=WATERMARK_TOPLEFT;
    if (optbuffer[3]) newentry->data.watermark.location=WATERMARK_TOPCENTRE;
    if (optbuffer[4]) newentry->data.watermark.location=WATERMARK_TOPRIGHT;
    if (optbuffer[5]) newentry->data.watermark.location=WATERMARK_LEFT;
    if (optbuffer[6]) newentry->data.watermark.location=WATERMARK_CENTRE;
    if (optbuffer[7]) newentry->data.watermark.location=WATERMARK_RIGHT;
    if (optbuffer[8]) newentry->data.watermark.location=WATERMARK_BOTTOMLEFT;
    if (optbuffer[9]) newentry->data.watermark.location=WATERMARK_BOTTOMCENTRE;
    if (optbuffer[10]) newentry->data.watermark.location=WATERMARK_BOTTOMRIGHT;
    strcpy(newentry->data.watermark.filename,sourcepath);
    if (optbuffer[12]) newentry->data.watermark.x_offset=get_int((char *)optbuffer[12]); else newentry->data.watermark.x_offset=0;
    if (optbuffer[13]) newentry->data.watermark.y_offset=get_int((char *)optbuffer[12]); else newentry->data.watermark.y_offset=0;
    pollword|=1;

    break;
  }

  return e;
}
