#ifndef __CHAR_SEQUENCE_H_
#define __CHAR_SEQUENCE_H_

#include <string>
#include <vector>

#include <readline/LineReader.h>

namespace cli {

class CharSequence {
	std::unique_ptr < LineReader > m_lineReader;
	std::string putbackBuffer;

	const char * linePtr = nullptr;
	std::vector < std::string > m_lines;

	void fetch ( bool readNextLine ) {
		if ( ! readNextLine ) {
			return;
		}

		std::string line;
		for ( std::string tmpLine; m_lineReader->readline ( tmpLine ); ) {
			if ( ! tmpLine.empty ( ) && tmpLine.back ( ) == '\\' ) {
				tmpLine.back ( ) = '\n';
				line += tmpLine;
			} else {
				line += tmpLine;
				line += '\n';
				break;
			}
		}

		if ( line.empty ( ) )
			return;

		m_lines.push_back ( std::move ( line ) );

		this->linePtr = m_lines.back ( ).c_str ( );
	}

public:
	template < class Reader >
	CharSequence ( Reader && reader ) : m_lineReader ( new Reader ( std::move ( reader ) ) ) {
	}

	int getCharacter ( ) const {
		if ( ! putbackBuffer.empty ( ) )
			return ( int ) putbackBuffer.back ( );
		else if ( linePtr == nullptr )
			return EOF;
		else
			return ( int ) * linePtr;
	}

	void advance ( bool readNextLine ) {
		if ( ! putbackBuffer.empty ( ) ) {
			putbackBuffer.pop_back ( );
		} else if ( linePtr == nullptr ) {
				fetch ( readNextLine );
		} else {
			if ( * linePtr != '\0' )
				++ linePtr;
			if ( * linePtr == '\0' )
				fetch ( readNextLine );
		}
	}

	std::string getLine ( ) const {
		std::string res;
		for ( const std::string & line : m_lines ) {
			res += line;
		}
		return res;
	}

	void putback ( std::string string ) {
		putbackBuffer.insert ( putbackBuffer.end ( ), string.rbegin ( ), string.rend ( ) );
	}

	void reset ( ) {
		putbackBuffer.clear ( );
		linePtr = nullptr;
		m_lines.clear ( );
		m_lineReader->reset ( );
	}
};

} /* namespace cli */

#endif /* __CHAR_SEQUENCE_H_ */
