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

#include "kernel.h"
#include "swis.h"
#include "tboxlibs/flex.h"
#include "tboxlibs/wimplib.h"

#include "mystring.h"
#include "fs.h"
#include "msgtrans.h"

#include "paths.h"

#include "pinboard.h"

#include "debug.h"

int fs_make_boot_relative(char *source,char **dest)
{
  char *bootpath;
  int prefixlen,i,dotcount=0;
  char *sourceoffset;

  if (strncmp(source,"Pinboard:",9)==0) {
    *dest=source;
    return 0;
  }
  
  // try to make a path relative to Boot:
  if ((bootpath=getenv("Boot$Path"))==NULL) {
    *dest=source;
    return 0;
  }

  // check if we're on the same disk or not
  // find root...
  for (prefixlen=0;*(bootpath+prefixlen)!='$' && *(bootpath+prefixlen)>0;prefixlen++);

  if (*(bootpath+prefixlen)==0) {
    *dest=source;
    return 0;
  }

  if (*(bootpath+prefixlen+1)!='.') {
    *dest=source;
    return 0;
  }
  prefixlen++;

  if (strncasecmp(source,bootpath,prefixlen)!=0) {
    *dest=source;
    return 0;
  }

  // same disk.  Adjust.
  if (strncasecmp(source,bootpath,strlen(bootpath))==0) {
    // is inside the boot path itself
    if (!flex_alloc((flex_ptr)dest,1+strlen(source)-(strlen(bootpath)-5))) {
      *dest=source;
      return 0;
    }
    sprintf(*dest,"Boot:%s",source+strlen(bootpath));
    return 1;
  }

  // is outside the boot path, work out how much to adjust to get to root
  if ((sourceoffset=strchr(source,'$'))==NULL) {
    *dest=source;
    return 0;
  }

  for (i=prefixlen+1;*(bootpath+i);i++) {
    if (*(bootpath+i)=='.') dotcount++;
  }

  if (!flex_alloc((flex_ptr)dest,strlen(sourceoffset+1)+5+2*dotcount+1)) {
    *dest=source;
    return 0;
  }
  
  strcpy(*dest,"Boot:");
  for (i=0;i<dotcount;i++) strcpy(*dest+5+2*i,"^.");
  strcpy(*dest+5+2*dotcount-1,sourceoffset+1);

  return 1;
}


void fs_filename_set_pinboardpath(char **output)
{
  char *pinboardpath;
  
  if ((pinboardpath=getenv("Pinboard$Path"))!=NULL) {
    if (strncasecmp(*output,pinboardpath,strlen(pinboardpath))==0) {
      memcpy(*output,"Pinboard:",9);
      memcpy(*output+9,*output+strlen(pinboardpath),strlen(*output)-strlen(pinboardpath)+1);
    }
  }
}

int fs_leafname_from_path(char **leaf_name,char *path)
{
  char *ptr;
  
  if ((ptr=strrchr(path,'.'))!=NULL) {
    int offset=ptr-path;
    if (!flex_alloc((flex_ptr)leaf_name,1+strlen(ptr+1))) {
      msgtrans_report_error("NoMem");
      return 0; // failed
    }
    strcpy(*leaf_name,path+offset+1);
  } else {
    if ((ptr=strrchr(path,':'))!=NULL) {
      int offset=ptr-path;
      if (!flex_alloc((flex_ptr)leaf_name,1+strlen(ptr+1))) {
        msgtrans_report_error("NoMem");
        return 0;
      }
      strcpy(*leaf_name,path+offset+1);
    } else {
      if (!flex_alloc((flex_ptr)&leaf_name,1+strlen(path))) {
        msgtrans_report_error("NoMem");
        return 0;
      }
      strcpy(*leaf_name,path);
    }
  }
  return 1;
}

int fs_pathprefix_from_path(char **prefix,char *path)
{
  // return path, ie 'adfs::foo.$.bar.bob'-> 'adfs::foo.$.bar' and 'Resources:$' -> 'Resources:'
  int i;
  char *pinboardpath;
  char *dot;
  
  if (strncmp(path,"Pinboard:",9)!=0) {
    for (i=strlen(path)-1;path[i]!='.' && path[i]!=':' && i>0;i--);
    
    if (path[i]==':') i++;
    if (!flex_alloc((flex_ptr)prefix,i+1)) {
      msgtrans_report_error("NoMem");
      return 0;
    }
    strncpy(*prefix,path,i);
    *(*prefix+i)=0;
    return 1;
  }

  // we need to expand the path
  if ((pinboardpath=getenv("Pinboard$Path"))==NULL) return 0;

  if (!flex_alloc((flex_ptr)prefix,strlen(pinboardpath)+strlen(path)-9+1)) return 0;

  strcpy(*prefix,pinboardpath);
  strcpy(*prefix+strlen(*prefix),path+9);
  dot=strrchr(*prefix,'.');
  if (dot) *dot=0;

  return 1;
}

