#define _X_OPEN_SOURCE_EXTENDED
#include <uf/utils/io/iostream.h>

#if UF_USE_NCURSES
#include <uf/ext/ncurses/ncurses.h>
#include <ncursesw/ncurses.h>
#endif
#include <uf/spec/terminal/terminal.h>

#include <sstream>
#include <iostream>

#include <uf/utils/memory/vector.h>

namespace {
	struct {
		int character;
		uf::stl::string temporary;
		struct {
			uf::stl::string buffer;
			uf::stl::vector<uf::stl::string> history;
		} input, output;
		struct {
			unsigned int buffer = 0;
			unsigned int history = -1;
		} indices;
		struct {
			int rows = 0;
			int columns = 0;
		} window;
		struct {
			int row = 0;
			int column = 0;
		} cursor, origin;
		struct {
			short last = 0;
		} color;
	} info;
	void addCh( char ch ) {
		if ( ch == '\r' ) ch = '\n';
		if ( ch == '\n' ) {
			::info.output.history.push_back(::info.output.buffer);
			::info.output.buffer = "";
			return;
		} else if ( ch < 32 || ch > 127 ) return;
		::info.output.buffer += ch;
	}
	void addStr( const uf::stl::string& str ) {
		for ( auto x : str ) addCh(x);
	}
#if UF_USE_DEPRECATED_STRING
	void addUStr( const uf::String& str ) {
		for ( auto x : str.getString() ) addCh(x);
	}
#endif
};
bool uf::IoStream::ncurses = false;
uf::IoStream uf::iostream;

uf::IoStream::IoStream() {
	//spec::terminal.setLocale();
}
uf::IoStream::~IoStream() {
}

void uf::IoStream::initialize() {
}
void uf::IoStream::terminate() {
}
void uf::IoStream::clear(bool all) {
	spec::terminal.clear();
}
uf::stl::string uf::IoStream::getBuffer() {
	return ::info.output.buffer;
}
uf::stl::vector<uf::stl::string> uf::IoStream::getHistory() {
	return ::info.output.history;
}
void uf::IoStream::pushHistory( const uf::stl::string& str ) {
	::info.output.history.emplace_back(str);
}
void uf::IoStream::back() {
}
char uf::IoStream::readChar(const bool& loop) {
	auto ch = std::cin.get();
	
	addCh(ch);
//	::info.input.history.push_back( ::fmt::format("{}", ch) );
	return ch;
}
uf::stl::string uf::IoStream::readString(const bool& loop) {
	uf::stl::string str;
	std::getline(std::cin, str);

	addStr(str);
//	::info.input.history.push_back( str );
	return str;
}
#if UF_USE_DEPRECATED_STRING
uf::String uf::IoStream::readUString(const bool& loop) {
	uf::stl::string str;
	std::getline(std::cin, str);

	addStr(str);
//	::info.input.history.push_back( str );
	return str;
}
#endif
char uf::IoStream::writeChar( char ch ) {
	addCh(ch);

	if ( ch == '\n' ) std::cout << std::endl;
	else std::cout << ch;
//	::info.input.history.push_back( ::fmt::format("{}", ch) );
	return ch;
}
const uf::stl::string& uf::IoStream::writeString( const uf::stl::string& str ) {
	addStr(str);
	
	if ( str == "\n" ) std::cout << std::endl;
	else std::cout << str;
//	::info.input.history.push_back( str );
	return str;
}
#if UF_USE_DEPRECATED_STRING
const uf::String& uf::IoStream::writeUString( const uf::String& str ) {
	addUStr(str);
	
	std::cout << (const char*) str.getString().c_str();
//	::info.input.history.push_back( str );
	return str;
}
#endif

void uf::IoStream::operator>> (bool& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}

void uf::IoStream::operator>> (char& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (short& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (unsigned short& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (int& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (unsigned int& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (long& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (unsigned long& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (long long& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (unsigned long long& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (float& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (double& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (long double& val) {
	uf::stl::stringstream ss;
	ss << this->readString();
	ss >> val;
}
void uf::IoStream::operator>> (uf::stl::string& val) {
	val = this->readString();
}
#if UF_USE_DEPRECATED_STRING
void uf::IoStream::operator>> (uf::String& val) {
	val = this->readUString();
}
#endif
std::istream& uf::IoStream::operator>> ( std::istream& is ) {
	uf::stl::string input;
	is >> input;
	this->writeString(input);
	return is;
}
std::istream& operator>> ( std::istream& is, uf::IoStream& io ) {
	uf::stl::string input;
	is >> input;
	io.writeString(input);
	return is;
}



uf::IoStream& uf::IoStream::operator<< (const bool& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const char& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const short& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const unsigned short& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const int& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const unsigned int& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const long& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const unsigned long& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const long long& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const unsigned long long& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const float& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const double& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const long double& val) {
	uf::stl::stringstream ss;
	ss << val;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const uf::stl::string& val) {
	this->writeString(val);
	return *this;
}
#if UF_USE_DEPRECATED_STRING
uf::IoStream& uf::IoStream::operator<< (const uf::String& val) {
	this->writeUString(val);
	return *this;
}
#endif
uf::IoStream& uf::IoStream::operator<< (const char* cstr) {
	this->writeString(uf::stl::string(cstr));
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (const uf::Serializer& val) {
	this->writeString(val.serialize());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< (void* ptr) {
	uf::stl::stringstream ss;
	ss << ptr;
	this->writeString(ss.str());
	return *this;
}
uf::IoStream& uf::IoStream::operator<< ( std::ostream& os ) {
	uf::stl::stringstream str;
	str << os.rdbuf();
	this->writeString( str.str() );
	return *this;
}
std::ostream& operator<< ( std::ostream& os, uf::IoStream& io ) {
	uf::stl::string output = io.readString();
	os << output;
	return os;
}

uf::stl::string uf::IoStream::getColor() {
	return ""; // this->m_currentColor;
}
void uf::IoStream::setColor( const uf::stl::string& str ) {
}

// manip via stream manipulator
uf::IoStream::Color::Manip1::Manip1( uf::IoStream& _io ) : io(_io) {}
uf::IoStream::Color::Manip1 uf::IoStream::operator<< ( const uf::IoStream::Color& color) {
	return uf::IoStream::Color::Manip1(*this);
}
uf::IoStream& uf::IoStream::Color::Manip1::operator<<( const uf::stl::string& name ) {
	this->io.setColor(name);
	return this->io;
}

// manip via function call
uf::IoStream::Color::Manip2::Manip2( const uf::stl::string& _name ) : name(_name) {}
uf::IoStream::Color::Manip2 uf::IoStream::Color::operator()( const uf::stl::string& name ) const {
	return uf::IoStream::Color::Manip2(name);
}
uf::IoStream& uf::IoStream::Color::operator()( uf::IoStream& io, const uf::stl::string& name ) const {
	io.setColor(name);
	return io;
}

uf::IoStream& uf::IoStream::operator<< (const uf::IoStream::Color::Manip2& color) {
	this->setColor(color.name);
	return *this;
}