/*! \file picout.h
 * \ingroup Core
 * \~\brief
 * \~english Universal output to console class
 * \~russian Универсальный вывод в консоль
*/
/*
	PIP - Platform Independent Primitives
	Universal output to console class
	Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program 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 Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PICOUT_H
#define PICOUT_H

#include "piincludes.h"

#ifdef DOXYGEN

//! \~english Macro used for conditional (piDebug) output to PICout
//! \~russian Макрос для условного (piDebug) вывода в PICout
#  define piCout

//! \relatesalso PIObject
//! \~english Macro used for conditional (piDebug && PIObject::debug()) output to PICout for subclasses of PIObject
//! \~russian Макрос для условного (piDebug && PIObject::debug()) вывода в PICout для наследников PIObject
#  define piCoutObj

#else
#  define piCout PICout(piDebug)
#  define piCoutObj PICout(piDebug && debug()) << (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]")))
#endif


class PIObject;


//! \relatesalso PICout
//! \~english Namespace contains enums that controls PICout
//! \~russian Пространство имен содержит перечисления для контроля PICout
namespace PICoutManipulators {

	//! \~english Enum contains special characters
	//! \~russian Перечисление со спецсимволами
	enum PICoutSpecialChar {
		Null	/*! \~english Null-character, '\\0' \~russian Нулевой символ, '\\0' */,
		NewLine	/*! \~english New line character, '\\n' \~russian Новая строка, '\\n' */,
		Tab		/*! \~english Tab character, '\\t' \~russian Табуляция, '\\t' */,
		Esc		/*! \~english Escape character, '\\e' \~russian Esc-символ, '\\e' */,
		Quote	/*! \~english Quote character, '\"' \~russian Кавычки, '\"' */
	};

	//! \~english Enum contains immediate action
	//! \~russian Перечисление с немедленными действиями
	enum PICoutAction {
		Flush			/*! \~english Flush the output \~russian Обновить вывод */,
		Backspace		/*! \~english Remove last symbol \~russian Удалить последний символ */,
		ShowCursor		/*! \~english Show cursor \~russian Показать курсор */,
		HideCursor		/*! \~english Hide cursor \~russian Скрыть курсор */,
		ClearLine		/*! \~english Clear current line \~russian Очистить текущую строку */,
		ClearScreen		/*! \~english Clear the screen \~russian Очистить экран */,
		SaveContol		/*! \~english Save control flags, equivalent to \a saveControl() \~russian Сохранить флаги, аналогично \a saveControl() */,
		RestoreControl	/*! \~english Restore control flags, equivalent to \a restoreControl() \~russian Восстановить флаги, аналогично \a restoreControl() */
	};

	//! \~english Enum contains control of PICout
	//! \~russian Перечисление с управлением PICout
	enum PICoutControl {
		AddNone			/*! \~english No controls \~russian Без управления */ = 0x0,
		AddSpaces		/*! \~english Spaces will be appear after each output \~russian Пробел после каждого вывода */ = 0x1,
		AddNewLine		/*! \~english New line will be appear after all output \~russian Новая строка после завершения вывода */ = 0x2,
		AddQuotes		/*! \~english Each string will be quoted \~russian Каждая строка в кавычках */ = 0x4,
		DefaultControls	/*! \~english Default controls \~russian Управление по умолчанию */ = AddSpaces | AddNewLine,
		AddAll			/*! \~english All controls \~russian Всё управление */ = 0xFF,
		NoLock			/*! \~english Don`t use mutex for output \~russian Не использовать мьютекс при выводе */ = 0x100,
	};

	//! \~english Enum contains output format
	//! \~russian Перечисление с форматом вывода
	enum PICoutFormat {
		Bin			/*! \~english Binary representation of integers \~russian Двоичное представление для целых чисел */ = 0x01,
		Oct			/*! \~english Octal representation of integers \~russian Восьмеричное представление для целых чисел */ = 0x02,
		Dec			/*! \~english Decimal representation of integers \~russian Десятичное представление для целых чисел */ = 0x04,
		Hex			/*! \~english Hexadecimal representation of integers \~russian Шестнадцатеричное представление для целых чисел */ = 0x08,
		Bold		/*! \~english Bold \~russian Жирный */ = 0x10,
		Faint		/*! \~english \~russian */ = 0x20,
		Italic		/*! \~english \~russian */ = 0x40,
		Underline	/*! \~english Underline \~russian Подчеркнутый */ = 0x80,
		Blink		/*! \~english Blink \~russian Мигающий */ = 0x100,
		Black		/*! \~english Black font \~russian Чёрный */ = 0x400,
		Red			/*! \~english Red font \~russian Красный */ = 0x800,
		Green		/*! \~english Green font \~russian Зелёный */ = 0x1000,
		Blue		/*! \~english Blue font \~russian Синий */ = 0x2000,
		Yellow		/*! \~english Yellow font \~russian Жёлтый */ = 0x4000,
		Magenta		/*! \~english Magenta font \~russian Пурпурный */ = 0x8000,
		Cyan		/*! \~english Cyan font \~russian Голубой */ = 0x10000,
		White		/*! \~english White font \~russian Белый */ = 0x20000,
		BackBlack	/*! \~english Black background \~russian Чёрный фон */ = 0x40000,
		BackRed		/*! \~english Red background \~russian Красный фон */ = 0x80000,
		BackGreen	/*! \~english Green background \~russian Зелёный фон */ = 0x100000,
		BackBlue	/*! \~english Blue background \~russian Синий фон */ = 0x200000,
		BackYellow	/*! \~english Yellow background \~russian Жёлтый фон */ = 0x400000,
		BackMagenta	/*! \~english Magenta background \~russian Пурпурный фон */ = 0x800000,
		BackCyan	/*! \~english Cyan background \~russian Голубой фон */ = 0x1000000,
		BackWhite	/*! \~english White background \~russian Белый фон */ = 0x2000000,
		Default		/*! \~english Default format \~russian Формат по умолчанию */ = 0x4000000
	};

	typedef PIFlags<PICoutControl> PICoutControls;
}




