#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include "kernel.h"
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>
#include <sys/stat.h>
#include <errno.h>

#include "windows.h"

typedef struct
{
  HANDLE fileHandle;
  char name[1024];
} FileLookup;

typedef struct
{
  int index;
  HANDLE table[FOPEN_MAX];
} FileHandleTable;

static FileHandleTable fileHandleTable = { 0};

static FileLookup fileLookup;
static int fileLookupCount = 0;
static int fileLookupSize = 0;

#define dprintf(a)
/*printf a*/

static void win32fake_closefiles(void)
{
  int i=0;
  for (i=0; i<FOPEN_MAX; i++)
  {
    if (fileHandleTable.table[i]!=-1)
    {
      dprintf(("closing file %d\n",fileHandleTable.table[i]));
      CloseHandle(fileHandleTable.table[i]);
      fileHandleTable.table[i]=-1;
    }
  }
}

static void win32fake_registerFileHandle(HANDLE f)
{
  int i=0;

  dprintf(("win32fake_registerFileHandle\n"));
  for (i=0; i<FOPEN_MAX; i++)
  {
    if (fileHandleTable.table[i]==-1)
    {
      dprintf(("Registering file h %d @ %d\n",f,i));
      fileHandleTable.table[i]=f;
      break;
    }
  }

}

void win32fake_restoreDisplay(void)
{
  fflush(stdin);
  putc(0x06, stdout);
  fflush(stdout);
}

void win32fake_finalise(void)
{
  win32fake_restoreDisplay();
  win32fake_closefiles();
}

void win32fake_finaliseSig(int number)
{
  win32fake_finalise();
  /* now die - make sure the user's told we've died */
  raise(SIGTERM);
}

void win32fake_init(void)
{
  int i=0;

  for (i=0; i<FOPEN_MAX; i++)
  {
    fileHandleTable.table[i]=-1; /* Invalid RISC OS file handle */
  }
  atexit(win32fake_finalise);
  /* on abnormal termination (eg Escape) atexit()s aren't called - we really
   * want our files to be closed + display restored, so...
   */
  signal(SIGINT,win32fake_finaliseSig);
}

char *strrchrq(char *str, int c)
{
  /* version of strrchr that ignores blocks between quotes */
  int i=0,l=0,quote=0;
  l=strlen(str);
  for (i=0; i<l; i++)
  {
    if ((str[l-i-1] == c) && !quote )
      return &(str[l-i-1]);

    if (str[l-i-1] == '\"')
      quote = !quote;
  }

  return NULL;
}

void MessageBox(void *ptr,char *message,char *windowTitle,int flags)
{
  fprintf(stderr,"%s:%s\n",windowTitle,message);
}

/*void fatalbox(char *message)
{
     xf(stderr,"%s\n",message);
     exit(4);
} */

int RegCreateKey(int section, char *keyName, HKEY *key)
{
  char buf[256];

  sprintf(buf,"Set %s %s\0",keyName,key);
  fprintf(stderr,buf);
  /*system(buf);*/

  return ERROR_SUCCESS;
}

void SetConsoleMode(HANDLE fh, int m)
{
  FILE *f;

  f = stdout;
  fflush(stdin);
  if (m)
    putc(21,(FILE *) f); /* disable char output */
  else
    putc(6,(FILE *) f); /* enable char output */
  fflush(stdout);
}

DWORD GetFileAttributes(char *file)
{
  _kernel_swi_regs r;
  _kernel_oserror *err;

  r.r[0] = 5; /* OS_File 5 */
  r.r[1] = (int) file;

  err=_kernel_swi(0x8 /* OS_File */, &r, &r);
  if (err)
    fprintf(stderr,"OS_File error no %d = %s\n",err->errnum,err->errmess);

  if (err || (r.r[0]==0))
    return -1;

  /* make image files look like ordinary files */
  if (r.r[0] == 3)
    r.r[0] = 1;

  dprintf(("GotFileAttributes  %d\n",r.r[0]));

  return r.r[0];
}

