
#include <PalmOS.h>
#include <VfsMgr.h>
#include <PalmOSGlue.h>
#include "pnobridge.h"
#include "host68k.h"
#include "endianutils.h"
#include "stdfile.h"
#include "StarterRsc.h"

#undef SF_LITTLEENDIAN

FILE *fopen ( const char *filename, char *mode ) {
//FILE *fopen ( char *filename, char *mode ) {
  UInt32 voliter = palm_vfs_iter();
  char path [ 256 ];
  Err error;
  FILE *myfile = MemPtrNew ( sizeof(FILE) );

#ifdef DEBUGFILE
  DEBUGS( __FUNCTION__);
#endif

  StrCopy ( myfile -> filename, filename );
  myfile -> tail = 0;
  myfile -> ref = 0;
  myfile -> size = 0;

  if ( StrChr ( mode, 'r' ) && StrChr ( mode, 'w' ) ) {
    myfile -> bufferp = 0; // no buffering on read+write files
    //DEBUGS("Internal eror: Game fopen() is confusing");
  } else if ( StrChr ( mode, 'w' ) ) {
    myfile -> bufferp = 2; // buffering single direction stream
  } else {
    myfile -> bufferp = 1; // buffering single direction stream
  }

  // looks like general purpose work, so don't screw with path
  path [ 0 ] = '\0';
  StrCat ( path, VFSPATH );
  StrCat ( path, filename );
  //StrPrintF ( path, "%s%s", VFSPATH, filename );

  //DEBUGS(path);

  error = VFSFileOpen ( voliter, path, vfsModeReadWrite, & (myfile->ref) );

#ifdef SF_LITTLEENDIAN
  myfile -> ref = ByteSwap32 ( myfile -> ref );
#endif

  if ( error != errNone ) {

    if ( StrChr ( mode, 'w' ) ) {
      /* couldn't open it, but try and create it.. */
      error = VFSFileCreate ( voliter, path );

      if ( error != errNone ) {
	return ( NULL );
      }

      error = VFSFileOpen ( voliter, path, vfsModeReadWrite, & (myfile->ref) );

      if ( error != errNone ) {
	MemPtrFree ( myfile );
	return ( NULL );
      }

    } else {
      MemPtrFree ( myfile );
      return ( NULL );
    }

  }

  VFSFileSize ( myfile -> ref, & (myfile -> size) );

#ifdef SF_LITTLEENDIAN
  //myfile -> size = ByteSwap32 ( myfile -> size );
#endif

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( myfile );
}

Int8 fclose ( FILE *file ) {

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
#endif

  if ( file -> bufferp == 2 && file -> tail ) {
    // write buffer, so we need to empty the buffer into the stream
    UInt32 result;
    VFSFileWrite ( file -> ref, file -> tail, file -> buffer, &result );
  }

  VFSFileClose ( file -> ref );

  MemPtrFree ( file );

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( 0 );
}

UInt32 fwrite ( const void *_buf, UInt32 size, UInt32 num, FILE *file ) {
//UInt32 fwrite ( char *buf, UInt32 size, UInt32 num, FILE *file ) {
  char *buf = (char*)_buf;
  UInt32 result;

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
  DEBUGU32(size*num);
#endif

  // for fwrite, lets assume they're calling it since they want to write
  // a block of stuff, not just a single character; so first lets flush
  // any buffer so far, and then write out their buffer
  // (later could shove their buf into our file buffer..)
#if 0
  Err error;

  if ( file -> tail ) {
    VFSFileWrite ( file -> ref, file -> tail, file -> buffer, &result );
    file -> tail = 0;
  }

  if ( size*num ) {
    error = VFSFileWrite ( file -> ref, size*num, buf, &result );
  }
#else
  result = size*num;
  while ( result ) {
    fputc ( *buf++, file );
    result--;
  }
#endif

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  //return ( result );
  return ( num );
}

int fputc ( UInt8 c, FILE *file ) {

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
#endif

  if ( file == stdout ) {
    //screen_push ( c );
  } else {

    //draw_progress_bar ( 100 * file -> tail / 255 );

    // buffer large enough to hold another char?
    if ( file -> tail == TAILMAX ) {
      UInt32 result;
      VFSFileWrite ( file -> ref, file -> tail, file -> buffer, &result );
      file -> tail = 0;
      //fwrite ( &c, 1, 1, file );
    }

    // insert into buffer
    file -> buffer [ file -> tail++ ] = c;

  }

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( c );
}

UInt8 file_exists ( const char *filename ) {
  FILE *f = fopen ( filename, "r" );

  if ( f ) {
    fclose ( f );
    return ( 1 );
  }

  return ( 0 );
}

UInt32 fread ( void *_buf, UInt32 size, UInt32 num, FILE *file ) {
//UInt32 fread ( char *buf, UInt32 size, UInt32 num, FILE *file ) {
  char *buf = (char*)_buf;
  UInt32 result;
  Err error;
  UInt32 ext = size * num;

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
  DEBUGS(file->filename);
  DEBUGU32(num*size);
#endif

  // do we have any data in the buffer already? if so, lets first use
  // that and then pick up any extra needed
  if ( ext < file -> tail ) {
    // buffer already has all the data
    //DEBUGS("less");

    // pick up buffered bytes
    MemMove ( buf, file -> buffer, ext );

    // shuffle buffer up into place
    MemMove ( file -> buffer, file -> buffer + ext, file -> tail - ext );

    // update tail to new size
    file -> tail -= ext;

  } else {
    // buffer doesn't have enough data
    //DEBUGS("more");

    // move buffer into buf
    MemMove ( buf, file -> buffer, file -> tail ); // might be zero

    // go to end of buff
    buf += file -> tail;

    // num remaining
    ext -= file -> tail;

    // empty buffer
    file -> tail = 0; // now empty

    // pick up remaining
    error = VFSFileRead ( file -> ref, ext, buf, &result );

  }

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  //return ( result );
  return ( size * num );
}

UInt32 ftell ( FILE *file ) {
  UInt32 result;
  Err error;

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
#endif

  error = VFSFileTell ( file -> ref, &result );

  if ( file -> bufferp == 1 ) {
    result -= file -> tail;
  } else if ( file -> bufferp == 2 ) {
    result += file -> tail;
  }

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( result );
}

int fgetc ( FILE *file ) {
  UInt8 c;

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
#endif

  // buffer empty? If so, populate it..
  if ( file -> tail == 0 ) {
    //fread ( &c, 1, 1, file );
    UInt32 size = 255;

    if ( file -> size - ftell ( file ) < 255 ) {
      size = file -> size - ftell ( file );
    }

    fread ( file -> buffer, size, 1, file );

    file -> tail += size;
  }

  // already in the buffer?
  if ( file -> tail ) {

    // disrupts loading display
    //draw_progress_bar ( 100 * file -> tail / 255 );

    c = file -> buffer [ 0 ];
    MemMove ( file -> buffer, file -> buffer + 1, file -> tail - 1 );
    file -> tail -= 1;

  }

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( c ); // -1 on error?
}

Int8 fseek ( FILE *file, Int32 rel, UInt16 source ) {
  Err error;

#ifdef DEBUGFILE
  DEBUGS(__FUNCTION__);
#endif

  file -> tail = 0; // should only fseeking on a read stream so we can dump it
  error = VFSFileSeek ( file -> ref, source, rel );

#ifdef DEBUGFILE
  DEBUGS( "Exit");
#endif

  return ( 0 ); // success
}
