//  fstream : headerfile for the free standard C++ library
//  
//  Copyright (C) 1999 by the free standard C++ Library Team
//                        see AUTHORS for more details
//
//  Homepage : http://www.inf.fu-berlin.de/~mkrueger/fscl/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either	
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Library General Public License for more details.
//  You should have received a copy of the GNU Library General Public
//  License along with this library; if not, write to the Free
//  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  version : 0.1 last modified : 17.09.99

#ifndef __CPP_FSTREAM
#define __CPP_FSTREAM

#include<cstddef>
#include<new>

#include<cstdio>
#include<cstring>
#include<ios>
#include<streambuf>
#include<istream>
#include<ostream>

namespace std {
	
	template <class charT, class traits>
	class basic_filebuf : public basic_streambuf<charT,traits>
	{
		public:
			typedef typename traits::int_type int_type;
			typedef typename traits::pos_type pos_type;
			typedef typename traits::off_type off_type;
		
			basic_filebuf() 
			: basic_streambuf<charT, traits>()
			{
				isopen = false;
				shouldclose = false;
			}
			
			virtual ~basic_filebuf()
			{
				if (is_open())
					close();
			}
			
			bool is_open() const
			{
				return this->isopen;
			}

		basic_filebuf<charT,traits> *open(const char* s, ios_base::openmode mode = ios_base::in)
			{ 
				this->mode = mode;
				shouldclose = false;				
				if (mode & ios_base::out && strcmp(s,"stdout") == 0) {
					fh = stdout;
				} else				
				if (mode & ios_base::in && strcmp(s,"stdin") == 0) {
					fh = stdin;
				} else				
				if (mode & ios_base::out && strcmp(s, "stderr") == 0) {
					fh = stderr;
				} else
				if (mode & ios_base::in) { 
					fh = fopen(s, "rb");
					shouldclose = true;
				} else
				if (mode & ios_base::out) {
					fh = fopen(s, "wb");
					shouldclose = true;
				} else {
					isopen = false;
					return this;
				}
				isopen = true;
				return this;
			}

			basic_filebuf<charT,traits>* close()
			{
				if (is_open()) {
					this->sync();
					if (shouldclose && fh) // fh shouldn't be 0 !!!
						fclose(fh);
					isopen = false;
				}
				return this;
			}

		protected:
		
			virtual int showmanyc()
			{ 
				return -1;
			}
	
			virtual int_type uflow()
			{			
				if (is_open()) {
					int_type c = fgetc(fh);
					if (c == EOF) 
						return traits::eof();
					lastc = c;
					return c;
				}
				return traits::eof();
			}
			
			virtual int_type underflow()
			{
				int_type u = uflow();
				ungetc(u, fh);
				return u;
			}

			virtual int_type pbackfail(int_type c = traits::eof())
			{
//				if (!traits::eq_int_type(c, traits::eof()))
					return traits::to_int_type(ungetc(lastc, fh));
/*				else {
					return traits::eof();
				}*/
			}
			
			virtual int_type overflow (int_type c = traits::eof())
			{ 
				if (is_open() && !traits::eq_int_type(c, traits::eof()) ) {
					fputc(c, fh);
					return c;
				}
				return traits::eof();
			}
			/*
			virtual pos_type seekoff(off_type off, ios_base::seekdir way, ios_base::openmode which = ios_base::in | ios_base::out)
			{
				switch(way) {
					case ios_base::cur:
						return fseek(fh, off, SEEK_CUR);
					break;
					case ios_base::beg:
						return fseek(fh, off, SEEK_SET);
					break;
					case ios_base::end:
						return fseek(fh, off, SEEK_END);
					break;
				}
			}
			
			virtual pos_type seekpos(pos_type sp, ios_base::openmode which = ios_base::in | ios_base::out)
			{
				return seekoff(sp, ios_base::beg, which);
			}
			*/