HANDLE CreateFile(char *name, int rw, int sharingDunno, void *dunno1,
                  int overwrite, int dunno2, int dunno3)
{
  int options,i=0;
  HANDLE fileHandle=0;

  switch (rw)
  {
    case GENERIC_READ:
      options = 0x40;
      break;
    case GENERIC_WRITE:
      options = 0x80;
      break;
    default:
      printf("CreateFile: rw erk!");
      exit(1);
  }

  switch (overwrite)
  {
    case OPEN_EXISTING:
      break;
    case CREATE_ALWAYS:
      break;
    default:
      printf("CreateFile: overwrite erk!");
      exit(1);
  }

  fileHandle = (HANDLE) _kernel_osfind(options,name);

  fileLookup.fileHandle = fileHandle;
  strcpy(fileLookup.name,name);

  win32fake_registerFileHandle(fileHandle);

  return fileHandle;
}

int WriteFile(HANDLE fileHandle, char *string, int stringLength, DWORD *lengthPtr, void *dunno)
{
  _kernel_osgbpb_block osg;

  if (fileHandle == STD_OUTPUT_HANDLE)
  {
    (*lengthPtr=fprintf(stdout,string));
    fflush(stdout);
    return;
  }
  if (fileHandle == STD_INPUT_HANDLE)
    return;

  osg.dataptr = string;
  osg.nbytes = stringLength;
  osg.fileptr = 0;
  osg.buf_len = stringLength;

  _kernel_osgbpb(2,fileHandle,&osg);

  *lengthPtr = stringLength - osg.nbytes;

  return *lengthPtr;
}

int ReadFile(HANDLE fileHandle, char *string, int stringLength, DWORD *lengthPtr, void *dunno)
{
  _kernel_osgbpb_block osg;
  _kernel_oserror *err;

  if (fileHandle == STD_INPUT_HANDLE)
  {
    fflush(stdin);
    fgets(string,stringLength,stdin);
    *lengthPtr = strlen(string)+1;
    return;
  }
  if (fileHandle == STD_OUTPUT_HANDLE)
    return;

  osg.dataptr = string;
  osg.nbytes = stringLength;
  osg.fileptr = 0;
  osg.buf_len = stringLength;

  _kernel_osgbpb(4,fileHandle,&osg);

  *lengthPtr = stringLength - osg.nbytes;

  return *lengthPtr;
}



static int HandleToFile(HANDLE f, char *fname)
{
  _kernel_swi_regs r;
  _kernel_oserror *err;

  if (fileLookup.fileHandle == f)
  {
    strcpy(fname,fileLookup.name);
    return 0;
  }

  if (f>(HANDLE) 0x7fff)
  {
    /* kludge! f is a filename, not a handle */
    strcpy(fname,(char *) f);
    return 0;
  }

  r.r[0] = 7; /* OS_Args 7 - read pathname of file handle */
  r.r[1] = (int) f;
  r.r[2] = (int) fname;
  r.r[3] = 1024;

  dprintf(("file %d\n",f));

  err = _kernel_swi(0x9 /* OS_Args */, &r, &r);
  if (err)
    fprintf(stderr,"HandleToFile: %d, %s of file %d\n",err->errnum,err->errmess,f);
  return (int) err;
}

#ifdef RISCOS_TIME
void GetFileTime(HANDLE file, void *dunno, FILETIME *accessTime, FILETIME *modifiedTime)
{
  char fname[1024];
  _kernel_osfile_block osf;
  int ret=0;

  if (HandleToFile(file,fname))
    return;

  ret=_kernel_osfile(5,fname,&osf);

  if (ret<0 || ((osf.load>>20)!=0xfff))
  {
    osf.exec = 0; /* no time set */
    osf.load = 0;
    dprintf(("Couldn't get time for %s\n",fname));
  }

  accessTime->time[0] = modifiedTime->time[0] = osf.exec;
  accessTime->time[1] = modifiedTime->time[1] = osf.load & 0xff;
  dprintf(("accessTime[0] = %x, accessTime[1] = %x\n",accessTime->time[0],accessTime->time[1]));
}
#else
void GetFileTime(HANDLE file, void *dunno, FILETIME *accessTime, FILETIME *modifiedTime)
{
  struct stat stb;
  char fname[1024];
  _kernel_osfile_block osf;
  int ret=0,fd=0;

  if (HandleToFile(file,fname))
    return;

  if ((fd = open(fname, O_RDONLY, 0)) < 0)
    goto syserr;
  if (fstat(fd, &stb)) {
    syserr:
      /*dprintf(("%s: %s", fname, strerror(errno)));*/
      return;
  }
  close(fd);

  accessTime->tv_sec = stb.st_atime;
  accessTime->tv_usec = stb.st_atime_usec;
  modifiedTime->tv_sec = stb.st_mtime;
  modifiedTime->tv_usec = stb.st_mtime_usec;


}

