//! \addtogroup Core
//! \{
//! \~\file picout.h
//! \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(StdOut)
//! \~russian Макрос для условного (piDebug) вывода в PICout(StdOut)
#  define piCout

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

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

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

#else
#  define piCout PICout(piDebug, PICoutStdStream::StdOut)
#  define piCoutObj                                     \
	PICout(piDebug && debug(), PICoutStdStream::StdOut) \
		<< (PIStringAscii("[") + className() + (name().isEmpty() ? "]" : PIStringAscii(" \"") + name() + PIStringAscii("\"]")))
#  define piCerr PICout(piDebug, PICoutStdStream::StdErr)
#  define piCerrObj                                     \
	PICout(piDebug && debug(), PICoutStdStream::StdErr) \
		<< (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 Faint \~russian Тусклый */                                                                        = 0x20,
	Italic /*! \~english Italic \~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;

} // namespace PICoutManipulators


//! \~english Enum contains console streams
//! \~russian Перечисление с потоками консоли
enum class PICoutStdStream {
	StdOut /*! \~english Standard output stream \~russian Стандартный поток вывода */ = 0,
	StdErr /*! \~english Standard error stream \~russian Стандартный поток ошибок */  = 1,
};


//! \~\ingroup Core
//! \~\brief
//! \~english Universal output to console class.
//! \~russian Универсальный вывод в консоль.
//! \details
//! \~english This class provides a universal output interface with support for various data types, formatting options, and multiple output
//! devices (console, buffer).
//! \~russian Данный класс предоставляет универсальный интерфейс вывода с поддержкой различных типов данных, опций форматирования и
//! нескольких устройств вывода (консоль, буфер).
class PIP_EXPORT PICout {
public:
	//! \~english Default constructor with default features (AddSpaces and AddNewLine)
	//! \~russian Конструктор по умолчанию (AddSpaces и AddNewLine)
	PICout(int controls = PICoutManipulators::DefaultControls, PICoutStdStream stream = PICoutStdStream::StdOut);

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

	PICout(const PICout & other);

	~PICout();


	class PIP_EXPORT Notifier {
		friend class PICout;

	public:
		//! \~english Singleton access to %PICout::Notifier
		//! \~russian Синглтон класса %PICout::Notifier
		//! \details
		//! \~english Returns the singleton instance of the Notifier class used for emitting PICout events.
		//! \~russian Возвращает синглтон-экземпляр класса Notifier, используемого для посылки событий PICout.
		static Notifier * instance();

		//! \~english Object that emit events from %PICout
		//! \~russian Объект, который посылает события от %PICout
		//! \details
		//! \~english Returns the PIObject instance that emits events when PICout with external buffer is destroyed.
		//! \~russian Возвращает экземпляр PIObject, который посылает события при уничтожении PICout с внешним буфером.
		static PIObject * object();

	private:
		Notifier();
		NO_COPY_CLASS(Notifier)

		PIObject * o           = nullptr;
		std::atomic_int new_id = {1};
	};

	//! \~english Enum contains output devices of %PICout
	//! \~russian Перечисление с устройствами вывода для %PICout
	enum OutputDevice {
		NoDevices /** \~english %PICout is disabled \~russian %PICout неактивен */             = 0x0,
		Console /** \~english Standard console output \~russian Стандартный вывод в консоль */ = 0x1,
		StdOut DEPRECATEDM("use PICout::Console")                                              = Console,
		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> значений
	//! \details
	//! \~english Outputs an integer value to the configured stream with optional format (binary, octal, decimal, hexadecimal).
	//! \~russian Выводит целочисленное значение в настроенный поток с опциональным форматированием (binary, octal, decimal, hexadecimal).
	PICout & operator<<(int v);

	//! \~english Output operator for <tt>"unsigned int"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned int"</tt> значений
	//! \details
	//! \~english Outputs an unsigned integer value to the configured stream with optional format (binary, octal, decimal, hexadecimal).
	//! \~russian Выводит беззнаковое целочисленное значение в настроенный поток с опциональным форматированием (binary, octal, decimal,
	//! hexadecimal).
	PICout & operator<<(uint v);

