#ifndef _zfstream_h
#define _zfstream_h

#include <fstream.h>
#include "zlib.h"

class gzfilebuf : public streambuf
{
public:
  gzfilebuf();
  virtual ~gzfilebuf();

  gzfilebuf* open  (const char *name, int io_mode );
  gzfilebuf* attach(int file_descriptor, int io_mode );
  gzfilebuf* close ();

  int setlevel   (const int level   );
  int setstrategy(const int strategy);

  inline int is_open(void) const { return (file !=NULL); }
  virtual streampos seekoff(streamoff, ios::seek_dir, int);
  virtual int sync(void);

protected:

  virtual int underflow(void);
  virtual int overflow(int = EOF);

private:
  gzFile file;
  short  mode;
  bool   is_my_fd;
  int    level;
  int    strategy;

  int flushbuf();
  int fillbuf();
};

class basic_gzfstream : virtual public ios
{
  friend class gzifstream;
  friend class gzofstream;
  friend gzofstream& setlevel   (gzofstream&, const int);
  friend gzofstream& setstrategy(gzofstream&, const int);

public:
  virtual ~basic_gzfstream();

  void attach(int fd, int io_mode);
  void open  (const char* name, int io_mode);
  void close (void);

protected:
  basic_gzfstream(void);

private:
  gzfilebuf* rdbuf(void);
  gzfilebuf  buffer;

};

class gzifstream : public basic_gzfstream, public istream
{
public:
  gzifstream(void);
  gzifstream(const char *name, int io_mode = ios::in);
  gzifstream(int fd, int io_mode = ios::in);

  virtual ~gzifstream(void);
};

class gzofstream : public basic_gzfstream, public ostream
{
public:
  gzofstream(void);
  gzofstream(const char *name, int io_mode = ios::out);
  gzofstream(int fd, int io_mode = ios::out);

  virtual ~gzofstream(void);

};

template<class T> class gzomanip
{
  friend gzofstream& operator<<<T>(gzofstream&, const gzomanip<T> &);
public:
  gzomanip(gzofstream& (*f)(gzofstream&, T), T v) : func(f), val(v) { }
private:
  gzofstream& (*func)(gzofstream&, T);
  T val;
};

template<class T> gzofstream&
operator<<(gzofstream& s, const gzomanip<T> &m) { return (*m.func)(s, m.val); }

inline gzofstream&
setlevel   (gzofstream& s, int l) { (s.rdbuf())->setlevel(l);    return s; }

inline gzomanip<int>
setlevel   (int l) { return gzomanip<int>(&setlevel,    l); }

inline gzofstream&
setstrategy(gzofstream& s, int l) { (s.rdbuf())->setstrategy(l); return s; }

inline gzomanip<int>
setstrategy(int l) { return gzomanip<int>(&setstrategy, l); }

#endif
