/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#ifndef __I4FILE_HPP
#define __I4FILE_HPP

#include "arch.hh"
#include "isllist.hh"


enum { I4_FILE_STATUS_DIRECTORY=1};
struct i4_file_status_struct
{
  w32 last_modified;
  w32 last_accessed;
  w32 created;
  w32 flags;
} ;

// Summary
//   A file (i4_file_class) is a purely virtual file 
//   which should allow much flexability.
//   A i4_file_class is opened through the i4_file_manager_class::open().

//   A file manager defaults going through the operating system's open(), but
//   like the unix model other file managers can be mounted on top of directories.
//   When a file is accessed in this directory, the open call will be transfered
//   to this file manager.

//   This allows for redirection, tar-like-files, memory-files, and other goodies.

//   One particular instance this is useful, might be running game off of a cdrom.
//   the data directory can be linked to the cdrom and the savegame data can
//   be linked to somewhere on the hard-drive.

//   i.e.

//   i4_file_man.mount_dir("/cdrom",new file_redirector("d:\\"));
//   i4_file_class *fp=i4_file_man.open("/cdrom/art/picture.pcx");


//   TODO : test mounting, now that all string are i4_const_str instead of char *

class i4_const_str;
class i4_str;

class i4_file_class
{
public:
  virtual w32 read (void *buffer, w32 size)          = 0;
  virtual w32 write(const void *buffer, w32 size)    = 0;
  virtual w32 seek (w32 offset)                      = 0;
  virtual w32 size ()                                = 0;
  virtual w32 tell ()                                = 0;

  i4_bool eof() { return tell()==size(); }

  typedef void (*async_callback)(w32 count, void *context);
  
  // returns i4_F if an immediate error occured
  virtual i4_bool async_read (void *buffer, w32 size, 
                              async_callback call,
                              void *context=0);

  virtual i4_bool async_write(const void *buffer, w32 size, 
                              async_callback call,
                              void *context=0);

  // abort current operation
  virtual void abort() {}

  // Nice Read Methods
  w8      read_8   ()
  {
    w8 buf;
    if (read(&buf,1)!=1)
      return 0xff;
    else return buf;
  }

  w16     read_16  ()   // loads and converts to LSB (intel-format)
  {
    w16 buf;
    if (read(&buf,2)!=2)
      return 0xffff;
    else return s_to_lsb(buf);
  }

  w32     read_32  ()   // loads and converts to LSB (intel-format)
  {
    w32 buf;
    if (read(&buf,4)!=4)
      return 0xffffffff;
    else return l_to_lsb(buf);
  }

  float   read_float()   // loads and converts to LSB (intel-format)
  {
    w32 buf;
    if (read(&buf,4)!=4)
      return (float)0xffff;

    buf = l_to_lsb(buf);

    return *((float*)&buf);
  }

  i4_str* read_str(w32 len);
  i4_str* read_counted_str();

  // C++ stream operators
  i4_file_class& operator>>(w32& v)   { v = (w32) read_32(); return *this; }
  i4_file_class& operator>>(sw32& v)  { v = (sw32)read_32(); return *this; }
  i4_file_class& operator>>(w16& v)   { v = (w16) read_16(); return *this; }
  i4_file_class& operator>>(sw16& v)  { v = (sw16)read_16(); return *this; }
  i4_file_class& operator>>(w8& v)    { v = (w8)  read_8();  return *this; }
  i4_file_class& operator>>(sw8& v)   { v = (sw8) read_8();  return *this; }
  i4_file_class& operator>>(float& v) { v = read_float();    return *this; }

  // Nice Write Methods
  w32     write_8   (w8 num)
  {
    return (write(&num,1));
  }

  w32     write_16  (w16 num)   // loads and converts to LSB (intel-format)
  {
    num = s_to_lsb(num);
    return (write(&num,2));
  }

  w32     write_32  (w32 num)   // loads and converts to LSB (intel-format)
  {
    num = l_to_lsb(num);
    return (write(&num,4));
  }

  w32     write_float(float num)   // loads and converts to LSB (intel-format)
  {
    w32 tmp = l_to_lsb( *((w32*)&num) );
    return (write(&tmp,4));
  }

  w32 write_str(const i4_const_str &str);
  w32 write_counted_str(const i4_const_str &str);

  // same as fprintf, but with the addition %S is a i4_const_str *
  int printf(char *format, ...);

  // write_format takes a different set of % symbols than the typical printf to elimante
  // confusion with sizes and be easily usuable with readf_binary
  // writef_binary("124fS", &a_w8_var, &a_w16_var, &a_w32_var, 
  //                        &a_float_var, &a_i4_const_str_pointer);
  int write_format(char *format, ...);

  // format is same as write_format
  int read_format(char *format, ...);

  

  // C++ stream operators
  i4_file_class& operator<<(w32 v)   { write_32(v);      return *this; }
  i4_file_class& operator<<(sw32 v)  { write_32((w32)v); return *this; }
  i4_file_class& operator<<(w16 v)   { write_16(v);      return *this; }
  i4_file_class& operator<<(sw16 v)  { write_16((w16)v); return *this; }
  i4_file_class& operator<<(w8 v)    { write_8(v);       return *this; }
  i4_file_class& operator<<(sw8 v)   { write_8((w8)v);   return *this; }
  i4_file_class& operator<<(float v) { write_float(v);   return *this; }

  virtual ~i4_file_class() {}
};


// open flags 
enum { I4_READ=1,
       I4_WRITE=2,
       I4_APPEND=4,
       I4_NO_BUFFER=8,
       I4_SUPPORT_ASYNC=16  // this flag is needed if you intend to call async_read/write
};

// returns NULL if unable to open file
i4_file_class *i4_open(const i4_const_str &name, w32 flags=I4_READ);  

// return i4_F on failure
i4_bool i4_unlink(const i4_const_str &name);                        

// returns i4_F if file does not exsist
i4_bool i4_get_status(const i4_const_str &filename, 
                      i4_file_status_struct &return_stat);

// return i4_F on failure
i4_bool i4_mkdir(const i4_const_str &path);
i4_bool i4_rmdir(const i4_const_str &path);
i4_bool i4_chdir(const i4_const_str &path);

struct i4_directory_struct
{
  i4_str **files;
  w32 tfiles;

  i4_str **dirs;
  w32 tdirs;

  i4_file_status_struct *file_status;  // array of file stats coresponding to above files

  i4_directory_struct() { tfiles=tdirs=0; files=dirs=0; file_status=0; }
  ~i4_directory_struct();
};

// returns i4_F if path is bad (tfiles and tdirs will be 0 as well)
// you are responsible for deleting both the array of strings and each string in the array
// file_status is a pointer to an array of file_status's that will be created, you
// must free these as well.  file_status may be 0 (default), in which case no array is created
class i4_status_class;
i4_bool i4_get_directory(const i4_const_str &path, 
                         i4_directory_struct &dir_struct, 
                         i4_bool get_status=i4_F,
                         i4_status_class *status=0);



struct i4_filename_struct
{
  char path[256];
  char filename[256];
  char extension[256];

  i4_filename_struct() { path[0]=0; filename[0]=0; extension[0]=0; }
};

// returns i4_F if path cannot be split
i4_bool i4_split_path(const i4_const_str &name, i4_filename_struct &fname_struct);

// return 0 if full path cannot be determined
i4_str *i4_full_path(const i4_const_str &relative_name);  

// returns the shortest relative path
i4_str *i4_relative_path(const i4_const_str &path);



#endif