	//! \~english Output operator for <tt>"long"</tt> values
	//! \~russian Оператор вывода для <tt>"long"</tt> значений
	//! \details
	//! \~english Outputs a long integer value to the configured stream with optional format (binary, octal, decimal, hexadecimal).
	//! \~russian Выводит длинное целочисленное значение в настроенный поток с опциональным форматированием (binary, octal, decimal,
	//! hexadecimal).
	PICout & operator<<(long v);

	//! \~english Output operator for <tt>"unsigned long"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned long"</tt> значений
	//! \details
	//! \~english Outputs an unsigned long integer value to the configured stream with optional format (binary, octal, decimal,
	//! hexadecimal).
	//! \~russian Выводит беззнаковое длинное целочисленное значение в настроенный поток с опциональным форматированием (binary, octal,
	//! decimal, hexadecimal).
	PICout & operator<<(ulong v);

	//! \~english Output operator for <tt>"long long"</tt> values
	//! \~russian Оператор вывода для <tt>"long long"</tt> значений
	//! \details
	//! \~english Outputs a long long integer value to the configured stream with optional format (binary, octal, decimal, hexadecimal).
	//! \~russian Выводит длинное long long целочисленное значение в настроенный поток с опциональным форматированием (binary, octal,
	//! decimal, hexadecimal).
	PICout & operator<<(llong v);

	//! \~english Output operator for <tt>"unsigned long long"</tt> values
	//! \~russian Оператор вывода для <tt>"unsigned long long"</tt> значений
	//! \details
	//! \~english Outputs an unsigned long long integer value to the configured stream with optional format (binary, octal, decimal,
	//! hexadecimal).
	//! \~russian Выводит беззнаковое длинное long long целочисленное значение в настроенный поток с опциональным форматированием (binary,
	//! octal, decimal, hexadecimal).
	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 Оператор вывода для указателей
	//! \details
	//! \~english Outputs a pointer address to the configured stream in hexadecimal format.
	//! \~russian Выводит адрес указателя в настроенный поток в шестнадцатеричном формате.
	PICout & operator<<(const void * v);

	//! \~english Output operator for PIObject and ancestors
	//! \~russian Оператор вывода для PIObject и наследников
	//! \details
	//! \~english Outputs a PIObject or its descendants to the configured stream, including class name and object name.
	//! \~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>
	//! \details
	//! \~english Sets output format flags (binary, octal, decimal, hexadecimal, bold, colors) for subsequent integer output.
	//! \~russian Устанавливает флаги форматирования вывода (binary, octal, decimal, hexadecimal, bold, colors) для последующего вывода
	//! целых чисел.
	PICout & operator<<(PIFlags<PICoutManipulators::PICoutFormat> v);

	//! \~english Output operator for \a PICoutFormat values
	//! \~russian Оператор вывода для \a PICoutFormat
	//! \details
	//! \~english Sets output format flag (binary, octal, decimal, hexadecimal, bold, colors) for subsequent integer output.
	//! \~russian Устанавливает флаг форматирования вывода (binary, octal, decimal, hexadecimal, bold, colors) для последующего вывода целых
	//! чисел.
	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);

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

	//! \~english Exec \a saveControls() and set control flags to "c"
	//! \~russian Выполнить \a saveControls() и установить флаги "c"
	PICout & saveAndSetControls(PICoutManipulators::PICoutControls c);

	//! \~english Save control flags to internal stack
	//! \~russian Сохраняет состояние флагов во внутренний стек
	//! \details
	//! \~english Saves the current control flags to an internal stack for later restoration using restoreControls().
	//! \~russian Сохраняет текущие флаги управления во внутренний стек для последующего восстановления с помощью restoreControls().
	//! \~\sa \a restoreControls()
	PICout & saveControls();

	//! \~english Restore control flags from internal stack
	//! \~russian Восстанавливает состояние флагов из внутреннего стека
	//! \details
	//! \~english Restores the control flags from the internal stack that were previously saved using saveControls().
	//! \~russian Восстанавливает флаги управления из внутреннего стека, которые были ранее сохранены с помощью saveControls().
	//! \~\sa \a saveControls()
	PICout & restoreControls();