//! \ingroup Core
//! \~\brief
//! \~english Universal output to console class.
//! \~russian Универсальный вывод в консоль.
class PIP_EXPORT PICout {
public:

	//! \~english Default constructor with default features (AddSpaces and AddNewLine)
	//! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine)
	PICout(int controls = PICoutManipulators::DefaultControls);

	//! \~english Construct with default features (AddSpaces and AddNewLine), but if \"active\" is false does nothing
	//! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine), но если не \"active\" то будет неактивным
	PICout(bool active);

	PICout(const PICout & other);

	~PICout();


	class PIP_EXPORT Notifier {
	public:
		//! \~english Singleton access to %PICout::Notifier
		//! \~russian Синглтон класса %PICout::Notifier
		static Notifier * instance();

		//! \~english Object that emit events from %PICout
		//! \~russian Объект, который посылает события от %PICout
		static PIObject * object();
	private:
		Notifier();
		PIObject * o;
	};

	//! \~english Enum contains output devices of %PICout
	//! \~russian Перечисление с устройствами вывода для %PICout
	enum OutputDevice {
		NoDevices  /** \~english %PICout is disabled \~russian %PICout неактивен */ = 0x0,
		StdOut     /** \~english Standard console output \~russian Стандартный вывод в консоль */ = 0x1,
		Buffer     /** \~english Internal buffer \~russian Внутренний буфер */ = 0x2,
		AllDevices /** \~english All \~russian Все */ = 0xFFFF,
	};

	typedef PIFlags<OutputDevice> OutputDevices;

	//! \~english Output operator for strings with <tt>"const char * "</tt> type
	//! \~russian Оператор вывода для строк <tt>"const char * "</tt>
	PICout & operator <<(const char * v);