#endif

int GetFileType(HANDLE file, void *dunno)
{
  char fname[1024];
  _kernel_osfile_block osf;
  int type=-1;

  if (HandleToFile(file,fname))
    return;

  _kernel_osfile(5,fname,&osf);

  if ((osf.load>>20)!=0xfff)
  {
    type = -1;
    return type;
  }

  type = (osf.load & 0xfff00)>>8;

  return type;
}

unsigned long GetFileSize(HANDLE file, void *dunno)
{
  char fname[1024];
  _kernel_osfile_block osf;
  int i=0;

  if (HandleToFile(file,fname))
    return;

  dprintf(("osfiling %s\n",fname));

  i=_kernel_osfile(5,fname,&osf);

  dprintf(("GotFileSize %d\n",osf.start));

  return (unsigned long) osf.start;
}

#ifdef RISCOS_TIME
void time_riscos_to_posix(utcTime *utc, unsigned long *posix)
{
  unsigned int *b;
  unsigned int t1, t2, tc;
  _kernel_oserror *e;

  /* borrowed from UnixLib 3.7 */

  b = (unsigned int *) utc->time;
  t1 = (utc->time[0]);
  t2 = (utc->time[1] & 0xff);

  if ((t1 == 0) && (t2 == 0))
  {
    *posix = 0;
    return;
  }

  tc = 0x6e996a00U;
  if (t1 < tc)
    t2--;
  t1 -= tc;
  t2 -= 0x33;			/* 00:00:00 Jan. 1 1970 = 0x336e996a00 */

  t1 = (t1 / 100) + (t2 * 42949673U);	/* 0x100000000 / 100 = 42949672.96 */
  t1 -= (t2 / 25);		/* compensate for .04 error */

  *posix=t1;

}
void time_posix_to_riscos(utcTime *utc, unsigned long *posix)
{
  struct tm *gmt;
  int ordinals[0x1c/4];
  _kernel_swi_regs r;

  gmt=gmtime((time_t *) posix);

  ordinals[0] = 0; /* cs */
  ordinals[1] = gmt->tm_sec;
  ordinals[2] = gmt->tm_min;
  ordinals[3] = gmt->tm_hour;
  ordinals[4] = gmt->tm_mday;
  ordinals[5] = gmt->tm_mon;
  ordinals[6] = gmt->tm_year;

  r.r[0] = -1; /* current territory */
  r.r[1] = (int) utc->time;
  r.r[2] = (int) ordinals;

  _kernel_swi(0x43051 /* Territory_ConvertOrdinalsToTime */, &r, &r);
}

#else
void time_riscos_to_posix(utcTime *utc, unsigned long *posix)
{
  *posix = utc->tv_sec;
}

void time_posix_to_riscos(utcTime *utc, unsigned long *posix)
{
  utc->tv_sec = *posix;
  utc->tv_usec = 0;
}

#endif

int FindNextFile(HANDLE hand, WIN32_FIND_DATA *fdat)
{
  _kernel_swi_regs r;
  _kernel_oserror *err;

  if (fdat->context==-1)
    return 0; /* not OK */

  r.r[0] = 9; /* OS_GBPB 9 */
  r.r[1] = (int) fdat->dirName;
  r.r[2] = (int) fdat->buffer;
  r.r[3] = 1; /* objects to read */
  r.r[4] = fdat->context;
  r.r[5] = 1024; /* buffer length */
  r.r[6] = (int) "*";

  err=_kernel_swi(0xC /*OS_GBPB*/, &r, &r);
  if (err)
  {
    fprintf(stderr,"OS_GBPB error no %d = %s\n",err->errnum,err->errmess);
    return 0; /* not OK */
  }
  if (r.r[3]==0)
    return 0;

  dprintf(("OS_GBPB ret name %s\n",fdat->buffer));
  fdat->context = r.r[4];
  return 1; /* OK */
}

