/*
 * Copyright 2010, Intel Corporation
 *
 * This file is part of PowerTOP
 *
 * This program file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; version 2 of the License.
 *
 * 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 General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program in a file named COPYING; if not, write to the
 * Free Software Foundation, Inc,
 * 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 * or just google for it.
 *
 * Authors:
 *	Arjan van de Ven <arjan@linux.intel.com>
 */
#include <iostream>
#include <fstream>

#include <stdio.h>
#include <limits.h>


#include "device.h"
#include "report/report.h"
#include "report/report-maker.h"
#include "ahci.h"
#include "../parameters/parameters.h"
#include "../lib.h"
#include "report/report-data-html.h"
#include <format>

std::vector <class ahci *> links;

static std::string disk_name(const std::string &path, const std::string &target, [[maybe_unused]] const std::string &shortname)
{
	std::string diskname = "";
	std::string pathname = std::format("{}/{}", path, target);

	for (const auto &entry : list_directory(pathname)) {
		if (entry.find(':') == std::string::npos)
			continue;

		diskname = read_sysfs_string(std::format("{}/{}/model", pathname, entry));
		if (!diskname.empty())
			break;
	}

	return diskname;
}

static std::string model_name(const std::string &path, const std::string &shortname)
{
	std::string pathname = std::format("{}/device", path);

	for (const auto &entry : list_directory(pathname)) {
		if (entry.find(':') == std::string::npos)
			continue;
		if (entry.find("target") == std::string::npos)
			continue;
		return disk_name(pathname, entry, shortname);
	}

	return "";
}

ahci::ahci(const std::string &_name, const std::string &path): device()
{
	std::string diskname;

	end_active = 0;
	end_slumber = 0;
	end_devslp = 0;
	end_partial = 0;
	start_active = 0;
	start_slumber = 0;
	start_devslp = 0;
	start_partial = 0;
	sysfs_path = path;

	register_sysfs_path(sysfs_path);

	name = std::format("ahci:{}", _name);
	active_index = get_param_index("ahci-link-power-active");
	partial_index = get_param_index("ahci-link-power-partial");

	active_rindex = get_result_index(std::format("{}-active", name));
	partial_rindex = get_result_index(std::format("{}-partial", name));
	slumber_rindex = get_result_index(std::format("{}-slumber", name));
	devslp_rindex = get_result_index(std::format("{}-devslp", name));

	diskname = model_name(sysfs_path, _name);

	if (diskname.empty())
		humanname = pt_format(_("SATA link: {}"), _name);
	else
		humanname = pt_format(_("SATA disk: {}"), diskname);
}

void ahci::start_measurement(void)
{
	start_active = read_sysfs(std::format("{}/ahci_alpm_active", sysfs_path));
	start_partial = read_sysfs(std::format("{}/ahci_alpm_partial", sysfs_path));
	start_slumber = read_sysfs(std::format("{}/ahci_alpm_slumber", sysfs_path));
	start_devslp = read_sysfs(std::format("{}/ahci_alpm_devslp", sysfs_path));
}

void ahci::end_measurement(void)
{
	double p;
	double total;

	end_active = read_sysfs(std::format("{}/ahci_alpm_active", sysfs_path));
	end_partial = read_sysfs(std::format("{}/ahci_alpm_partial", sysfs_path));
	end_slumber = read_sysfs(std::format("{}/ahci_alpm_slumber", sysfs_path));
	end_devslp = read_sysfs(std::format("{}/ahci_alpm_devslp", sysfs_path));

	if (end_active < start_active)
		end_active = start_active;
	if (end_partial < start_partial)
		end_partial = start_partial;
	if (end_slumber < start_slumber)
		end_slumber = start_slumber;

	total = (double)end_active + end_partial + end_slumber + end_devslp -
		start_active - start_partial - start_slumber - start_devslp;

	if (total < 0.00001)
		return;

	/* percent in active */
	p = (end_active - start_active) / total * 100.0;
	if (p < 0)
		 p = 0;
	report_utilization(std::format("{}-active", name), p);

	/* percent in partial */
	p = (end_partial - start_partial) / total * 100.0;
	if (p < 0)
		 p = 0;
	report_utilization(std::format("{}-partial", name), p);

	/* percent in slumber */
	p = (end_slumber - start_slumber) / total * 100.0;
	if (p < 0)
		 p = 0;
	report_utilization(std::format("{}-slumber", name), p);

	/* percent in devslp */
	p = (end_devslp - start_devslp) / total * 100.0;
	if (p < 0)
		 p = 0;
	report_utilization(std::format("{}-devslp", name), p);
}


