//! \~\file pilog.h
//! \~\ingroup Application
//! \~\brief
//! \~english High-level log
//! \~russian Высокоуровневый лог
/*
    PIP - Platform Independent Primitives
    High-level log
    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 PIlog_H
#define PIlog_H

#include "pifile.h"
#include "piiostream.h"
#include "pithread.h"

//! \~\ingroup Application
//! \~\brief
//! \~english High-level log
//! \~russian Высокоуровневый лог
//! \~\details
//! \~english Provides log with optional file and console output, with configurable timestamp format and line format.
//! \~russian Предоставляет лог с опциональным выводом в файл и консоль, с настраиваемым форматом метки времени и форматом строки.
class PIP_EXPORT PILog: public PIThread {
	PIOBJECT_SUBCLASS(PILog, PIThread)

public:
	//! \~english Constructs log with console output, timestamped lines and rotated log files.
	//! \~russian Создает лог с выводом в консоль, строками с метками времени и ротацией файлов.
	PILog();

	//! \~english Stops logging thread and flushes queued messages.
	//! \~russian Останавливает поток логирования и дописывает сообщения из очереди.
	~PILog();

	//! \~english Message category
	//! \~russian Категория сообщения
	enum class Level {
		Error /** \~english Error   \~russian Ошибка */,
		Warning /** \~english Warning \~russian Предупреждение */,
		Info /** \~english Information    \~russian Информация */,
		Debug /** \~english Debug   \~russian Отладка */,
	};

	//! \~english Output channel
	//! \~russian Канал вывода
	enum Output {
		File /** \~english File \~russian Файл */          = 0x1,
		Console /** \~english Console \~russian Консоль */ = 0x2,
		All /** \~english All \~russian Все */             = 0xFF,
	};

	//! \~english Enables or disables output channel "o".
	//! \~russian Включает или выключает канал вывода "o".
	void setOutput(Output o, bool on = true) { output.setFlag(o, on); }

	//! \~english Returns prefix for filename.
	//! \~russian Возвращает префикс имени файла.
	PIString logName() const { return log_name; }

	//! \~english Set prefix for filename. Should be set \b before \a setDir()!
	//! \~russian Устанавливает префикс имени файла. Должен быть установлен \b до вызова \a setDir()!
	void setLogName(const PIString & n) { log_name = n; }

	//! \~english Returns if color for console output enabled.
	//! \~russian Возвращает использовать ли цвет для вывода в консоль.
	bool colorConsole() const { return color_console; }

	//! \~english Set color for console output enabled. True by default.
	//! \~russian Устанавливает использовать ли цвет для вывода в консоль. Включено по умолчанию.
	void setColorConsole(bool yes) { color_console = yes; }


	//! \~english Returns directory for log files.
	//! \~russian Возвращает директорию файлов лога.
	PIString dir() const { return log_dir; }

	//! \~english Set directory for log files. Should be set \b after \a setLogName()!
	//! \~russian Устанавливает директорию для файлов. Должна быть установлена \b после вызова \a setLogName()!
	void setDir(const PIString & d);


	//! \~english Returns lifetime for file.
	//! \~russian Возвращает время жизни файла.
	PISystemTime fileSplitTime() const { return split_time; }

	//! \~english Sets log file rotation interval. A new file is created every "st".
	//! \~russian Устанавливает интервал ротации файла лога. Новый файл создается каждые "st".
	void setFileSplitTime(PISystemTime st) { split_time = st; }


	//! \~english Returns timestamp format for line.
	//! \~russian Возвращает формат метки времени для строки.
	PIString timestampFormat() const { return timestamp_format; }

	//! \~english Set timestamp format for line. Default is "yyyy-MM-dd hh:mm:ss.zzz".
	//! \~russian Устанавливает формат метки времени для строки. По умолчанию "yyyy-MM-dd hh:mm:ss.zzz".
	void setTimestampFormat(const PIString & f) { timestamp_format = f; }


	//! \~english Returns line format.
	//! \~russian Возвращает формат строки.
	PIString lineFormat() const { return line_format; }

	//! \~english Sets line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m".
	//! \~russian Устанавливает формат строки. "t" - метка времени, "c" - категория, "m" - сообщение. По умолчанию "t - c: m".
	void setLineFormat(const PIString & f);


	//! \~english Returns maximum level.
	//! \~russian Возвращает максимальную категорию.
	Level level() const { return max_level; }

	//! \~english Sets maximum accepted level. Messages above "l" are ignored. Default is \a Level::Debug.
	//! \~russian Устанавливает максимальный принимаемый уровень. Сообщения выше "l" игнорируются. По умолчанию \a Level::Debug.
	void setLevel(Level l);

	//! \~english Returns \a PICout for \a Level::Error level.
	//! \~russian Возвращает \a PICout для категории \a Level::Error.
	PICout error(PIObject * context = nullptr);

	//! \~english Returns \a PICout for \a Level::Warning level.
	//! \~russian Возвращает \a PICout для категории \a Level::Warning.
	PICout warning(PIObject * context = nullptr);

	//! \~english Returns \a PICout for \a Level::Info level.
	//! \~russian Возвращает \a PICout для категории \a Level::Info.
	PICout info(PIObject * context = nullptr);

	//! \~english Returns \a PICout for \a Level::Debug level.
	//! \~russian Возвращает \a PICout для категории \a Level::Debug.
	PICout debug(PIObject * context = nullptr);

	//! \~english Writes all queued lines and stops logging. Also called from destructor.
	//! \~russian Записывает все строки из очереди и останавливает логирование. Также вызывается из деструктора.
	void stop();

	//! \~english Reads all rotated and current log lines and returns them as %PIStringList.
	//! \~russian Читает строки из текущего и уже ротированных логов и возвращает их как %PIStringList.
	PIStringList readAllLogs() const;

private:
	EVENT_HANDLER2(void, coutDone, int, id, PIString *, buff);

	PICout makePICout(PIObject * context, Level cat);
	void enqueue(const PIString & msg, Level cat = Level::Debug);

	struct Entry {
		Level cat;
		PIDateTime time;
		PIString msg;
	};

	PIString entryToString(const Entry & e) const;
	void newFile();
	void run() override;

	mutable PIMutex log_mutex;
	mutable PIFile log_file;
	PIIOTextStream log_ts;
	PITimeMeasurer split_tm;
	PISystemTime split_time;
	PIString log_dir, timestamp_format, line_format, line_format_p, log_name;
	PIQueue<Entry> queue;
	PIMap<Level, int> id_by_cat;
	Level max_level        = Level::Debug;
	PIFlags<Output> output = All;
	bool color_console     = true;
	int part_number = -1, cout_id = -1;
};

#endif
