/*
 * fdstream.cpp
 *
 * Created on: Feb 12, 2016
 * Author: Radovan Cerveny
 */

#include "fdstream.hpp"

namespace ext {

const int CERR_FD = 2;

 // used for measurements output
const int CMEASURE_FD = 5;

 // used for logging
const int CLOG_FD = 4;

const int FAIL_FD = -1;

fdstreambuf::fdstreambuf ( int fileDescriptor ) : fd ( fileDescriptor ) {
	setp ( buff.begin ( ), buff.end ( ) - 1 );
	setg ( buff.end ( ), buff.end ( ), buff.end ( ) );
}

fdstreambuf::~fdstreambuf ( ) {
	sync ( );
}

bool fdstreambuf::flush ( ) {
	std::ptrdiff_t n = pptr ( ) - pbase ( );

	ssize_t osz = write ( fd, buff.data ( ), n );

	pbump ( -n );

	return osz != -1;
}

fdstreambuf::int_type fdstreambuf::underflow ( ) {
	if ( gptr ( ) < egptr ( ) )
		return traits_type::to_int_type ( * gptr ( ) );

	 // we have moved the eback pointer from buff.end(), move one character back for putback
	size_t moved = 0;

	if ( eback ( ) == buff.begin ( ) ) {
		buff[0] = * ( egptr ( ) - 1 );

		moved++;
	}

	ssize_t s = read ( fd, buff.begin ( ) + moved, buff_sz - moved );

	if ( s == 0 )
		return traits_type::eof ( );

	setg ( buff.begin ( ), buff.begin ( ) + moved, buff.begin ( ) + moved + s );

	return traits_type::to_int_type ( * gptr ( ) );
}

fdstreambuf::int_type fdstreambuf::pbackfail ( int_type c = traits_type::eof ( ) ) {
	if ( c == traits_type::eof ( ) ) return c;

	return * ( gptr ( ) ) = c;
}

fdstreambuf::int_type fdstreambuf::overflow ( fdstreambuf::int_type ch ) {
	if ( ch == traits_type::eof ( ) ) return traits_type::eof ( );

	buff.back ( ) = ch;

	pbump ( 1 );

	if ( !flush ( ) ) return traits_type::eof ( );

	return ch;
}

int fdstreambuf::sync ( ) {
	return flush ( ) ? 0 : -1;
}

fdaccessor::fdaccessor ( int new_fd, int fallback_fd ) {
	if ( fcntl ( new_fd, F_GETFD ) != -1 ) {
		fd = new_fd;
		redirected = true;
	} else if ( fcntl ( fallback_fd, F_GETFD ) != -1 ) {
		fd = fallback_fd;
		redirected = false;
	} else {
		fd = FAIL_FD;
		redirected = false;
	}
}

int fdaccessor::get_fd ( ) const {
	return fd;
}

bool fdaccessor::is_redirected ( ) const {
	return redirected;
}

ofdstream::ofdstream ( int fd, int fallback_fd ) : /* NOTE: this might potentially be dangerous since fdbuf is initialized later */ std::ostream ( & fdbuf ), fda ( fd, fallback_fd ), fdbuf ( fda.get_fd ( ) ) {
	if ( fda.get_fd ( ) == FAIL_FD )
		setstate ( ios_base::failbit );
}

ofdstream::~ofdstream ( ) = default;

bool ofdstream::is_redirected ( ) const {
	return fda.is_redirected ( );
}

ifdstream::ifdstream ( int fd, int fallback_fd ) : /* NOTE: this might potentially be dangerous since fdbuf is initialized later */ std::istream ( & fdbuf ), fda ( fd, fallback_fd ), fdbuf ( fda.get_fd ( ) ) {
	if ( fda.get_fd ( ) == FAIL_FD )
		setstate ( ios_base::failbit );
}

ifdstream::~ifdstream ( ) = default;

bool ifdstream::is_redirected ( ) const {
	return fda.is_redirected ( );
}

} /* namespace ext */
