// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp 
// Compiler Used: MSVC40, egcs-2.90.29, HP CPP 10.24
// Produced By: Doug Gaer   
// File Creation Date: 09/20/1999
// Date Last Modified: 06/21/2000
// Copyright (c) 2000 Dougas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBS C++ classes are copyright (c) 2000, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

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

// Define the __DOS_INCLUDES__ macro to use DOS path separators
#ifdef __DOS_INCLUDES__
#include <sys\types.h>
#include <sys\stat.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif

#if defined(__SMEMORY_BLOCK__) // Fixed length memory (static) blocks
typedef MemoryBlock MEMTYPE;

#elif defined(__VMEMORY_BUFFER__) // Variable length memory buffers
typedef MemoryBuffer MEMTYPE;

#else // Default to variable length memory buffers
typedef MemoryBuffer MEMTYPE;
#endif

typedef vbDeviceCachePtr devCachePointer; // Pointer to cache

// Example class used to create the device cache
class vbFileCache : public vbDeviceCache
{
public:
  vbFileCache(int CacheSize = 1024);
  ~vbFileCache() { }

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(devCachePointer 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, vbDeviceTypes dev);
  void Write(const void *buf, unsigned Bytes, vbDeviceTypes 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
  vbDeviceBucketCache cache;

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

vbFileCache::vbFileCache(int CacheSize) : cache(CacheSize) 
{ 
  is_ok = 1; 
  ready_for_writing = 1; 
  ready_for_reading = 1;
  cache.Connect(this); 
}

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

    default:
      break;
  }
}
  
void vbFileCache::Write(const void *buf, unsigned Bytes, vbDeviceTypes dev) 
{
  switch(dev) {
    case vbDEVICE_CONSOLE:
      cout.write((char *)buf, Bytes);
      break;

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

long vbFileCache::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 vbFileCache::CopyFileToFile(const char *in, const char *out, int *bc)
{
  if(!OpenInputFile(in)) return 0;
  if(!OpenOutputFile(out)) return 0;
  long end_of_file = StaticFileSize(in);

  vbDeviceTypes o_device = vbDEVICE_DISK_FILE; // Output device
  vbDeviceTypes i_device = vbDEVICE_NULL;      // No input buffering

  // Setup a pointer to the cache buckets
  devCachePointer 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 vbFileCache::LoadFile(devCachePointer 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.Alloc();
    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.Alloc();
      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.Alloc();
	p->Load(sbuf, end_of_file);
	break;
      }
      else
	continue;
    }
  }
  return  byte_count;
}

int vbFileCache::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 vbFileCache::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)
{

  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;
  vbFileCache dev(cache_size); // Device cache used to process a file

  cout << "Creating a device cache using " << cache_size 
       << " cache buckets." << endl;
  cout << "Reserving " << (sizeof(MEMTYPE) * 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 --------- //
// ------------------------------- //