int fs_canon_nomoan(char *filename, char **canon_name)
{
  int size;
  _kernel_oserror *e;

  // canonicalise a filename
  if (strncasecmp(filename,"Pinboard:",9)==0) {
    if (!flex_alloc((flex_ptr)canon_name,1+strlen(filename))) {
      msgtrans_report_error("NoMem");
      return 0;
    }
    strcpy(*canon_name,filename);
    return 1;
  }
  
  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,filename,0,0,0,0,&size);
  if (e) {
    size=1+strlen(filename);
    if (!flex_alloc((flex_ptr)canon_name,size)) {
      msgtrans_report_error("NoMem");
      return 0;
    }
    strcpy(*canon_name,filename);
    fs_filename_set_pinboardpath(canon_name);
    return 1;
  }
  size=1-size;
  
  if (!flex_alloc((flex_ptr)canon_name,size)) {
    msgtrans_report_error("NoMem");
    return 0;
  }

  e=_swix(OS_FSControl,_INR(0,5),37,filename,*canon_name,0,0,size);
  if (e) {
    if (!flex_extend((flex_ptr)canon_name,1+strlen(filename))) {
      msgtrans_report_error("NoMem");
      return 0;
    }
    strcpy(*canon_name,filename);
  }

  // see if it needs to be converted to a pinboard: path now...
  fs_filename_set_pinboardpath(canon_name);
  
  return 1;
}

int fs_canon(char *filename,char **canon_name)
{
  int size;
  _kernel_oserror *e;

  // canonicalise a filename 
  if (strncmp(filename,"Pinboard:",9)==0) {
    if (!flex_alloc((flex_ptr)canon_name,1+strlen(filename))) {
      msgtrans_report_error("NoMem");
      return 0;
    }

    strcpy(*canon_name,filename);
    
    return 1;
  }
  
  _swix(OS_FSControl,_INR(0,5)|_OUT(5),37,filename,0,0,0,0,&size);
  size=1-size;
  
  if (!flex_alloc((flex_ptr)canon_name,size)) {
    msgtrans_report_error("NoMem");
    return 0;
  }

  e=_swix(OS_FSControl,_INR(0,5),37,filename,*canon_name,0,0,size);
  if (e) {
    wimp_report_error(e,1,msgtrans_lookup("_TaskName"));
    flex_free((flex_ptr)canon_name);
    return 0;
  }

  // see if it needs to be converted to a pinboard: path now...
  fs_filename_set_pinboardpath(canon_name);
  
  return 1;
}

int upcall_make_filename(char *fs,char *path,char **output)
{
  // convert the upcall name to a full one, and reduce to Pinboard: if necessary
  // note # for sp field

  if (!flex_alloc((flex_ptr)output,strlen(fs)+strlen(path)+2)) {
    msgtrans_report_error("NoMem");
    return -1;
  }
  if (fs[0]) {
    sprintf(*output,"%s%s%s",fs,path[0]=='#'?"":":",path);
  } else {
    strcpy(*output,path);
  }

  fs_filename_set_pinboardpath(output);
  
  return 1;
}

void fs_boot_application(char *path)
{
  // boot application.  When added from a *command, we may not have
  // booted it, so sprites etc missing
  // any !Boot file?
  char *app_path;
  int handle,type; //,space;
  
  if (!flex_alloc((flex_ptr)&app_path,48+strlen(path))) {
    msgtrans_report_error("NoMem");
    return;
  }
  
  sprintf(app_path,"%%Run %s.!Boot",path);

  _swix(OS_File,_INR(0,1)|_OUT(0),5,app_path+5,&type);
  if (type!=0) {
    // run this one
    wimp_start_task(app_path,&handle);
    flex_free((flex_ptr)&app_path);
    return;
  }

  // no !Boot, so ensure sprites are installed
  sprintf(app_path,"%%Pinboard_IconSprites %s.!Sprites",path);
  _swix(OS_CLI,_IN(0),app_path); // let Wimp decide which Sprites file is best
  // don't report an error from this one

  flex_free((flex_ptr)&app_path);
}

_kernel_oserror *fs_get_types(char *file,int *objtype,int *filetype)
{
  unsigned int load,type;
  _kernel_oserror *e;

  e=_swix(OS_File,_INR(0,1)|_OUT(0)|_OUT(2),5,file,&type,&load);
  if (e) {
    if (objtype) *objtype=0;
    if (filetype) *filetype=-3;
    debugf("fs_get_types: %s error %s\n",file,e->errmess);
    return e;
  }

  switch (type) {
    case 0:
      if (objtype) *objtype=type;
      if (filetype) *filetype=-3;
      break; // not found

    case 1:
      // filetype?
      if (objtype) *objtype=type;

      if (filetype) {
        if ((load & 0xfff00000)==0xfff00000) {
          *filetype=(load>>8) & 0xfff;
        } else {
          *filetype=-1; // ie load/exec type file
        }
      }

      break;

    case 2:
    {
      if (objtype) *objtype=type;

      // directory or application
      if (filetype) {

        char *dot;
        if ((dot=strrchr(file,'.'))==NULL) {
          if ((dot=strchr(file,':'))==NULL) {
            dot=file;
          } else {
            dot++;
          }
        } else {
          dot++;
        }
        if (*dot=='!') *filetype=0x2000; else *filetype=0x1000;
      }
      break;
    }

    case 3: // images are treated as files, unless they are application directories
    {
      char *dot;
      if ((dot=strrchr(file,'.'))==NULL) {
        if ((dot=strchr(file,':'))==NULL) {
          dot=file;
        } else {
          dot++;
        }
      } else {
        dot++;
      }
      if (*dot=='!') {
        // is app dir
        if (filetype) *filetype=0x2000;
        if (objtype) *objtype=2;
        return NULL;
      }

      if (objtype) *objtype=1;
      if (filetype) *filetype=(load>>8) & 0xfff; // must have a type else it wouldn't be an image file
      break;
    }
  }

  debugf("fs_get_types: %s gives object type %d, filetype %x\n",file,objtype?*objtype:-1, filetype?*filetype:-4);

  return NULL;
}