			virtual streamsize xsputn(const charT* s, streamsize n)
			{
				return (streamsize)fwrite(s, sizeof(charT), n, fh);
			}
			
			virtual streamsize xsgetn(charT* s, streamsize n)
			{
				return (streamsize)fread(s, sizeof(charT), n, fh);
			}


			virtual int sync()
			{
				if (is_open()) {
					fflush(fh);
				}
				return traits::eof();
			}
			
			/* 
			is inherited
			virtual void     imbue(const locale& loc)
			{
			}*/
	
		private:
			bool  isopen, shouldclose;
			ios_base::openmode mode;
			char lastc;
			FILE  *fh;
	};
	
	typedef basic_filebuf<char>    filebuf;
	typedef basic_filebuf<wchar_t> wfilebuf;
	
	template <class charT, class traits>
	class basic_ifstream : public basic_istream<charT,traits>
	{
		public:
			typedef charT                     char_type;
			typedef typename traits::int_type int_type;
			typedef typename traits::pos_type pos_type;
			typedef typename traits::off_type off_type;
	
			basic_ifstream() 
			: basic_istream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{			
			}
			
			explicit basic_ifstream(const char* s, ios_base::openmode mode = ios_base::in) : basic_istream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{
				fb.open(s, mode);
			}
			
			basic_filebuf<charT,traits>* rdbuf() const
			{
				return &sb;
			}			

			bool is_open()
			{
				return fb.is_open();
			}
			
			void open(const char* s, ios_base::openmode mode = in)
			{
				fb.open(s, mode);
			}
			
			void close()
			{
				fb.close();
			}

			~basic_ifstream()
			{
				close();
			}

		private:
			basic_filebuf<charT,traits> fb;
	};
	
	typedef basic_ifstream<char>    ifstream;
	typedef basic_ifstream<wchar_t> wifstream;
	
	template <class charT, class traits>
	class basic_ofstream : public basic_ostream<charT,traits>
	{
		public:
			typedef charT                     char_type;
			typedef typename traits::int_type int_type;
			typedef typename traits::pos_type pos_type;
			typedef typename traits::off_type off_type;
			
			basic_ofstream() : basic_ostream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{
			}			
			
			explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out | ios_base::trunc) : basic_ostream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{
				fb.open(s, mode);
			}
			
			basic_filebuf<charT,traits>* rdbuf() const
			{
				return &fb;
			}

			bool is_open()
			{
				return fb.is_open();
			}
			
			void open(const char* s, ios_base::openmode mode = out | trunc)
			{
				fb.open(s, mode);
			}
			
			void close()
			{
				fb.close();
			}
			
			~basic_ofstream()
			{
				close();
			}
			
		private:
			basic_filebuf<charT,traits> fb;
	};
	
	typedef basic_ofstream<char>    ofstream;
	typedef basic_ofstream<wchar_t> wofstream;
	
	template <class charT, class traits>
	class basic_fstream : public basic_iostream<charT,traits>
	{		
		public:
			typedef charT                     char_type;
			typedef typename traits::int_type ins_type;
			typedef typename traits::pos_type pos_type;
			typedef typename traits::off_type off_type;
			
			basic_fstream() 
			: basic_ostream<charT, traits>(&fb), basic_istream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{
			}
			
			explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out)
			: basic_ostream<charT, traits>(&fb), basic_istream<charT, traits>(&fb), basic_ios<charT, traits>(&fb)
			{
				fb.open(s, mode);
			}

			basic_filebuf<charT,traits>* rdbuf() const 
			{
				return &fb;
			}

			bool is_open()
			{
				return fb.is_open();
			}

			void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out)
			{
				fb.open(s, mode);
			}

			void close()			
			{
				fb.close();
			}

			~basic_fstream()
			{
				close();
			}

		private:
			basic_filebuf<charT,traits> fb;
	};
	
	typedef basic_fstream<char>     fstream;
	typedef basic_fstream<wchar_t> wfstream;
}

#endif
