// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp 
// C++ Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: glNET Software
// File Creation Date: 09/20/1999
// Date Last Modified: 05/25/2001
// Copyright (c) 2001 glNET Software
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
 
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  
USA

This is a test program for the device cache classes.
*/
// ----------------------------------------------------------- // 
#include <fstream.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "devcache.h"
#include "memblock.h"
#include "membuf.h"

#ifdef __MSVC_DEBUG__
#include "leaktest.h"
#endif

// Example class used to create the device cache
class gxFileCache : public gxDeviceCache
{
public:
  gxFileCache(unsigned CacheSize = 1024);
  ~gxFileCache() { }

public:
  void Flush() { cache.Flush(); } // Flush the cache buckets
  unsigned BucketsInUse() { return cache.BucketsInUse(); }
  void CloseOutputFile() { infile.close(); }
  void CloseInputFile() { outfile.close(); }
  int OpenInputFile(const char *in);
  int OpenOutputFile(const char *out);
  long StaticFileSize(const char *fname);
  unsigned LoadFile(gxDeviceCachePtr p, long end_of_file);
  int CopyFileToFile(const char *in, const char *out, int *bc = 0);

private: // Base class interface
  void Read(void *buf, unsigned Bytes, gxDeviceTypes dev);
  void Write(const void *buf, unsigned Bytes, gxDeviceTypes dev);

private: // Device objects
  fstream outfile;           // File used to output data
  fstream infile;            // File used to input data
  
private: // Temporary variables used during read/write operations
  long infile_len;
  long outfile_len;

private: // Device cache
  gxDeviceBucketCache cache;

public: // Functions used to get the current device cache
  gxDeviceBucketCache *GetCache() { return &cache; }
};

gxFileCache::gxFileCache(unsigned CacheSize) : cache(CacheSize) 
{ 
  ready_for_writing = 1; 
  ready_for_reading = 1;
  cache.Connect(this); 
}

void gxFileCache::Read(void *buf, unsigned Bytes, gxDeviceTypes dev) 
{
  switch(dev) {
    case gxDEVICE_DISK_FILE:
      if(!infile) { ready_for_reading = 0; return; }
      else { ready_for_reading = 1; }
      infile.read((char *)buf, Bytes);
      break;

    default:
      break;
  }
}
  
void gxFileCache::Write(const void *buf, unsigned Bytes, gxDeviceTypes dev) 
{
  switch(dev) {
    case gxDEVICE_CONSOLE:
      cout.write((char *)buf, Bytes);
      break;

    case gxDEVICE_DISK_FILE:
      if(!outfile) { ready_for_writing = 0; return; }
      else { ready_for_writing = 1; }
      outfile.write((char *)buf, Bytes);
      break;
      
    default:
      break;
  }
}

long gxFileCache::StaticFileSize(const char *fname)
// Returns the file size of fname. 
{
  struct stat buf;
  int result = stat(fname, &buf);
  if(result != 0) return 0;
  return buf.st_size;
}

int gxFileCache::CopyFileToFile(const char *in, const char *out, int *bc)
{
  if(!OpenInputFile(in)) return 0;
  if(!OpenOutputFile(out)) return 0;

  if(!cache) return 0;
  
  long end_of_file = StaticFileSize(in);

  gxDeviceTypes o_device = gxDEVICE_DISK_FILE; // Output device
  gxDeviceTypes i_device = gxDEVICE_NULL;      // No input buffering

  // Setup a pointer to the cache buckets
  gxDeviceCachePtr p(cache, o_device, i_device); 
  *bc = LoadFile(p, end_of_file); // Load the file into the cache 
  cache.Flush(); // Ensure all the buckets a written to the output device
  infile.close();
  outfile.close();
  return 1;
}

unsigned gxFileCache::LoadFile(gxDeviceCachePtr p, long end_of_file)
// Load a file from disk into the device cache. Returns the
// number bytes read from the file.
{
  unsigned i, byte_count = 0;
  char sbuf[MEMORY_BLOCK_SIZE];
  for(i = 0; i < MEMORY_BLOCK_SIZE; i++) sbuf[i] = 0;

  if(end_of_file <= (long)MEMORY_BLOCK_SIZE) {
    // The file is smaller then one block
    infile.read(sbuf, end_of_file);
    byte_count = end_of_file;
    p->Load(sbuf, end_of_file);
  }
  else { // The file is larger then one block
    while(end_of_file > (long)MEMORY_BLOCK_SIZE) {
      for(i = 0; i < MEMORY_BLOCK_SIZE; i++) sbuf[i] = 0; 
      byte_count += MEMORY_BLOCK_SIZE;
      infile.read(sbuf, MEMORY_BLOCK_SIZE);
      p->Load(sbuf, MEMORY_BLOCK_SIZE);
      end_of_file -= MEMORY_BLOCK_SIZE;
      if(end_of_file <= (long)MEMORY_BLOCK_SIZE) {
	for(i = 0; i < MEMORY_BLOCK_SIZE; i++) sbuf[i] = 0; 
	byte_count += end_of_file;
	infile.read(sbuf, end_of_file);
	p->Load(sbuf, end_of_file);
	break;
      }
      else
	continue;
    }
  }
  return  byte_count;
}

int gxFileCache::OpenInputFile(const char *in)
// Open the file if it exists. Returns false
// it the file cannot be opened or if it does
// not exist.
{
#if defined(__WIN32__) || defined (__DOS__)
  // In MS-DOS/Windows there are two file types, text and binary
  infile.open(in, ios::in | ios::binary | ios::nocreate);
#elif defined(__UNIX__) 
  // In UNIX there is only one file type
  infile.open(in, ios::in | ios::nocreate);
#else
#error You must define a file system: __WIN32__ __DOS__ or __UNIX__
#endif

  if(!infile) return 0;
  infile_len = StaticFileSize(in);
  return 1;
}

int gxFileCache::OpenOutputFile(const char *out)
// Open the specifed file for writing and truncate
// it. Returns false if the file cannot be opened.
{
#if defined(__WIN32__) || defined (__DOS__)
  // In MS-DOS/Windows there are two file types, text and binary
  outfile.open(out, ios::out | ios::binary | ios::trunc);
#elif defined(__UNIX__) 
  // In UNIX there is only one file type
  outfile.open(out, ios::out|ios::trunc);
#else
#error You must define a file system: __WIN32__ __DOS__ or __UNIX__
#endif

  if(!outfile) return 0;
  outfile_len = StaticFileSize(out);
  return 1;
}

int main(int argc, char **argv)
{
#ifdef __MSVC_DEBUG__
  InitLeakTest();
#endif

  if(argc < 3) {
    cout << endl;
    cout << "Usage: " << argv[0] << " infile outfile" << endl;
    cout << endl;
    return 0;
  }

  char *in = argv[1];
  char *out = argv[2];

  int cache_size = 1024;
  gxFileCache dev(cache_size); // Device cache used to process a file

  cout << "Creating a device cache using " << cache_size 
       << " cache buckets." << endl;
  cout << "Reserving " << (sizeof(gxDeviceBucket) * cache_size) 
       << " bytes of memory." << endl;

  int byte_count;
  
  if(!dev.CopyFileToFile(in, out, &byte_count)) {
    cout << "Error copying file." << endl;
    return 0;
  }

  cout << "Finished processing file." << endl;
  cout << "Byte count = " << byte_count << endl;

  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //