#ifndef _BFSTREAM_H
#define _BFSTREAM_H

#include <fstream>
#include <string>
#include <cstring>
#include "bsstream.h"

namespace disk_tools {

using namespace std;

class bfstream : public fstream {
public:
   bfstream(const char * file_name, int op);
   bfstream() { }
   ~bfstream();

   bfstream & operator<<(const string & str);
   bfstream & operator<<(const char * str);
   bfstream & operator<<(const char ch);
   bfstream & operator<<(const short s);
   bfstream & operator<<(const int i);
   bfstream & operator<<(const long & l);
   bfstream & operator<<(const float f);
   bfstream & operator<<(const double & d);
   bfstream & operator<<(const unsigned short s);
   bfstream & operator<<(const unsigned int i);
   bfstream & operator<<(const unsigned long & l);
   bfstream & operator<<(const bool & b);

   bfstream & operator>>(string & str);
   bfstream & operator>>(char * str);  
   bfstream & operator>>(char & ch);
   bfstream & operator>>(short & s);
   bfstream & operator>>(int & i);  
   bfstream & operator>>(long & l);
   bfstream & operator>>(float & f);
   bfstream & operator>>(double & d);
   bfstream & operator>>(unsigned short & s);
   bfstream & operator>>(unsigned int & i);  
   bfstream & operator>>(unsigned long & l);
   bfstream & operator>>(bool & b);

   void seek(long offset);

   bool bad() { 
      return fstream::bad();
   }

   bool fail() { 
      return fstream::fail();
   }

   void read(char * ptr, size_t sz) {
      fstream::read(ptr, sz);
   }

   void write(const char * ptr, size_t sz) {
      fstream::write(ptr, sz);
   }

   void open(const char * filename, int flags) {
      fstream::open(filename, flags);
   }
};

inline
bfstream &
bfstream::operator>>(bool & b)
{
   read((char*)&b, sizeof(bool));
   return *this;
}

inline
bfstream &
bfstream::operator<<(const bool & b)
{
   write((const char*)&b, sizeof(bool));
   return *this;
}

inline
void
bfstream::seek(long offset)
{
   //fstream::seekg(offset, ios::beg);
   off_t off = tellg();
   if(off < offset) {
      off_t temp = offset - off;
      seekg(temp, ios::cur);
   }
   else if(off == offset) {

   }
   else {
      off_t temp = off - offset;
      seekg(-temp, ios::cur);
   }
}

inline
bfstream::bfstream(const char * file_name, int opt)
:fstream(file_name, opt | ios::binary) 
{
}

inline
bfstream::~bfstream()
{
   fstream::close();
}

inline
bfstream & bfstream::operator<<(const string & str)
{
   size_t size = str.size() + 1;
   fstream::write(reinterpret_cast< const char * >(&size), sizeof(size_t));
   fstream::write(str.c_str(), size);
   return *this;
}

inline
bfstream & bfstream::operator<<(const char * str)
{
   size_t size = strlen(str) + 1;
   fstream::write(reinterpret_cast< const char * >(&size), sizeof(size_t));
   fstream::write(str, size);
   return *this;
}

inline
bfstream & bfstream::operator<<(const char ch)
{
   fstream::write(&ch, sizeof(const char));
   return *this;
}

inline
bfstream & bfstream::operator<<(const short s)
{
   fstream::write(reinterpret_cast< const char * >(&s), sizeof(const short));
   return *this;
}

inline
bfstream & bfstream::operator<<(const int i)
{
   fstream::write(reinterpret_cast< const char * >(&i), sizeof(const int));
   return *this;
}

inline
bfstream & bfstream::operator<<(const long & l)
{
   fstream::write(reinterpret_cast< const char * >(&l), sizeof(const long));
   return *this;
}

inline
bfstream & bfstream::operator<<(const float f)
{
   fstream::write(reinterpret_cast< const char * >(&f), sizeof(const float));
   return *this;
}

inline
bfstream & bfstream::operator<<(const double & d)
{
   fstream::write(reinterpret_cast< const char * >(&d), sizeof(const double));
   return *this;
}

inline
bfstream & bfstream::operator<<(const unsigned short s)
{
   fstream::write(reinterpret_cast< const char * >(&s), sizeof(const unsigned short));
   return *this;
}

inline
bfstream & bfstream::operator<<(const unsigned int i)
{
   fstream::write(reinterpret_cast< const char * >(&i), sizeof(const unsigned int));
   return *this;
}

inline
bfstream & bfstream::operator<<(const unsigned long & l)
{
   fstream::write(reinterpret_cast< const char * >(&l), sizeof(const unsigned long));
   return *this;
}

inline
bfstream & bfstream::operator>>(string & str)
{
   size_t sz;

   fstream::read(reinterpret_cast< char * >(&sz), sizeof(size_t));

   char * buffer = new char [ sz ];

   memset(buffer, 0, sz);

   fstream::read(buffer, sz);

   str = buffer;

   delete [] buffer;

   return *this;
}

inline
bfstream & bfstream::operator>>(char * str)
{
   size_t sz;

   fstream::read(reinterpret_cast< char * >(&sz), sizeof(size_t));

   fstream::read(str, sz);

   return *this;
}

inline
bfstream & bfstream::operator>>(char & ch)
{
   fstream::read(&ch, sizeof(char));
   return *this;
}

inline
bfstream & bfstream::operator>>(short & s)
{
   fstream::read(reinterpret_cast< char* >(&s), sizeof(short));
   return *this;
}

inline
bfstream & bfstream::operator>>(int & i)
{
   fstream::read(reinterpret_cast< char* >(&i), sizeof(int));
   return *this;
}

inline
bfstream & bfstream::operator>>(long & l)
{
   fstream::read(reinterpret_cast< char * >(&l), sizeof(long));

   return *this;
}

inline
bfstream & bfstream::operator>>(float & f)
{
   fstream::read(reinterpret_cast< char * >(&f), sizeof(float));

   return *this;
}

inline
bfstream & bfstream::operator>>(double & d)
{
   fstream::read(reinterpret_cast< char * >(&d), sizeof(double));

   return *this;
}

inline
bfstream & bfstream::operator>>(unsigned short & s)
{
   fstream::read(reinterpret_cast< char * >(&s), sizeof(unsigned short));

   return *this;
}

inline
bfstream & bfstream::operator>>(unsigned int & i)
{
   fstream::read(reinterpret_cast< char * >(&i), sizeof(unsigned int));

   return *this;
}

inline
bfstream & bfstream::operator>>(unsigned long & l)
{
   fstream::read(reinterpret_cast< char * >(&l), sizeof(unsigned long));

   return *this;
}

}

#endif /* _BFSTREAM_H */
