/*
  basic file stream class
*/

#ifndef BOB_DOM_FILESTREAM_INCLUDED
#define BOB_DOM_FILESTREAM_INCLUDED

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "bob_dom_stream.h"

/*
	bob_dom_obinaryfilestream - binary output stream
	bob_dom_otextfilestream - text output stream
*/


class bob_filestream
{
	protected:
		char *m_name;
		
		void name(const char *pszName)
		{
			delete[] m_name;
			size_t len=strlen(pszName) + 1;
			m_name=new char[len];
			memcpy(m_name, pszName, len);
		}
		
	public:
		enum mode{
			seekbof    =  1,
			seekeof    =  2,
			rdonly =  4,
			rdwr   =  8,
			create = 16
		};
		
		enum seek_type{
			seek_begin,
			seek_end,
			seek_current
		};
		
		bob_filestream() { m_name=new char[1]; m_name[0]=0; }
		virtual ~bob_filestream() { delete[] m_name; }
		
		const char *name() const { return m_name; }
		
		virtual bool open(const char *fileName, mode m=seekbof)=0;
		virtual void close()=0;
};

class bob_dom_obinaryfilestream : public bob_ostream<char>, public bob_filestream
{
	public:
		virtual size_t seek(int offset, seek_type origin)=0;
};

class bob_dom_otextfilestream : public bob_ostream<char>, public bob_filestream
{
	public:
		static const bob_addsemicolon = 4;
		
};

class bob_dom_itextfilestream : public bob_istream<char>, public bob_filestream
{

};

class bob_dom_ibinaryfilestream : public bob_istream<unsigned char>, public bob_filestream
{

};

// binary input operators
inline
bob_dom_ibinaryfilestream& operator >> (bob_dom_ibinaryfilestream& is, int& right)
{
	is.read((unsigned char*)&right, sizeof(right));
	right=be2le((unsigned int)right);
	return is;
}

inline
bob_dom_ibinaryfilestream& operator >> (bob_dom_ibinaryfilestream& is, short& right)
{
	is.read((unsigned char*)&right, sizeof(right));
	right=be2le((unsigned short)right);
	return is;
}

inline
bob_dom_ibinaryfilestream& operator >> (bob_dom_ibinaryfilestream& is, double& right)
{
	static const double multiplier=1.52587890625E-05; // == 1 / 65535
	
	int v;
	is >> v;
	right=v * multiplier;
	return is;
}

inline
bob_dom_ibinaryfilestream& operator >> (bob_dom_ibinaryfilestream& is, float& right)
{
	static const double multiplier=1.52587890625E-05; // == 1 / 65535
	
	int v;
	is >> v;
	right=(float)(v * multiplier);
	return is;
}

inline
bob_dom_ibinaryfilestream& operator >> (bob_dom_ibinaryfilestream& is, char*& right)
{
	char *buff=0, *p;
	size_t size=250, readed;
	buff=new char[size];
	
	size_t start=is.tell();
	while(is.good()){
		readed=is.read((unsigned char*)buff, size);
		if(is.fail()) break;
		if(p=(char*)memchr(buff, 0, readed)) break;
	}
	
	if(p){
		size_t send=is.tell();
		size_t end=(send - readed) + (p - buff);
		
		delete buff;
		size=end - start + 1;
		buff=new char[size];
		is.advance( - ((int)send - (int)start));
		is.read((unsigned char*)buff, size);
	}
	else{
		delete buff;
		buff=0;
	}
	right=buff;
	
	return is;
}

/* binary output operators */
inline 
bob_dom_obinaryfilestream& operator << (bob_dom_obinaryfilestream& os, int right)
{
	right=be2le((unsigned int)right);
	os.write((bob_dom_obinaryfilestream::const_value_type_ptr)&right, sizeof(right));
	return os;
}

inline 
bob_dom_obinaryfilestream& operator << (bob_dom_obinaryfilestream& os, short right)
{
	right=be2le((unsigned short)right);
	os.write((bob_dom_obinaryfilestream::const_value_type_ptr)&right, sizeof(right));
	return os;
}

inline
bob_dom_obinaryfilestream& operator << (bob_dom_obinaryfilestream& os, double& right)
{
	static const double multiplier=1.52587890625E-05; // == 1 / 65535
	int i;
	
	// int = right / multiplier
	__asm{
		mov eax, right
		fld qword ptr [eax]
		fdiv qword ptr multiplier
		fistp i
	}
	os << i;
	
	return os;
}

inline
bob_dom_obinaryfilestream& operator << (bob_dom_obinaryfilestream& os, float right)
{
	static const double multiplier=1.52587890625E-05; // == 1 / 65535
	
	int i;
	// int = right / multiplier
	__asm{
		//mov eax, right
		//fld dword ptr [eax]
		fld right
		fdiv qword ptr multiplier
		fistp i
	}
	os << i;
	
	return os;
}

inline 
bob_dom_obinaryfilestream& operator << (bob_dom_obinaryfilestream& os, const char *right)
{
	os.write((bob_dom_obinaryfilestream::const_value_type_ptr)right, strlen(right) + 1);
	return os;
}

/* text output operators */
inline 
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, const char *right)
{
	os.write(right, strlen(right));
	if(os.flags() & bob_dom_otextfilestream::bob_addsemicolon)
		os.write("; ", 2);
	
	return os;
}

inline 
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, int right)
{
	static char ch[20];
	if(os.flags() & bob_dom_otextfilestream::hex)
		sprintf(ch, "%X", right);
	else
		itoa(right, ch, 10);
	os << ch;
	return os;
}

inline 
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, float right)
{
	char ch[20];
	sprintf(ch, "%f", right);
	os << ch;
	return os;
}

inline
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, double& right)
{
	char ch[20];
	sprintf(ch, "%f", (float)right);
	os << ch;
	return os;
}

inline 
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, char right)
{
	os.put(right);
	return os;
}

// text manipulators

inline
bob_dom_otextfilestream& endl(bob_dom_otextfilestream& os)
{
	os.put('\r');
	os.put('\n');
	return os;
}

inline 
bob_dom_otextfilestream& autoSemicolons(bob_dom_otextfilestream& os)
{
	os.setf(bob_dom_otextfilestream::bob_addsemicolon);
	return os;
}

inline 
bob_dom_otextfilestream& noSemicolons(bob_dom_otextfilestream& os)
{
	os.unsetf(bob_dom_otextfilestream::bob_addsemicolon);
	return os;
}

inline
bob_dom_otextfilestream& hex(bob_dom_otextfilestream& os)
{
	os.setf(bob_dom_otextfilestream::hex);
	return os;
}

inline
bob_dom_otextfilestream& dec(bob_dom_otextfilestream& os)
{
	os.unsetf(bob_dom_otextfilestream::hex);
	return os;
}

inline
bob_dom_otextfilestream& operator << (bob_dom_otextfilestream& os, bob_dom_otextfilestream& f(bob_dom_otextfilestream& os))
{
	return f(os);
}

#endif // !defined(BOB_DOM_FILESTREAM_INCLUDED)