double ahci::utilization(void) const
{
	double p;
	double total;

	total = (double)end_active + end_partial + end_slumber + end_devslp - start_active - start_partial - start_slumber - start_devslp;

	if (total < 0.00001)
		return 0.0;

	p = (end_partial - start_partial + end_active - start_active) / total * 100.0;

	if (p < 0)
		p = 0;

	return p;
}

void create_all_ahcis(void)
{
	for (const auto &entry : list_directory("/sys/class/scsi_host/")) {
		class ahci *bl;

		bool ok = false;
		read_sysfs(std::format("/sys/class/scsi_host/{}/ahci_alpm_accounting", entry), &ok);
		if (!ok)
			continue;

		write_sysfs(std::format("/sys/class/scsi_host/{}/ahci_alpm_accounting", entry), "1");

		bl = new ahci(entry, std::format("/sys/class/scsi_host/{}", entry));
		all_devices.push_back(bl);
		register_parameter("ahci-link-power-active", 0.6);  /* active sata link takes about 0.6 W */
		register_parameter("ahci-link-power-partial");
		links.push_back(bl);
	}
}



double ahci::power_usage(struct result_bundle *result, struct parameter_bundle *bundle)
{
	double power;
	double factor;
	double util;

	power = 0;

	factor = get_parameter_value(active_index, bundle);
	util = get_result_value(active_rindex, result);
	power += util * factor / 100.0;


	factor = get_parameter_value(partial_index, bundle);
	util = get_result_value(partial_rindex, result);
	power += util * factor / 100.0;

	return power;
}

void ahci_create_device_stats_table(void)
{
	unsigned int i;

	/* div attr css_class and css_id */
	tag_attr div_attr;
	init_div(&div_attr, "clear_block", "ahci");

	/* Set Title attributes */
	tag_attr title_attr;
	init_title_attr(&title_attr);

	/* Add section */
	report.add_div(&div_attr);

	if (links.size() == 0) {
		report.add_title(&title_attr, __("AHCI ALPM Residency Statistics - Not supported on this machine"));
		report.end_div();
		return;
	}

	/* Set Table attributes, rows, and cols */
	table_attributes std_table_css;
	int cols = 5;
	int rows = static_cast<int>(links.size()) + 1;
	init_std_side_table_attr(&std_table_css, rows, cols);



	/* Set array of data in row Major order */
	std::vector<std::string> ahci_data(cols * rows);
	ahci_data[0]=__("Link");
	ahci_data[1]=__("Active");
	ahci_data[2]=__("Partial");
	ahci_data[3]=__("Slumber");
	ahci_data[4]=__("Devslp");

	/* traverse list of all devices and put their residency in the table */
	for (i = 0; i < links.size(); i++){
		links[i]->report_device_stats(ahci_data, i);
	}
	report.add_title(&title_attr, __("AHCI ALPM Residency Statistics"));
	report.add_table(ahci_data, &std_table_css);
	report.end_div();
}

void ahci::report_device_stats(std::vector<std::string> &ahci_data, int idx) const
{
	int offset=(idx*5+5);
	const double active_util = get_result_value(active_rindex, &all_results);
	const double partial_util = get_result_value(partial_rindex, &all_results);
	const double slumber_util = get_result_value(slumber_rindex, &all_results);
	const double devslp_util = get_result_value(devslp_rindex, &all_results);

	ahci_data[offset] = humanname;
	offset += 1;

	ahci_data[offset]= std::format("{:5.1f}",  active_util);
	offset +=1;

	ahci_data[offset]= std::format("{:5.1f}",  partial_util);
	offset +=1;

	ahci_data[offset]= std::format("{:5.1f}",  slumber_util);
	offset +=1;

	ahci_data[offset]= std::format("{:5.1f}",  devslp_util);
}

void ahci::collect_json_fields(std::string &_js)
{
    device::collect_json_fields(_js);
    JSON_FIELD(start_active);
    JSON_FIELD(end_active);
    JSON_FIELD(start_partial);
    JSON_FIELD(end_partial);
    JSON_FIELD(start_slumber);
    JSON_FIELD(end_slumber);
    JSON_FIELD(start_devslp);
    JSON_FIELD(end_devslp);
    JSON_FIELD(sysfs_path);
    JSON_FIELD(name);
    JSON_FIELD(partial_rindex);
    JSON_FIELD(active_rindex);
    JSON_FIELD(slumber_rindex);
    JSON_FIELD(devslp_rindex);
    JSON_FIELD(partial_index);
    JSON_FIELD(active_index);
    JSON_FIELD(humanname);
}
