/*! \file pisystemmonitor.h
 * \ingroup System
 * \~\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 "pithread.h"
#include "pifile.h"


//! \ingroup System
//! \~\brief
//! \~english Process monitoring.
//! \~russian Мониторинг процесса.
class PIP_EXPORT PISystemMonitor: public PIThread {
	PIOBJECT_SUBCLASS(PISystemMonitor, PIThread);
	friend class PIIntrospectionServer;
public:
	//! \~english Constructs unassigned %PISystemMonitor
	//! \~russian Создает непривязанный %PISystemMonitor
	PISystemMonitor();

	~PISystemMonitor();

#pragma pack(push, 1)
	//! \ingroup System
	//! \~\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
		//! \~russian
		ullong ram_total = 0;

		//! \~english
		//! \~russian
		ullong ram_free = 0;

		//! \~english
		//! \~russian
		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 System
	//! \~\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 System
	//! \~\brief
	//! \~english Process statistics.
	//! \~russian Статистика процесса.
	struct PIP_EXPORT ProcessStats: ProcessStatsFixed {

		//! \~english Fill human-readable 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 System
	//! \~\brief
	//! \~english Thread statistics.
	//! \~russian Статистика потока.
	struct PIP_EXPORT ThreadStats: ThreadStatsFixed {

		//! \~english Name
		//! \~russian Имя
		PIString name;
	};

#ifndef MICRO_PIP

	//! \~english Starts monitoring of process with PID "pID" and update interval "interval_ms" milliseconds
	//! \~russian Начинает мониторинг процесса с PID "pID" и интервалом обновления "interval_ms" миллисекунд
	bool startOnProcess(int pID, int interval_ms = 1000);
#endif

	//! \~english Starts monitoring of application process with update interval "interval_ms" milliseconds
	//! \~russian Начинает мониторинг процесса приложения с интервалом обновления "interval_ms" миллисекунд
	bool startOnSelf(int interval_ms = 1000);

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

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

	//! \~english Returns monitoring process statistics
	//! \~russian Возвращает статистику наблюдаемого процесса
	ProcessStats statistic() const;

	//! \~english Returns monitoring process threads statistics
	//! \~russian Возвращает статистику потоков наблюдаемого процесса
	PIVector<ThreadStats> threadsStatistic() const;

	void setStatistic(const ProcessStats & s);


	//! \~english
	//! \~russian
	static ullong totalRAM();

	//! \~english
	//! \~russian
	static ullong freeRAM();

	//! \~english
	//! \~russian
	static ullong usedRAM();


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
