//! \~\file pisystemmonitor.h
//! \~\ingroup Application
//! \brief
//! \~english System resources monitoring
//! \~russian Мониторинг ресурсов системы
/*
    PIP - Platform Independent Primitives
    Process resource monitor
    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 PISYSTEMMONITOR_H
#define PISYSTEMMONITOR_H

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


//! \~\ingroup Application
//! \~\brief
//! \~english Process and thread resource monitoring.
//! \~russian Мониторинг ресурсов процесса и его потоков.
//! \details
//! \~english This module provides process and system resource monitoring capabilities including CPU usage, memory consumption, and thread
//! statistics. It supports cross-platform monitoring on Linux, Windows, macOS, and ESP platforms.
//! \~russian Этот модуль предоставляет возможности мониторинга ресурсов процессов и системы, включая использование CPU, потребление памяти
//! и статистику потоков. Поддерживается кроссплатформенный мониторинг на Linux, Windows, macOS и ESP платформах.
class PIP_EXPORT PISystemMonitor: public PIThread {
	PIOBJECT_SUBCLASS(PISystemMonitor, PIThread);
	friend class PIIntrospectionServer;

public:
	//! \~english Constructs unassigned %PISystemMonitor.
	//! \~russian Создает непривязанный %PISystemMonitor.
	PISystemMonitor();

	//! \~english Stops monitoring and detaches from the current process target.
	//! \~russian Останавливает мониторинг и отсоединяет объект от текущей цели.
	~PISystemMonitor();

#pragma pack(push, 1)
	//! \~\ingroup Application
	//! \~\brief
	//! \~english Process statistics (fixed-size fields).
	//! \~russian Статистика процесса (фиксированные поля).
	struct PIP_EXPORT ProcessStatsFixed {
		//! \~english PID
		//! \~russian PID
		int ID                  = 0;

		//! \~english Parent PID
		//! \~russian PID родителя
		int parent_ID           = 0;

		//! \~english Group ID
		//! \~russian ID группы
		int group_ID            = 0;

		//! \~english Session ID
		//! \~russian ID сессии
		int session_ID          = 0;

		//! \~english Priority
		//! \~russian Приоритет
		int priority            = 0;

		//! \~english Threads count
		//! \~russian Количество потоков
		int threads             = 0;

		//! \~english Physical memory in bytes
		//! \~russian Физическая память в байтах
		ullong physical_memsize = 0;

		//! \~english Resident memory in bytes
		//! \~russian Резидентная память в байтах
		ullong resident_memsize = 0;

		//! \~english Share memory in bytes
		//! \~russian Разделяемая память в байтах
		ullong share_memsize    = 0;

		//! \~english Virtual memory in bytes
		//! \~russian Виртуальная память в байтах
		ullong virtual_memsize  = 0;

		//! \~english Data memory in bytes
		//! \~russian Память данных в байтах
		ullong data_memsize     = 0;

		//! \~english Total RAM in bytes.
		//! \~russian Общий объем RAM в байтах.
		ullong ram_total        = 0;

		//! \~english Free RAM in bytes.
		//! \~russian Свободный объем RAM в байтах.
		ullong ram_free         = 0;

		//! \~english Used RAM in bytes.
		//! \~russian Используемый объем RAM в байтах.
		ullong ram_used         = 0;

		//! \~english CPU load in kernel space
		//! \~russian Загрузка CPU в пространстве ядра
		float cpu_load_system   = 0.f;

		//! \~english CPU load in user space
		//! \~russian Загрузка CPU в пространстве пользователя
		float cpu_load_user     = 0.f;
	};

	//! \~\ingroup Application
	//! \~\brief
	//! \~english Thread statistics (fixed-size fields).
	//! \~russian Статистика потока (фиксированные поля).
	struct PIP_EXPORT ThreadStatsFixed {
		//! \~english TID
		//! \~russian TID
		llong id = 0;

		//! \~english Overall live time
		//! \~russian Полное время жизни
		PISystemTime work_time;

		//! \~english Busy time in kernel space
		//! \~russian Время работы в пространстве ядра
		PISystemTime kernel_time;

		//! \~english Busy time in user space
		//! \~russian Время работы в пространстве пользователя
		PISystemTime user_time;

		//! \~english CPU load in kernel space
		//! \~russian Загрузка CPU в пространстве ядра
		float cpu_load_kernel = -1.f;

		//! \~english CPU load in user space
		//! \~russian Загрузка CPU в пространстве пользователя
		float cpu_load_user   = -1.f;

		//! \~english Date and time of creation
		//! \~russian Дата и время создания
		PIDateTime created;
	};
#pragma pack(pop)

	//! \~\ingroup Application
	//! \~\brief
	//! \~english Process statistics.
	//! \~russian Статистика процесса.
	struct PIP_EXPORT ProcessStats: ProcessStatsFixed {
		//! \~english Fills human-readable memory size fields.
		//! \~russian Заполняет поля с человекочитаемыми размерами памяти.
		void makeStrings();

		//! \~english Execution command
		//! \~russian Команда запуска
		PIString exec_name;

		//! \~english State
		//! \~russian Состояние
		PIString state;

		//! \~english Human-readable physical memory
		//! \~russian Физическая память в читаемом виде
		PIString physical_memsize_readable;

		//! \~english Human-readable resident memory
		//! \~russian Резидентная память в читаемом виде
		PIString resident_memsize_readable;

		//! \~english Human-readable share memory
		//! \~russian Разделяемая память в читаемом виде
		PIString share_memsize_readable;

		//! \~english Human-readable virtual memory
		//! \~russian Виртуальная память в читаемом виде
		PIString virtual_memsize_readable;

		//! \~english Human-readable data memory
		//! \~russian Память данных в читаемом виде
		PIString data_memsize_readable;
	};

	//! \~\ingroup Application
	//! \~\brief
	//! \~english Thread statistics.
	//! \~russian Статистика потока.
	struct PIP_EXPORT ThreadStats: ThreadStatsFixed {
		//! \~english Name
		//! \~russian Имя
		PIString name;
	};

#ifndef MICRO_PIP

	//! \~english Starts monitoring the process with PID "pID" using the given update interval.
	//! \~russian Запускает мониторинг процесса с PID "pID" с указанным интервалом обновления.
	bool startOnProcess(int pID, PISystemTime interval = PISystemTime::fromSeconds(1.));
#endif

	//! \~english Starts monitoring the current application process.
	//! \~russian Запускает мониторинг текущего процесса приложения.
	bool startOnSelf(PISystemTime interval = PISystemTime::fromSeconds(1.));

	//! \~english Stops monitoring.
	//! \~russian Останавливает мониторинг.
	void stop();


	//! \~english Returns PID of the monitored process.
	//! \~russian Возвращает PID наблюдаемого процесса.
	int pID() const { return pID_; }

	//! \~english Returns latest process statistics snapshot.
	//! \~russian Возвращает последний снимок статистики процесса.
	ProcessStats statistic() const;

	//! \~english Returns latest per-thread statistics snapshot.
	//! \~russian Возвращает последний снимок статистики по потокам.
	PIVector<ThreadStats> threadsStatistic() const;

	//! \~english Replaces current process statistics with external data.
	//! \~russian Заменяет текущую статистику процесса внешними данными.
	void setStatistic(const ProcessStats & s);


	//! \~english Returns total RAM in bytes on supported platforms.
	//! \~russian Возвращает общий объем RAM в байтах на поддерживаемых платформах.
	static ullong totalRAM();

	//! \~english Returns free RAM in bytes on supported platforms.
	//! \~russian Возвращает свободный объем RAM в байтах на поддерживаемых платформах.
	static ullong freeRAM();

	//! \~english Returns used RAM in bytes on supported platforms.
	//! \~russian Возвращает используемый объем RAM в байтах на поддерживаемых платформах.
	static ullong usedRAM();

	//! \~\events
	//! \~\{

	//! \~\fn void measured()
	//! \~english Raised after a new statistics snapshot is measured.
	//! \~russian Вызывается после измерения нового снимка статисти
	EVENT(measured);

	//! \~\}

private:
	void run() override;
	void gatherThread(llong id);
	float calcThreadUsage(PISystemTime & t_new, PISystemTime & t_old);

	ProcessStats stat;
	PIVector<ThreadStats> cur_ts;
	PIMap<llong, ThreadStats> last_tm, cur_tm;
	PIMap<llong, PIString> tbid;
	mutable PIMutex stat_mutex;
	int pID_, page_size, cpu_count, cycle;
#ifndef MICRO_PIP
	PRIVATE_DECLARATION(PIP_EXPORT)
#endif

	class PIP_EXPORT Pool {
		friend class PISystemMonitor;

	public:
		static Pool * instance();
		PISystemMonitor * getByPID(int pID);

	private:
		void add(PISystemMonitor * sm);
		void remove(PISystemMonitor * sm);
		PIMap<int, PISystemMonitor *> sysmons;
		PIMutex mutex;
	};
};

//! \relatesalso PICout
//! \~english Output operator to \a PICout
//! \~russian Оператор вывода в \a PICout
inline PICout operator<<(PICout s, const PISystemMonitor::ThreadStats & v) {
	s.saveAndSetControls(0);
	s << "ThreadInfo(\"" << v.name << "\", created " << v.created << ", work " << v.work_time.toMilliseconds() << " ms"
	  << ", kernel " << v.kernel_time.toMilliseconds() << " ms"
	  << ", user " << v.user_time.toMilliseconds() << " ms"
	  << ")\n";
	s.restoreControls();
	return s;
}


//! \relatesalso PIBinaryStream
//! \~english Store operator.
//! \~russian Оператор сохранения.
BINARY_STREAM_WRITE(PISystemMonitor::ProcessStats) {
	s << PIMemoryBlock(&v, sizeof(PISystemMonitor::ProcessStatsFixed)) << v.exec_name << v.state;
	return s;
}

//! \relatesalso PIBinaryStream
//! \~english Restore operator.
//! \~russian Оператор извлечения.
BINARY_STREAM_READ(PISystemMonitor::ProcessStats) {
	s >> PIMemoryBlock(&v, sizeof(PISystemMonitor::ProcessStatsFixed)) >> v.exec_name >> v.state;
	v.makeStrings();
	return s;
}

//! \relatesalso PIBinaryStream
//! \~english Store operator.
//! \~russian Оператор сохранения.
BINARY_STREAM_WRITE(PISystemMonitor::ThreadStats) {
	s << PIMemoryBlock(&v, sizeof(PISystemMonitor::ThreadStatsFixed)) << v.name;
	return s;
}

//! \relatesalso PIBinaryStream
//! \~english Restore operator.
//! \~russian Оператор извлечения.
BINARY_STREAM_READ(PISystemMonitor::ThreadStats) {
	s >> PIMemoryBlock(&v, sizeof(PISystemMonitor::ThreadStatsFixed)) >> v.name;
	return s;
}

#endif // PISYSTEMMONITOR_H