HANDLE FindFirstFile(char *dirName, WIN32_FIND_DATA *fdat)
{
  static char buffer[2048];
  int ok=0;
  DWORD att=0;

  dprintf(("fff %s\n",dirName));

  strcpy(fdat->dirName,dirName);
  fdat->buffer = buffer;
  fdat->context = 0;
  fdat->cFileName = fdat->buffer;

  att=GetFileAttributes(fdat->dirName);
  switch (att)
  {
    case -1:
      ok = 0; /* no such file */
      break;
    case 1:
    case 3:
      ok = 1; /* file */
      /*strcpy(fdat->buffer,strrchr(fdat->dirName,'.')+1);*/
      fdat->buffer[0] = '\0'; /* already got rest of path */
      fdat->context = -1; /* prevent FindNextFile from trying to OS_GBPB a file - TM 13/03/01 */
      break;
    case 2:
    default:
      ok = FindNextFile((HANDLE) NULL,fdat);
      break;
  }

  if (!ok)
    return (HANDLE) INVALID_HANDLE;
  else
    return (HANDLE) &fdat; /* kludge - don't actually care what handle is */
}

int CreateDirectory(char *name, void *dunno)
{
  int err;
  _kernel_osfile_block osf;
  osf.start = 0; /* number of entries */

  err = _kernel_osfile(8,name,&osf);

  if (err<0)
    return 0;
  else
    return 1;
}

#ifdef RISCOS_TIME
void SetFileTime(HANDLE f, void *dunno, utcTime *accessTime, utcTime *writeTime)
{
  char fname[1024];
  _kernel_osfile_block osf;

  if (HandleToFile(f,fname))
    return;

  osf.load = (writeTime->time[1] & 0xff) | 0xfff00000 | 0x000ffd00; /* sort filetyping out */

  _kernel_osfile(2,fname,&osf);

  osf.exec = (writeTime->time[0]);
  _kernel_osfile(3,fname,&osf);
}
#else

void SetFileTime(HANDLE f, void *dunno, utcTime *accessTime, utcTime *writeTime)
{
  char fname[1024];
  int ret=0,fd=0;
  struct utimbuf uTime;

  if (HandleToFile(f,fname))
    return;

  uTime.actime = accessTime->tv_sec;
  uTime.modtime = writeTime->tv_sec;
  dprintf(("atime %ld mtime %ld\n",uTime.actime,uTime.modtime));

  if (uTime.modtime == 0)
    utime(fname,NULL);
  else
    utime(fname,&uTime);
}
#endif

void SetFileType(HANDLE file, int type)
{
  char fname[1024];
  _kernel_osfile_block osf;

  if (HandleToFile(file,fname))
    return;

  osf.load = type;
  _kernel_osfile(18,fname,&osf);

}



int canonicalise(char *result, char *input)
{
  _kernel_swi_regs r;
  _kernel_oserror *err;

  r.r[0] = 37; /* canonicalise path */
  r.r[1] = (int) input;
  r.r[2] = (int) result;
  r.r[3] = 0;
  r.r[4] = 0;
  r.r[5] = 2048; /* buffer size */

  err=_kernel_swi(0x29 /* OS_FSControl */, &r,&r);

  if (err)
    return 0;
  else
    return 1;

}

int mimemaplookup(char *name)
{
  char *ext;
  char extWithDot[1024];
  _kernel_swi_regs r;
  _kernel_oserror *err;

  if ((ext=strrchr(name,'/'))==NULL)
    return 0xfff; /* data */

  sprintf(extWithDot,".%s",ext+1);
  dprintf(("extwd=%s\n",extWithDot));
  r.r[0] = 3; /*MMM_TYPE_DOT_EXTN*/
  r.r[1] = (int) (extWithDot);
  r.r[2] = 0; /*MMM_TYPE_RISCOS*/

  err=_kernel_swi(0x50b00 /* MimeMap_Translate */, &r, &r);
  if (err)
    return 0xfff;
  else
    return r.r[3];
}