	//! \~english Output operator for \a PIString
	//! \~russian Оператор вывода для \a PIString
	PICout & operator <<(const PIString & v);

	//! \~english Output operator for boolean values
	//! \~russian Оператор вывода для логических значений
	PICout & operator <<(bool v);

	//! \~english Output operator for <tt>"char"</tt> values
	//! \~russian Оператор вывода для <tt>"char"</tt> значений
	PICout & operator <<(char v);

	//! \~english Output operator for <tt>"unsigned char"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned char"</tt> значений
	PICout & operator <<(uchar v);

	//! \~english Output operator for <tt>"short"</tt> values
	//! \~russian Оператор вывода для <tt>"short"</tt> значений
	PICout & operator <<(short v);

	//! \~english Output operator for <tt>"unsigned short"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned short"</tt> значений
	PICout & operator <<(ushort v);

	//! \~english  Output operator for <tt>"int"</tt> values
	//! \~russian Оператор вывода для <tt>"int"</tt> значений
	PICout & operator <<(int v);

	//! \~english Output operator for <tt>"unsigned int"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned int"</tt> значений
	PICout & operator <<(uint v);

	//! \~english Output operator for <tt>"long"</tt> values
	//! \~russian Оператор вывода для <tt>"long"</tt> значений
	PICout & operator <<(long v);

	//! \~english Output operator for <tt>"unsigned long"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned long"</tt> значений
	PICout & operator <<(ulong v);

	//! \~english Output operator for <tt>"long long"</tt> values
	//! \~russian Оператор вывода для <tt>"long long"</tt> значений
	PICout & operator <<(llong v);

	//! \~english Output operator for <tt>"unsigned long long"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned long long"</tt> значений
	PICout & operator <<(ullong v);

	//! \~english Output operator for <tt>"float"</tt> values
	//! \~russian Оператор вывода для <tt>"float"</tt> значений
	PICout & operator <<(float v);

	//! \~english Output operator for <tt>"double"</tt> values
	//! \~russian Оператор вывода для <tt>"double"</tt> значений
	PICout & operator <<(double v);

	//! \~english Output operator for <tt>"ldouble"</tt> values
	//! \~russian Оператор вывода для <tt>"ldouble"</tt> значений
	PICout & operator <<(ldouble v);

	//! \~english Output operator for pointers
	//! \~russian Оператор вывода для указателей
	PICout & operator <<(const void * v);

	//! \~english Output operator for PIObject and ancestors
	//! \~russian Оператор вывода для PIObject и наследников
	PICout & operator <<(const PIObject * v);

	//! \~english Output operator for \a PICoutSpecialChar values
	//! \~russian Оператор вывода для \a PICoutSpecialChar
	PICout & operator <<(PICoutManipulators::PICoutSpecialChar v);

	//! \~english Output operator for \a PIFlags<PICoutFormat> values
	//! \~russian Оператор вывода для \a PIFlags<PICoutFormat>
	PICout & operator <<(PIFlags<PICoutManipulators::PICoutFormat> v);

	//! \~english Output operator for \a PICoutFormat values
	//! \~russian Оператор вывода для \a PICoutFormat
	PICout & operator <<(PICoutManipulators::PICoutFormat v);

	//! \~english Do some action
	//! \~russian Делает действие
	PICout & operator <<(PICoutManipulators::PICoutAction v);

	//! \~english Set control flag "c" is "on" state
	//! \~russian Установить флаг "c" в "on" состояние
	PICout & setControl(PICoutManipulators::PICoutControl c, bool on = true) {co_.setFlag(c, on); return *this;}

	//! \~english Set control flags "c"
	//! \~russian Установить флаги "c"
	PICout & setControls(PICoutManipulators::PICoutControls c) {co_ = c; return *this;}

