#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"

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,12+strlen(path))) {
    msgtrans_report_error("NoMem");
    return;
  }
  
  sprintf(app_path,"%s.!Boot",path);
  _swix(OS_File,_INR(0,1)|_OUT(0),5,app_path,&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,"%s.!Sprites",path);
  if (_swix(Wimp_Extend,_INR(0,3)|_OUT(3),13,app_path,NULL,0,&space)==NULL) {
    char *sprites;
    if (flex_alloc((flex_ptr)&sprites,space+1)) {
      _swix(Wimp_Extend,_INR(0,3),13,app_path,sprites,space+1);
      _swix(Wimp_SpriteOp,_IN(0)|_IN(2),11,sprites);
      flex_free((flex_ptr)&sprites);
    }

  }
  flex_free((flex_ptr)&app_path);
}
