/**********************************************************************
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) 
***********************************************************************/

#include "file/file.hh"
#include "memory/malloc.hh"
#include "error/error.hh"
#include "time/profile.hh"
#include "error/error.hh"
#include "file/buf_file.hh"
#include "file/async.hh"
#include "file/file_man.hh"
#include "memory/array.hh"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "string/string.hh"

i4_profile_class pf_win32_seek("Win32File::Seek");

i4_profile_class pf_win32_read("Win32File::Read");
i4_profile_class pf_win32_write("Win32File::Write");

class i4_win32_async_reader : public i4_async_reader
{
public:
  i4_win32_async_reader(char *name) : i4_async_reader(name) {}
  virtual w32 read(sw32 fd, void *buffer, w32 count) 
  { 
    w32 res = _read(fd,buffer,count);
    return res;
  }
} i4_win32_async_instance("hd_async_reader_2");


////////////////////////////////////////////////////////////////////////
//
//  Normal Win32 File Class
//

class i4_win32_file_class : public i4_file_class
{
protected:
  w32 fd;
public:

  i4_win32_file_class(w32 fd) : fd(fd) {}

  virtual w32 read (void *buffer, w32 size)
  {
    pf_win32_read.start();
    w32 res = _read(fd,buffer,size);
    pf_win32_read.stop();
    return res;
  }

  virtual w32 write(const void *buffer, w32 size)
  {
    pf_win32_write.start();
    w32 res = _write(fd,buffer,size);
    pf_win32_write.stop();
    return res;
  }

  virtual w32 seek (w32 offset)
  {  
    pf_win32_seek.start();
    w32 res = lseek(fd, offset, SEEK_SET);
    pf_win32_seek.stop();
    return res;
  }

  virtual w32 size ()                       
  { 
    w32 len = _filelength(fd);

    /*
    w32 cur = lseek(fd,0,SEEK_CUR);
      ]  len = lseek(fd,0,SEEK_END);
    lseek(fd,cur,SEEK_SET);
    */
    return len;
  }

  virtual w32 tell ()
  {
    return _tell(fd);
  }

  ~i4_win32_file_class()
  { 
    _close(fd); 
  }


  // returns i4_F if an immediate error occured
  virtual i4_bool async_read (void *buffer, w32 size, 
                              async_callback call,
                              void *context=0)
  {
    if (i4_threads_supported())
      return i4_win32_async_instance.start_read(fd, buffer, size, call, context);
    else
      call(read(buffer,size),context);
    return i4_T;
  }

};


////////////////////////////////////////////////////////////////////////
//
//  File Manager Methods
//










// see file/file.hh for a description of what each of these functions do
class i4_win32_file_manager_class : public i4_file_manager_class
{
public:   
  virtual i4_file_class *open(const i4_const_str &name, w32 flags)
  {
    sw32 f=0;
    i4_bool no_buffer=i4_F;
    
    flags &= ~I4_SUPPORT_ASYNC;     // don't have to do anything special for these
    
    if (flags & I4_NO_BUFFER)
    {
      flags=(flags & (~I4_NO_BUFFER));
      no_buffer=i4_T;
    }

    f=O_BINARY;       // open all files in binary mode
    char sbuf[256];

    switch (flags)
    {
      case I4_READ: 
        f|=O_RDONLY;
        break;

      case I4_WRITE: 
        f |= O_WRONLY | O_CREAT;
        _unlink(i4_os_string(name,sbuf,sizeof(sbuf)));
        break;

      case I4_WRITE|I4_READ: 
        f |= O_RDWR | O_CREAT;
        _unlink(i4_os_string(name,sbuf,sizeof(sbuf)));
        break;

      case I4_APPEND:
      case I4_WRITE|I4_APPEND:
        f |= O_WRONLY|O_CREAT|O_APPEND;
        break;

      default: 
        i4_warning("i4_file_class::Bad open flags!");
        return NULL;     
    }

    int fd;


    fd=::_open(i4_os_string(name,sbuf,sizeof(sbuf)),f,_S_IREAD | _S_IWRITE);

    if (fd<0)
    {
      i4_warning("i4_file_class::open failed for %s\n",i4_os_string(name,sbuf,sizeof(sbuf)));
      return NULL;
    }


    i4_file_class *ret_fp;
    if (!no_buffer)
      ret_fp=new i4_buffered_file_class(new i4_win32_file_class(fd));
    else
      ret_fp=new i4_win32_file_class(fd);

    return ret_fp;
  }



  virtual i4_bool unlink(const i4_const_str &name)
  {
    char buf[256];
    return _unlink(i4_os_string(name,buf,sizeof(buf)))==0;
  }


  virtual i4_bool mkdir(const i4_const_str &name)
  {  
    char buf[256];
    return ::_mkdir(i4_os_string(name,buf,sizeof(buf)))==0;
  }

  i4_bool get_status(const i4_const_str &filename, i4_file_status_struct &return_stat)
  {
    i4_bool error=i4_F;
    struct _stat times;
    char buf[256];

    return_stat.flags=0;
    if (_stat(i4_os_string(filename,buf,sizeof(buf)),×)==0)      
    {
      return_stat.last_modified=times.st_mtime;
      return_stat.last_accessed=times.st_atime;
      return_stat.created=times.st_ctime;

      if (times.st_mode &  _S_IFDIR)
        return_stat.flags=I4_FILE_STATUS_DIRECTORY;     
    }
    else 
      error=i4_T;

    return (i4_bool)(!error);
  }

  virtual i4_bool get_directory(const i4_const_str &path, 
                                i4_directory_struct &dir_struct,
                                i4_bool get_status,
                                i4_status_class *status)

  {
    _finddata_t fdat;
    char os_str[255],buf[256];
    sprintf(os_str,"%s/*.*",i4_os_string(path,buf,sizeof(buf)));

    long handle=_findfirst(os_str,&fdat),done;
    if (handle==-1)
      return i4_F;
  
    i4_array stats(64,64);

    do
    {
      if (fdat.attrib & _A_SUBDIR)
      {
        dir_struct.tdirs++;
        dir_struct.dirs=(i4_str **)i4_realloc(dir_struct.dirs,
                                              sizeof(i4_str *)*dir_struct.tdirs,"dir list");
        dir_struct.dirs[dir_struct.tdirs-1]=new i4_str(i4_const_str(fdat.name));
      }
      else
      {
        i4_file_status_struct *s=stats.add();
        s->last_accessed=fdat.time_access;
        s->last_modified=fdat.time_write;
        s->created=fdat.time_create;

        dir_struct.tfiles++;
        dir_struct.files=(i4_str **)i4_realloc(dir_struct.files,
                                               sizeof(i4_str *)*dir_struct.tfiles,"dir list");
        dir_struct.files[dir_struct.tfiles-1]=new i4_str(i4_const_str(fdat.name));
      }

      done=_findnext(handle, &fdat);
    } while (done!=-1);


    if (get_status)
    {    
      if (dir_struct.tfiles)
      {
        i4_file_status_struct *sa;
        sa=(i4_file_status_struct *)i4_malloc(sizeof(i4_file_status_struct)*dir_struct.tfiles,"");
        for (int j=0; j