	//! \~english Conditional put space character to output
	//! \~russian Условно добавляет пробел
	//! \details
	//! \~english Conditionally adds a space character to the output if the AddSpaces control flag is enabled.
	//! \~russian Условно добавляет пробел в вывод, если включен флаг управления AddSpaces.
	PICout & space();

	//! \~english Conditional put quote character to output
	//! \~russian Условно добавляет кавычки
	//! \details
	//! \~english Conditionally adds quote characters to the output if the AddQuotes control flag is enabled.
	//! \~russian Условно добавляет кавычки в вывод, если включен флаг управления AddQuotes.
	PICout & quote();

	//! \~english Conditional put new line character to output
	//! \~russian Условно добавляет новую строку
	//! \details
	//! \~english Conditionally adds a newline character to the output if the AddNewLine control flag is enabled.
	//! \~russian Условно добавляет символ новой строки в вывод, если включен флаг управления AddNewLine.
	PICout & newLine();

	//! \~english Write char
	//! \~russian Пишет символ
	//! \details
	//! \~english Writes a single character directly to the output stream without any formatting.
	//! \~russian Записывает один символ непосредственно в поток вывода без какого-либо форматирования.
	PICout & write(char c);

	//! \~english Write raw data
	//! \~russian Пишет сырые символы
	//! \details
	//! \~english Writes raw C-style string data directly to the output stream without any formatting.
	//! \~russian Записывает сырые данные C-строки непосредственно в поток вывода без какого-либо форматирования.
	PICout & write(const char * str);

	//! \~english Write raw data
	//! \~russian Пишет сырые символы
	//! \details
	//! \~english Writes raw data of specified length directly to the output stream without any formatting.
	//! \~russian Записывает сырые данные указанной длины непосредственно в поток вывода без какого-либо форматирования.
	PICout & write(const char * str, int len);

	//! \~english Write raw \a PIString
	//! \~russian Пишет сырой \a PIString
	//! \details
	//! \~english Writes raw PIString data directly to the output stream without any formatting.
	//! \~russian Записывает сырые данные PIString непосредственно в поток вывода без какого-либо форматирования.
	PICout & write(const PIString & s);

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

	//! \~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.
	//! \~russian Конструктор с внешним буфером.
	//! \details
	//! \~english Creates a PICout instance that outputs to an external PIString buffer with specified control flags.
	//! \~russian Создает экземпляр PICout, который выводит данные во внешний буфер PIString с указанными флагами управления.
	static PICout withExternalBuffer(PIString * buffer,
	                                 PIFlags<PICoutManipulators::PICoutControl> controls = PICoutManipulators::AddSpaces);

	//! \~english Construct with external buffer and ID "id". See \a Notifier for details
	//! \~russian Конструктор с внешним буфером и ID "id". Подробнее \a Notifier
	//! \details
	//! \~english Creates a PICout instance with external buffer and notification ID. When the last copy is destroyed, it emits a finished()
	//! event via Notifier.
	//! \~russian Создает экземпляр PICout с внешним буфером и идентификатором уведомления. При уничтожении последней копии посылается
	//! событие finished() через Notifier.
	static PICout withExternalBufferAndID(PIString * buffer,
	                                      int id,
	                                      PIFlags<PICoutManipulators::PICoutControl> controls = PICoutManipulators::DefaultControls);

	//! \~english Returns unique external buffer ID for later use in \a withExternalBufferAndID()
	//! \~russian Возвращает уникальный ID для внешнего буфера для дальнейшего использования в \a withExternalBufferAndID()
	static int registerExternalBufferID();

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

private:
	void init();
	void applyFormat(PICoutManipulators::PICoutFormat f);
	void writeChar(char c);

	static OutputDevices devs;
	PRIVATE_DECLARATION(PIP_EXPORT)
	bool first_out_ = true, is_copy_ = false, format_changed_ = false, actve_ = true;
	int int_base_ = 10, win_attr_ = 0, id_ = -1;
	PIString * buffer_                       = nullptr;
	PICoutManipulators::PICoutControls ctrl_ = PICoutManipulators::DefaultControls;
	PICoutStdStream stream_                  = PICoutStdStream::StdOut;
};

#endif // PICOUT_H