	//! \~english Exec \a saveControls() and set control flags to "c"
	//! \~russian Иыполнить \a saveControls() и Установить флаги "c"
	PICout & saveAndSetControls(PICoutManipulators::PICoutControls c) {saveControls(); co_ = c; return *this;}

	//! \~english Save control flags to internal stack
	//! \~russian Сохраняет состояние флагов во внутренний стек
	//! \~\sa \a restoreControl()
	PICout & saveControls();

	//! \~english Restore control flags from internal stack
	//! \~russian Восстанавливает состояние флагов из внутреннего стека
	//! \~\sa \a saveControl()
	PICout & restoreControls();

	//! \~english Conditional put space character to output
	//! \~russian Условно добавляет пробел
	PICout & space();

	//! \~english Conditional put quote character to output
	//! \~russian Условно добавляет кавычки
	PICout & quote();

	//! \~english Conditional put new line character to output
	//! \~russian Условно добавляет новую строку
	PICout & newLine();

	//! \~english Write char
	//! \~russian Пишет символ
	PICout & write(char c);

	//! \~english Write raw data
	//! \~russian Пишет сырые символы
	PICout & write(const char * str);

	//! \~english Write raw data
	//! \~russian Пишет сырые символы
	PICout & write(const char * str, int len);

	//! \~english Write raw \a PIString
	//! \~russian Пишет сырой \a PIString
	PICout & write(const PIString & s);

	//! \~english Output \a PIString to stdout
	//! \~russian Вывод \a PIString в stdout
	static void stdoutPIString(const PIString & s);

	//! \~english Returns internal PIString buffer
	//! \~russian Возвращает внутренний PIString буфер
	static PIString getBuffer();

	//! \~english Returns internal PIString buffer and clear it
	//! \~russian Возвращает внутренний PIString буфер и очищает его
	static PIString getBufferAndClear();

	//! \~english Clear internal PIString buffer
	//! \~russian Очищает внутренний PIString буфер
	static void clearBuffer();


	//! \~english Set output to device "d" enabled "on". Returns if it was enabled
	//! \~russian Устройство вывода "d" устанавливается в "on". Возвращает было ли устройство активно
	static bool setOutputDevice(OutputDevice d, bool on = true);

	//! \~english Turn on output device "d". Returns if it was enabled
	//! \~russian Включает устройство вывода "d". Возвращает было ли устройство активно
	static bool enableOutputDevice(OutputDevice d) {return setOutputDevice(d, true);}

	//! \~english Turn off output device "d". Returns if it was enabled
	//! \~russian Выключает устройство вывода "d". Возвращает было ли устройство активно
	static bool disableOutputDevice(OutputDevice d) {return setOutputDevice(d, false);}

	//! \~english Set output to devices to "d"
	//! \~russian Устанавливает устройства вывода "d"
	static void setOutputDevices(OutputDevices d);

	//! \~english Returns current output devices
	//! \~russian Возвращает текущие устройства вывода
	static OutputDevices currentOutputDevices() {return devs;}

	//! \~english Returns if output device "d" is active
	//! \~russian Возвращает активно ли устройство вывода "d"
	static bool isOutputDeviceActive(OutputDevice d);


	//! \~english Construct with external buffer and ID "id". See \a Notifier for details
	//! \~russian Конструктор с внешним буфером и ID "id". Подробнее \a Notifier
	static PICout withExternalBuffer(PIString * buffer, int id = 0, PIFlags<PICoutManipulators::PICoutControl> controls = PICoutManipulators::AddSpaces | PICoutManipulators::AddNewLine);

	static PIMutex & __mutex__();
	static PIString & __string__();

private:
	void init();
	void applyFormat(PICoutManipulators::PICoutFormat f);

	static OutputDevices devs;
	PRIVATE_DECLARATION(PIP_EXPORT)
	bool fo_, cc_, fc_, act_;
	int cnb_, attr_, id_;
	PIString * buffer_;
	PICoutManipulators::PICoutControls co_;
};

#endif // PICOUT_H
