/*
 * Happy I/O abstractions
 *
 * Classes for making I/O of byte-level structures easier.  These
 * classes provide functions like writeLong and readLong for various
 * underlying structures like files and strings.
 */

#ifndef HAPPYIO_H
#define HAPPYIO_H

#include <str.h>
#include <suio++.h>
#include <stdio.h>
#include <err.h>

// Abstract base class for byte-level writing to streams
class streamWriter
{
public:
  virtual void write(const void *x, int size) = 0;
  virtual bool hasError() = 0;
  virtual ~streamWriter() {};

# define WRITER(name, type) void write##name(const type x) \
                            { write(&x, sizeof(type)); }
  // Simple writers
  WRITER(Byte, unsigned char);
  WRITER(Short, unsigned short);
  WRITER(Long, unsigned long);
# undef WRITER

  void writeStr(const str x)
  {
    long len = x.len();
    writeLong(x.len());
    write(x.cstr(), len);
  }
};

// Abstract base class for byte-level reading from streams
class streamReader
{
public:
  virtual void read(void *x, int size) = 0;
  virtual bool hasError() = 0;
  virtual bool eos() = 0;
  virtual ~streamReader() {};

# define READER(name, type) type read##name() \
                            { type x; read(&x, sizeof(type)); return x; }
  READER(Byte, unsigned char);
  READER(Short, unsigned short);
  READER(Long, unsigned long);
# undef READER

  str readStr()
  {
    long len = readLong();
    if (hasError())
      fatal << "Could not read length of string";

    char *buf = New char[len];
    if (!buf)
      fatal << "Could not allocate buffer to read string of length "
            << len << "\n";

    read(buf, len);
    if (hasError())
      fatal << "Could not read string contents of length " << len << "\n";

    return str(buf, len);
  }
};

// Stream writer for stdio streams
class fileWriter : public streamWriter
{
public:
  fileWriter(FILE *fp);
  void write(const void *x, int size);
  bool hasError();
private:
  FILE *fp;
};

// Stream reader for stdio streams
class fileReader : public streamReader
{
public:
  fileReader(FILE *fp);
  void read(void *x, int size);
  bool hasError();
  bool eos();
private:
  FILE *fp;
};

// Stream writer for strings
class strWriter : public streamWriter
{
public:
  strWriter();
  void write(const void *x, int size);
  bool hasError();
  str getStr();
private:
  suio buf;
};

// Stream reader for strings
class strReader : public streamReader
{
public:
  strReader(str s, int offset = 0);
  void read(void *x, int size);
  bool hasError();
  bool eos();
private:
  str s;
  int offset;
  bool eosFlag;
};

#endif // HAPPYIO_H
