// I/O through <stdio.h> or <iostream.h>

#ifndef _CL_IO_H
#define _CL_IO_H

#include "cl_number.h"

#if !defined(CL_IO_STDIO) && !defined(CL_IO_IOSTREAM)
// Prefer iostream based I/O - this is what people expect.
#define CL_IO_IOSTREAM
#endif


#if defined(CL_IO_STDIO)

// I/O through <stdio.h>

#include <stdio.h>

#define cl_istream  FILE *
#define cl_ostream  FILE *
#define cl_stdin   stdin
#define cl_stdout  stdout
#define cl_stderr  stderr
extern FILE* cl_debugout;

// Elementary operations on cl_istream

#define cl_EOF  EOF

inline int freadchar (cl_istream stream)
{
	// return fgetc(stream);
	return getc(stream);
}

inline int funreadchar (cl_istream stream, int c)
{
	return ungetc(c,stream);
}

// Elementary operations on cl_ostream

inline void fprintchar (cl_ostream stream, char c)
{
	// fputc(c,stream);
	putc(c,stream);
}

inline void fprint (cl_ostream stream, const char * string)
{
	// fprintf(stream,"%s",string);
	fputs(string,stream);
}

extern void fprintdecimal (cl_ostream stream, int x);

#endif


#if defined(CL_IO_IOSTREAM)

// I/O through <iostream.h>

#include <iostream.h>

#define cl_istream  istream&
#define cl_ostream  ostream&
#define cl_stdin   cin
#define cl_stdout  cout
#define cl_stderr  cerr
extern ostream* cl_debugout_stream;
#define cl_debugout  (*cl_debugout_stream)

// Elementary operations on cl_istream

#define cl_EOF  (-1)

inline int freadchar (cl_istream stream)
{
	char c;
	if (stream.get(c))
		return c;
	else
		// EOF or error
		return cl_EOF;
}

inline int funreadchar (cl_istream stream, int c)
{
	if (c != cl_EOF)
		stream.putback((char)c);
	return c;
}

// Elementary operations on cl_ostream

inline void fprintchar (cl_ostream stream, char c)
{
	stream.put(c);
}

inline void fprint (cl_ostream stream, const char * string)
{
	stream << string;
}

inline void fprintdecimal (cl_ostream stream, int x)
{
	stream << dec << x;
}

#endif


class cl_print_flags;
CL_REQUIRE(cl_prin_globals)

inline void fprint (cl_ostream stream, const cl_number& x)
{
	extern cl_print_flags cl_default_print_flags;
	extern void print_number (cl_ostream, const cl_print_flags& flags, const cl_number&);
	print_number(stream,cl_default_print_flags,x);
}

// The following do strictly the same as the general `fprint' above.
// They are here only so that you don't need the complex and long-float number
// printers in order to print an integer. ("Treeshaking")

inline void fprint (cl_ostream stream, const cl_R& x)
{
	extern cl_print_flags cl_default_print_flags;
	extern void print_real (cl_ostream, const cl_print_flags& flags, const cl_R&);
	print_real(stream,cl_default_print_flags,x);
}

inline void fprint (cl_ostream stream, const cl_RA& x)
{
	extern cl_print_flags cl_default_print_flags;
	extern void print_rational (cl_ostream, const cl_print_flags& flags, const cl_RA&);
	print_rational(stream,cl_default_print_flags,x);
}

// The compiler now needs to be told the following.

inline void fprint (cl_ostream stream, const cl_I& x)
	{ fprint(stream, (const cl_RA&) x); }
inline void fprint (cl_ostream stream, const int x)
	{ fprint(stream,(cl_I)x); }
inline void fprint (cl_ostream stream, const unsigned int x)
	{ fprint(stream,(cl_I)x); }
inline void fprint (cl_ostream stream, const long x)
	{ fprint(stream,(cl_I)x); }
inline void fprint (cl_ostream stream, const unsigned long x)
	{ fprint(stream,(cl_I)x); }


inline void fprintdecimal (cl_ostream stream, const cl_I& x)
{
	extern void print_integer (cl_ostream stream, unsigned int base, const cl_I& z);
	print_integer(stream,10,x);
}

inline void fprintbinary (cl_ostream stream, const cl_I& x)
{
	extern void print_integer (cl_ostream stream, unsigned int base, const cl_I& z);
	print_integer(stream,2,x);
}

inline void fprintoctal (cl_ostream stream, const cl_I& x)
{
	extern void print_integer (cl_ostream stream, unsigned int base, const cl_I& z);
	print_integer(stream,8,x);
}

inline void fprinthexadecimal (cl_ostream stream, const cl_I& x)
{
	extern void print_integer (cl_ostream stream, unsigned int base, const cl_I& z);
	print_integer(stream,16,x);
}


// Define the customary << and >> operators.

#define CL_DEFINE_PRINT_OPERATOR(_class_)  \
inline cl_ostream operator<< (cl_ostream stream, const _class_& x)	\
{									\
	fprint(stream,x);						\
	return stream;							\
}
CL_DEFINE_PRINT_OPERATOR(cl_number)

struct cl_read_flags;
#define CL_DEFINE_READ_OPERATOR(_class_)  \
inline cl_istream operator>> (cl_istream stream, _class_& result)	\
{									\
	extern cl_read_flags _class_##_read_flags;			\
	extern cl_number read_number (cl_istream, const cl_read_flags&);\
	result = As(_class_)(read_number(stream,_class_##_read_flags));	\
	return stream;							\
}
CL_DEFINE_READ_OPERATOR(cl_N)
CL_DEFINE_READ_OPERATOR(cl_R)
CL_DEFINE_READ_OPERATOR(cl_RA)
CL_DEFINE_READ_OPERATOR(cl_I)
CL_DEFINE_READ_OPERATOR(cl_F)
CL_DEFINE_READ_OPERATOR(cl_SF)
CL_DEFINE_READ_OPERATOR(cl_FF)
CL_DEFINE_READ_OPERATOR(cl_DF)
CL_DEFINE_READ_OPERATOR(cl_LF)

#endif /* _CL_IO_H */
