/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#ifndef _WINDOWS

#include "diskdevices.h"

#include "stats.h"

static void	apply_diskstat(zbx_single_diskdevice_data *device, time_t now, zbx_uint64_t *dstat)
{
	register int	i;
	time_t		clock[ZBX_AVG_COUNT], sec;
	int		index[ZBX_AVG_COUNT];

	assert(device);

	device->index++;

	if (ZBX_MAX_COLLECTOR_HISTORY == device->index)
		device->index = 0;

	device->clock[device->index] = now;
	device->r_sect[device->index] = dstat[ZBX_DSTAT_R_SECT];
	device->r_oper[device->index] = dstat[ZBX_DSTAT_R_OPER];
	device->r_byte[device->index] = dstat[ZBX_DSTAT_R_BYTE];
	device->w_sect[device->index] = dstat[ZBX_DSTAT_W_SECT];
	device->w_oper[device->index] = dstat[ZBX_DSTAT_W_OPER];
	device->w_byte[device->index] = dstat[ZBX_DSTAT_W_BYTE];

	clock[ZBX_AVG1] = clock[ZBX_AVG5] = clock[ZBX_AVG15] = now + 1;
	index[ZBX_AVG1] = index[ZBX_AVG5] = index[ZBX_AVG15] = -1;

	for (i = 0; i < ZBX_MAX_COLLECTOR_HISTORY; i++)
	{
		if (0 == device->clock[i])
			continue;

#define DISKSTAT(t)											\
	do												\
	{												\
		if ((device->clock[i] >= (now - (t * 60))) && (clock[ZBX_AVG ## t] > device->clock[i]))	\
		{											\
			clock[ZBX_AVG ## t] = device->clock[i];						\
			index[ZBX_AVG ## t] = i;							\
		}											\
	}												\
	while(0)

		DISKSTAT(1);
		DISKSTAT(5);
		DISKSTAT(15);
	}

#define SAVE_DISKSTAT(t)\
	do											\
	{											\
		if (-1 == index[ZBX_AVG ## t] || 0 == now - device->clock[index[ZBX_AVG ## t]])	\
		{										\
			device->r_sps[ZBX_AVG ## t] = 0;					\
			device->r_ops[ZBX_AVG ## t] = 0;					\
			device->r_bps[ZBX_AVG ## t] = 0;					\
			device->w_sps[ZBX_AVG ## t] = 0;					\
			device->w_ops[ZBX_AVG ## t] = 0;					\
			device->w_bps[ZBX_AVG ## t] = 0;					\
		}										\
		else										\
		{										\
			sec = now - device->clock[index[ZBX_AVG ## t]];				\
			device->r_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_SECT] - 		\
					device->r_sect[index[ZBX_AVG ## t]]) / (double)sec;	\
			device->r_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_OPER] - 		\
					device->r_oper[index[ZBX_AVG ## t]]) / (double)sec;	\
			device->r_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_R_BYTE] - 		\
					device->r_byte[index[ZBX_AVG ## t]]) / (double)sec;	\
			device->w_sps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_SECT] - 		\
					device->w_sect[index[ZBX_AVG ## t]]) / (double)sec;	\
			device->w_ops[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_OPER] - 		\
					device->w_oper[index[ZBX_AVG ## t]]) / (double)sec;	\
			device->w_bps[ZBX_AVG ## t] = (dstat[ZBX_DSTAT_W_BYTE] - 		\
					device->w_byte[index[ZBX_AVG ## t]]) / (double)sec;	\
		}										\
	}											\
	while(0)

	SAVE_DISKSTAT(1);
	SAVE_DISKSTAT(5);
	SAVE_DISKSTAT(15);
}

static void	process_diskstat(zbx_single_diskdevice_data *device)
{
	time_t		now = time(NULL);
	zbx_uint64_t	dstat[ZBX_DSTAT_MAX];

	if (FAIL == zbx_get_diskstat(device->name, dstat))
		return;

	apply_diskstat(device, now, dstat);

	device->ticks_since_polled++;
}

void	collect_stats_diskdevices(void)
{
	stats_lock_diskstats();
	diskstat_shm_reattach();

	zbx_diskdevices_data	*diskdevices = get_diskdevices();

	for (int i = 0; i < diskdevices->count; i++)
	{
		process_diskstat(&diskdevices->device[i]);

		/* remove device from collector if not being polled for long time */
		if (DISKDEVICE_TTL <= diskdevices->device[i].ticks_since_polled)
		{
			if ((diskdevices->count - 1) > i)
			{
				memcpy(diskdevices->device + i, diskdevices->device + i + 1,
					sizeof(zbx_single_diskdevice_data) * (diskdevices->count - i));
			}

			diskdevices->count--;
			i--;
		}
	}

	stats_unlock_diskstats();
}

zbx_single_diskdevice_data	*collector_diskdevice_get(const char *devname)
{
	zbx_single_diskdevice_data	*device = NULL;

	assert(devname);

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() devname:'%s'", __func__, devname);

	stats_lock_diskstats();

	if (0 == diskdevice_collector_started())
		diskstat_shm_init();
	else
		diskstat_shm_reattach();

	zbx_diskdevices_data	*diskdevices = get_diskdevices();

	for (int i = 0; i < diskdevices->count; i++)
	{
		if (0 == strcmp(devname, diskdevices->device[i].name))
		{
			device = &diskdevices->device[i];
			device->ticks_since_polled = 0;
			zabbix_log(LOG_LEVEL_DEBUG, "%s() device '%s' found", __func__, devname);
			break;
		}
	}
	stats_unlock_diskstats();

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __func__, (void *)device);

	return device;
}

zbx_single_diskdevice_data	*collector_diskdevice_add(const char *devname)
{
	zbx_single_diskdevice_data	*device = NULL;

	assert(devname);

	zabbix_log(LOG_LEVEL_DEBUG, "In %s() devname:'%s'", __func__, devname);

	stats_lock_diskstats();

	if (0 == diskdevice_collector_started())
		diskstat_shm_init();
	else
		diskstat_shm_reattach();

	zbx_diskdevices_data	*diskdevices = get_diskdevices();

	if (diskdevices->count == MAX_DISKDEVICES)
	{
		zabbix_log(LOG_LEVEL_DEBUG, "%s() collector is full", __func__);
		goto end;
	}

	if (diskdevices->count == diskdevices->max_diskdev)
	{
		diskstat_shm_extend();
		diskdevices = get_diskdevices();
	}

	device = &(diskdevices->device[diskdevices->count]);
	memset(device, 0, sizeof(zbx_single_diskdevice_data));
	zbx_strlcpy(device->name, devname, sizeof(device->name));
	device->index = -1;
	device->ticks_since_polled = 0;
	(diskdevices->count)++;

	process_diskstat(device);
end:
	stats_unlock_diskstats();

	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __func__, (void *)device);

	return device;
}

#endif	/* _WINDOWS */