/*
 * 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 <fstream>
#include <vector>
#include <cstring>
#include <stdlib.h>
#include <sstream>
#include <ncurses.h>
#include <unistd.h>
#include "cpu.h"
#include "cpudevice.h"
#include "cpu_rapl_device.h"
#include "dram_rapl_device.h"
#include "intel_cpus.h"
#include "../parameters/parameters.h"

#include "../perf/perf_bundle.h"
#include "../lib.h"
#include "../display.h"
#include "../report/report.h"
#include "../report/report-maker.h"
#include "../report/report-data-html.h"

static class abstract_cpu system_level;

std::vector<class abstract_cpu *> all_cpus;

static	class perf_bundle * perf_events;



class perf_power_bundle: public perf_bundle
{
	virtual void handle_trace_point(void *trace, int cpu, uint64_t time) override;

};


static class abstract_cpu * new_package(int package, int cpu, const std::string &vendor, int family, int model)
{
	class abstract_cpu *ret = nullptr;
	class cpudevice *cpudev;
	class cpu_rapl_device *cpu_rapl_dev;
	class dram_rapl_device *dram_rapl_dev;

	std::string packagename;
	if (vendor == "GenuineIntel")
		if (is_supported_intel_cpu(family, model, cpu)) {
			ret = new nhm_package(family, model);
			ret->set_intel_MSR(true);
		}

	if (!ret) {
		ret = new cpu_package;
		ret->set_intel_MSR(false);
	}

	ret->set_number(package, cpu);
	ret->set_type("Package");
	ret->childcount = 0;

	packagename = pt_format(_("cpu package {}"), cpu);
	cpudev = new cpudevice(_("cpu package"), packagename.c_str(), ret);
	all_devices.push_back(cpudev);

	packagename = pt_format(_("package-{}"), cpu);
	cpu_rapl_dev = new cpu_rapl_device(cpudev, _("cpu rapl package"), packagename.c_str(), ret);
	if (cpu_rapl_dev->device_present())
		all_devices.push_back(cpu_rapl_dev);
	else
		delete cpu_rapl_dev;

	packagename = pt_format(_("package-{}"), cpu);
	dram_rapl_dev = new dram_rapl_device(cpudev, _("dram rapl package"), packagename.c_str(), ret);
	if (dram_rapl_dev->device_present())
		all_devices.push_back(dram_rapl_dev);
	else
		delete dram_rapl_dev;

	return ret;
}

static class abstract_cpu * new_core(int core, int cpu, const std::string &vendor, int family, int model)
{
	class abstract_cpu *ret = nullptr;

	if (vendor == "GenuineIntel")
		if (is_supported_intel_cpu(family, model, cpu)) {
			ret = new nhm_core(family, model);
			ret->set_intel_MSR(true);
		}

	if (!ret) {
		ret = new cpu_core;
		ret->set_intel_MSR(false);
	}

	ret->set_number(core, cpu);
	ret->childcount = 0;
	ret->set_type("Core");

	return ret;
}

static class abstract_cpu * new_i965_gpu(void)
{
	class abstract_cpu *ret = nullptr;

	ret = new i965_core;
	ret->childcount = 0;
	ret->set_type("GPU");

	return ret;
}

static class abstract_cpu * new_cpu(int number, const std::string &vendor, int family, int model)
{
	class abstract_cpu * ret = nullptr;

	if (vendor == "GenuineIntel")
		if (is_supported_intel_cpu(family, model, number)) {
			ret = new nhm_cpu;
			ret->set_intel_MSR(true);
		}

	if (!ret) {
		ret = new cpu_linux;
		ret->set_intel_MSR(false);
	}
	ret->set_number(number, number);
	ret->set_type("CPU");
	ret->childcount = 0;

	return ret;
}




static void handle_one_cpu(unsigned int number, const std::string &vendor, int family, int model)
{
	unsigned int package_number = 0;
	unsigned int core_number = 0;
	class abstract_cpu *package, *core, *cpu;

	core_number = read_sysfs(std::format("/sys/devices/system/cpu/cpu{}/topology/core_id", number));
	if (core_number == (unsigned int) -1)
		core_number = number;

	package_number = read_sysfs(std::format("/sys/devices/system/cpu/cpu{}/topology/physical_package_id", number));
	if (package_number == (unsigned int) -1)
		package_number = 0;


	if (system_level.children.size() <= package_number)
		system_level.children.resize(package_number + 1);

	if (!system_level.children[package_number]) {
		system_level.children[package_number] = std::unique_ptr<abstract_cpu>(new_package(package_number, number, vendor, family, model));
		system_level.childcount++;
	}

	package = system_level.children[package_number].get();
	package->parent = &system_level;


	if (package->children.size() <= core_number)
		package->children.resize(core_number + 1);

	if (!package->children[core_number]) {
		package->children[core_number] = std::unique_ptr<abstract_cpu>(new_core(core_number, number, vendor, family, model));
		package->childcount++;
	}

	core = package->children[core_number].get();
	core->parent = package;

	if (core->children.size() <= number)
		core->children.resize(number + 1);

	if (!core->children[number]) {
		core->children[number] = std::unique_ptr<abstract_cpu>(new_cpu(number, vendor, family, model));
		core->childcount++;
	}

	cpu = core->children[number].get();
	cpu->parent = core;

	if (number >= all_cpus.size())
		all_cpus.resize(number + 1, nullptr);
	all_cpus[number] = cpu;
}

static void handle_i965_gpu(void)
{
	unsigned int core_number = 0;
	class abstract_cpu *package;


	package = system_level.children[0].get();

	core_number = package->children.size();

	if (package->children.size() <= core_number)
		package->children.resize(core_number + 1);

	if (!package->children[core_number]) {
		package->children[core_number] = std::unique_ptr<abstract_cpu>(new_i965_gpu());
		package->childcount++;
	}
}

static class abstract_cpu * new_xe_gpu(void)
{
	class abstract_cpu *ret = new xe_core;
	ret->childcount = 0;
	ret->set_type("GPU");
	return ret;
}

static void handle_xe_gpu(void)
{
	class abstract_cpu *package = system_level.children[0].get();

	const unsigned int core_number = package->children.size();
	if (package->children.size() <= core_number)
		package->children.resize(core_number + 1);

	if (!package->children[core_number]) {
		package->children[core_number] =
			std::unique_ptr<abstract_cpu>(new_xe_gpu());
		package->childcount++;
	}
}

void enumerate_cpus(void)
{
	const std::string content = read_file_content("/proc/cpuinfo");

	if (content.empty())
		return;

	int number = -1;
	std::string vendor;
	int family = 0;
	int model = 0;

	std::istringstream stream(content);
	std::string line;

	/* Not all /proc/cpuinfo include "vendor_id\t". */

	while (std::getline(stream, line)) {

		if (line.starts_with("vendor_id\t")) {
			size_t pos = line.find(':');
			if (pos != std::string::npos) {
				pos++;
				if (pos < line.length() && line[pos] == ' ')
					pos++;
				vendor = line.substr(pos);
			}
		}
		if (line.starts_with("processor\t")) {
			size_t pos = line.find(':');
			if (pos != std::string::npos) {
				try {
					number = std::stoull(line.substr(pos + 1));
				} catch (...) {}
			}
		}
		if (line.starts_with("cpu family\t")) {
			size_t pos = line.find(':');
			if (pos != std::string::npos) {
				try {
					family = std::stoull(line.substr(pos + 1));
				} catch (...) {}
			}
		}
		if (line.starts_with("model\t")) {
			size_t pos = line.find(':');
			if (pos != std::string::npos) {
				try {
					model = std::stoull(line.substr(pos + 1));
				} catch (...) {}
			}
		}
		/* on x86 and others 'bogomips' is last
		 * on ARM it *can* be bogomips, or 'CPU revision'
		 * on POWER, it's 'revision'
		 * on RISCV64 it's 'isa'
		 */

		std::string lower_line = line;
		std::transform(lower_line.begin(), lower_line.end(), lower_line.begin(), ::tolower);

		if (lower_line.starts_with("bogomips\t")
		    || lower_line.starts_with("cpu revision\t")
		    || line.starts_with("revision")
		    || line.starts_with("isa\t")) {
			if (number == -1) {
				/* Not all /proc/cpuinfo include "processor\t". */
				number = 0;
			}
			if (number >= 0) {
				handle_one_cpu(number, vendor, family, model);
				set_max_cpu(number);
				number = -2;
			}
		}
	}

	if (!find_intel_rc6_card_path().empty())
		handle_i965_gpu();

	if (!find_xe_card_path().empty())
		handle_xe_gpu();

	perf_events = new perf_power_bundle();

	if (!perf_events->add_event("power","cpu_idle")){
		perf_events->add_event("power","power_start");
		perf_events->add_event("power","power_end");
	}
	if (!perf_events->add_event("power","cpu_frequency"))
		perf_events->add_event("power","power_frequency");

}

void start_cpu_measurement(void)
{
	perf_events->start();
	system_level.measurement_start();
}

void end_cpu_measurement(void)
{
	system_level.measurement_end();
	perf_events->stop();
}

static int has_state_level(class abstract_cpu *acpu, int state, int line)
{
	switch (state) {
		case PSTATE:
			return acpu->has_pstate_level(line);
			break;
		case CSTATE:
			return acpu->has_cstate_level(line);
			break;
	}
	return 0;
}

static std::string fill_state_name(class abstract_cpu *acpu, int state, int line)
{
	switch (state) {
		case PSTATE:
			return acpu->fill_pstate_name(line);
			break;
		case CSTATE:
			return acpu->fill_cstate_name(line);
			break;
	}
	return "-EINVAL";
}

static std::string fill_state_line(class abstract_cpu *acpu, int state, int line,
					const std::string &sep = "")
{
	switch (state) {
		case PSTATE:
			return acpu->fill_pstate_line(line);
			break;
		case CSTATE:
			return acpu->fill_cstate_line(line, sep);
			break;
	}
	return "-EINVAL";
}

static int get_cstates_num(void)
{
	unsigned int package, core, cpu;
	class abstract_cpu *_package, * _core, * _cpu;
	int cstates_num;

	for (package = 0, cstates_num = 0;
			package < system_level.children.size(); package++) {
		_package = system_level.children[package].get();
		if (_package == nullptr)
			continue;

		/* walk package cstates and get largest cstates number */
		for (const auto &s : _package->cstates)
			cstates_num = std::max(cstates_num, s->line_level);

		/*
		 * for each core in this package, walk core cstates and get
		 * largest cstates number
		 */
		for (core = 0; core < _package->children.size(); core++) {
			_core = _package->children[core].get();
			if (_core == nullptr)
				continue;

			for (const auto &s : _core->cstates)
				cstates_num = std::max(cstates_num, s->line_level);

			/*
			 * for each core, walk the logical cpus in case
			 * there is are more linux cstates than hw cstates
			 */
			 for (cpu = 0; cpu < _core->children.size(); cpu++) {
				_cpu = _core->children[cpu].get();
				if (_cpu == nullptr)
					continue;

				for (const auto &s : _cpu->cstates)
					cstates_num = std::max(cstates_num, s->line_level);
			}
		}
	}

	return cstates_num;
}

void report_display_cpu_cstates(void)
{
	unsigned int package, core, cpu;
	int line, cstates_num, title=0, core_num=0;
	class abstract_cpu *_package, *_core = nullptr, * _cpu;
	std::string core_type;

	cstates_num = get_cstates_num();
	/* div attr css_class and css_id */
	tag_attr div_attr;
	init_div(&div_attr, "clear_block", "cpuidle");

	/* Set Table attributes, rows, and cols */
	table_attributes std_table_css;
	table_size pkg_tbl_size;
	table_size core_tbl_size;
	table_size cpu_tbl_size;


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

	/* Report add section */
	report.add_div(&div_attr);
	report.add_title(&title_attr, __("Processor Idle State Report"));

	/* Set array of data in row Major order */
       	int idx1, idx2, idx3;
	std::string tmp_str;

	for (package = 0; package < system_level.children.size(); package++) {
		bool first_core = true;
		idx1=0;
		idx2=0;
		idx3=0;

		_package = system_level.children[package].get();
		if (!_package)
			continue;
		/* Tables for PKG, CORE, CPU */
		pkg_tbl_size.cols=2;
		pkg_tbl_size.rows= ((cstates_num+1)-LEVEL_HEADER)+1;
		std::vector<std::string> pkg_data(pkg_tbl_size.cols * pkg_tbl_size.rows);

		core_tbl_size.cols=2;
		core_tbl_size.rows=(cstates_num *_package->children.size())
				+ _package->children.size();
		std::vector<std::string> core_data(core_tbl_size.cols * core_tbl_size.rows);
		int num_cpus=0, num_cores=0;

		for (core = 0; core < _package->children.size(); core++) {
			_core = _package->children[core].get();
			if (!_core)
				continue;
			core_type = _core->get_type();
			if (!core_type.empty())
				if (core_type == "Core")
					num_cores+=1;

			for (cpu = 0; cpu < _core->children.size(); cpu++) {
				_cpu = _core->children[cpu].get();
				if (!_cpu)
					continue;
				num_cpus+=1;
			}
		}
		if (num_cores > 0)
			cpu_tbl_size.cols = (2 * (num_cpus / num_cores)) + 1;
		else
			cpu_tbl_size.cols = 1;
		cpu_tbl_size.rows = ((cstates_num+1-LEVEL_HEADER) * _package->children.size())
				+ _package->children.size();
		std::vector<std::string> cpu_data(cpu_tbl_size.cols * cpu_tbl_size.rows);

		for (core = 0; core < _package->children.size(); core++) {
			cpu_data[idx3]="&nbsp;";
			idx3+=1;
			_core = _package->children[core].get();

			if (!_core)
				continue;

			core_num+=1;

			/* *** PKG STARTS *** */
			for (line = LEVEL_HEADER; line <= cstates_num; line++) {
				bool first_cpu = true;
				if (!_package->has_cstate_level(line))
				continue;
				if (line == LEVEL_HEADER) {
					if (first_core) {
						pkg_data[idx1]=__("Package");
						idx1+=1;
						pkg_data[idx1]=std::to_string(_package->get_number());
						idx1+=1;
					}
				} else if (first_core) {
					tmp_str=_package->fill_cstate_name(line);
					pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
					idx1+=1;
					tmp_str=_package->fill_cstate_line(line);
					pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
					idx1+=1;
				}

				/* *** CORE STARTS *** */
				if (!_core->can_collapse()) {

					/*
						* Patch for compatibility with Ryzen processors
						* See https://github.com/fenrus75/powertop/issues/64
					*/
					if (idx2 >= core_tbl_size.cols * core_tbl_size.rows) break;

					if (line == LEVEL_HEADER) {
						/* Here we need to check for which core type we
						* are using. Do not use the core type for the
						* report.addf as it breaks an important macro use
						* for translation decision making for the reports.
						* */
						core_type = _core->get_type();
						if (!core_type.empty()) {
							if (core_type == "Core") {
								core_data[idx2]="";
								idx2+=1;
								core_data[idx2]=pt_format(__("Core {}"), _core->get_number());
								idx2+=1;
							} else {
								core_data[idx2]="";
								idx2+=1;
								core_data[idx2]=pt_format(__("GPU {}"), _core->get_number());
								idx2+=1;
							}
						}
					} else {


						tmp_str=_core->fill_cstate_name(line);
						core_data[idx2]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
						tmp_str=_core->fill_cstate_line(line);
						core_data[idx2]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
					}
				}
				// *** CPU STARTS ***
				for (cpu = 0; cpu < _core->children.size(); cpu++) {
					_cpu = _core->children[cpu].get();

					if (!_cpu)
						continue;
					if (line == LEVEL_HEADER) {
						cpu_data[idx3] = __("CPU");
						idx3+=1;
						cpu_data[idx3]=std::to_string(_cpu->get_number());
						idx3+=1;
						continue;
					}

					if (first_cpu) {
						title+=1;
						cpu_data[idx3]=_cpu->fill_cstate_name(line);
						idx3+=1;
						first_cpu = false;
					}

					tmp_str=_cpu->fill_cstate_percentage(line);
					cpu_data[idx3]=(tmp_str=="" ? "&nbsp;" : tmp_str);
					idx3+=1;

					if (line != LEVEL_C0){
						tmp_str=_cpu->fill_cstate_time(line);
						cpu_data[idx3]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx3+=1;
					} else {
						cpu_data[idx3]="&nbsp;";
						idx3+=1;
					}
				}
			}
			first_core = false;
		}

		/* Report Output */
		if (core_num > 0)
			title=title/core_num;
		else if (_core && !_core->children.empty())
			title=title/_core->children.size();

		init_pkg_table_attr(&std_table_css, pkg_tbl_size.rows,  pkg_tbl_size.cols);
		report.add_table(pkg_data, &std_table_css);
		if (_core && !_core->can_collapse()) {
			init_core_table_attr(&std_table_css, title+1, core_tbl_size.rows,
				core_tbl_size.cols);
			report.add_table(core_data, &std_table_css);
		}
		init_cpu_table_attr(&std_table_css, title+1, cpu_tbl_size.rows,
				cpu_tbl_size.cols);
		report.add_table(cpu_data, &std_table_css);
	}
	report.end_div();
}

void report_display_cpu_pstates(void)
{
	unsigned int package, core, cpu;
	int line, title=0;
	class abstract_cpu *_package, *_core = nullptr, * _cpu;
	unsigned int pstates_num;
	std::string core_type;

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

	/* Set Table attributes, rows, and cols */
	table_attributes std_table_css;
	table_size pkg_tbl_size;
	table_size core_tbl_size;
	table_size cpu_tbl_size;


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

	/* Report add section */
        report.add_div(&div_attr);
        report.add_title(&title_attr, __("Processor Frequency Report"));

        /* Set array of data in row Major order */
	int idx1, idx2, idx3, num_cpus=0, num_cores=0;
        std::string tmp_str;

	pstates_num = 0;
	for (const auto *acpu : all_cpus) {
		if (acpu)
			pstates_num = std::max<unsigned int>(pstates_num,
				acpu->pstates.size());
	}

	for (package = 0; package < system_level.children.size(); package++) {
		bool first_core = true;
		idx1=0;
		idx2=0;
		idx3=0;

		_package = system_level.children[package].get();
		if (!_package)
			continue;

		/*  Tables for PKG, CORE, CPU */
		pkg_tbl_size.cols=2;
		pkg_tbl_size.rows=((pstates_num+1)-LEVEL_HEADER)+2;
		std::vector<std::string> pkg_data(pkg_tbl_size.cols * pkg_tbl_size.rows);

		core_tbl_size.cols=2;
		core_tbl_size.rows=((pstates_num+2) *_package->children.size());
		std::vector<std::string> core_data(core_tbl_size.cols * core_tbl_size.rows);

		/* PKG */
		num_cpus=0;
		num_cores=0;
		for (core = 0; core < _package->children.size(); core++) {
			_core = _package->children[core].get();
			if (!_core)
				continue;

			core_type = _core->get_type();
			if (!core_type.empty())
				if (core_type == "Core")
					num_cores+=1;

			for (cpu = 0; cpu < _core->children.size(); cpu++) {
				_cpu = _core->children[cpu].get();
				if (!_cpu)
					continue;
				num_cpus+=1;
			}
		}
		if (num_cores > 0)
			cpu_tbl_size.cols = (num_cpus / num_cores) + 1;
		else
			cpu_tbl_size.cols = 1;
		cpu_tbl_size.rows= (pstates_num+2) * _package->children.size()
				+ _package->children.size();
		std::vector<std::string> cpu_data(cpu_tbl_size.cols * cpu_tbl_size.rows);

		/* Core */
		for (core = 0; core < _package->children.size(); core++) {
			cpu_data[idx3]="&nbsp;";
			idx3+=1;
			_core = _package->children[core].get();
			if (!_core)
				continue;

			if (!_core->has_pstates())
				continue;

			for (line = LEVEL_HEADER; line < (int)pstates_num; line++) {
				bool first_cpu = true;

				if (!_package->has_pstate_level(line))
					continue;

				if (first_core) {
					if (line == LEVEL_HEADER) {
						pkg_data[idx1]=__("Package");
						idx1+=1;
						pkg_data[idx1]=std::to_string(_package->get_number());
						idx1+=1;
					} else {
						tmp_str=_package->fill_pstate_name(line);
						pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx1+=1;
						tmp_str=_package->fill_pstate_line(line);
						pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx1+=1;
					}
				}


				if (!_core->can_collapse()) {
					if (line == LEVEL_HEADER) {
						core_data[idx2]="";
						idx2+=1;
						core_data[idx2]=pt_format(__("Core {}"), _core->get_number());
						idx2+=1;
					} else {
						tmp_str=_core->fill_pstate_name(line);
						core_data[idx2]= (tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
						tmp_str=_core->fill_pstate_line(line);
						core_data[idx2]= (tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
					}
				}

				/* CPU */
				for (cpu = 0; cpu < _core->children.size(); cpu++) {
					_cpu = _core->children[cpu].get();
					if (!_cpu)
						continue;

					if (line == LEVEL_HEADER) {
						cpu_data[idx3] = pt_format(__("CPU {}"), _cpu->get_number());
						idx3+=1;
						continue;
					}

					if (first_cpu) {
						tmp_str=_cpu->fill_pstate_name(line);
						cpu_data[idx3]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx3+=1;
						first_cpu = false;
					}

					tmp_str=_cpu->fill_pstate_line(line);
					cpu_data[idx3]=(tmp_str=="" ? "&nbsp;" : tmp_str);
					idx3+=1;
				}
			}
			first_core = false;
		}
		init_pkg_table_attr(&std_table_css, pkg_tbl_size.rows, pkg_tbl_size.cols);
		report.add_table(pkg_data, &std_table_css);
		if (_core && !_core->can_collapse()){
			title=pstates_num+2;
			init_core_table_attr(&std_table_css, title,
				core_tbl_size.rows, core_tbl_size.cols);
			report.add_table(core_data, &std_table_css);
		} else {
			title=pstates_num+1;
		}

		init_cpu_table_attr(&std_table_css, title,
				cpu_tbl_size.rows, cpu_tbl_size.cols);
		report.add_table(cpu_data, &std_table_css);
	}
	report.end_div();
}

void impl_w_display_cpu_states(int state)
{
	WINDOW *win;
	unsigned int package, core, cpu;
	int line, loop, cstates_num, pstates_num;
	class abstract_cpu *_package, * _core, * _cpu;
	int ctr = 0;

	cstates_num = get_cstates_num();

	pstates_num = 0;
	for (const auto *acpu : all_cpus) {
		if (!acpu)
			continue;

		pstates_num = std::max<int>(pstates_num, acpu->pstates.size());
	}

	if (state == PSTATE) {
		win = get_ncurses_win("Frequency stats");
		loop = pstates_num;
	} else {
		win = get_ncurses_win("Idle stats");
		loop = cstates_num;
	}

	if (!win)
		return;

	wclear(win);
#ifndef ENABLE_TEST_FRAMEWORK
	{
		const char *tabname;
		if (state == PSTATE)
			tabname = "Frequency stats";
		else
			tabname = "Idle stats";
		class tab_window *tw = tab_windows[tabname];
		if (tw)
			tw->reset_content_size();
	}
#endif
	wmove(win, 2, 0);

	for (package = 0; package < system_level.children.size(); package++) {
		int first_pkg = 0;
		_package = system_level.children[package].get();
		if (!_package)
			continue;

		for (core = 0; core < _package->children.size(); core++) {
			_core = _package->children[core].get();
			if (!_core)
				continue;
			if (!_core->has_pstates() && state == PSTATE)
				continue;

			for (line = LEVEL_HEADER; line <= loop; line++) {
				int first = 1;
				ctr = 0;
				std::string linebuf;

				if (!has_state_level(_package, state, line))
					continue;

				if (first_pkg == 0) {
					linebuf += fill_state_name(_package, state, line);
					if (linebuf.length() < (unsigned int)ctr + 10)
						linebuf.append(ctr + 10 - linebuf.length(), ' ');
					linebuf += fill_state_line(_package, state, line);
				}
				ctr += 20;
				if (linebuf.length() < (unsigned int)ctr)
					linebuf.append(ctr - linebuf.length(), ' ');

				linebuf += "| ";
				ctr += 2;

				if (!_core->can_collapse()) {
					linebuf += fill_state_name(_core, state, line);
					if (linebuf.length() < (unsigned int)ctr + 10)
						linebuf.append(ctr + 10 - linebuf.length(), ' ');
					linebuf += fill_state_line(_core, state, line);
					ctr += 20;
					if (linebuf.length() < (unsigned int)ctr)
						linebuf.append(ctr - linebuf.length(), ' ');

					linebuf += "| ";
					ctr += 2;
				}

				for (cpu = 0; cpu < _core->children.size(); cpu++) {
					_cpu = _core->children[cpu].get();
					if (!_cpu)
						continue;

					if (first == 1) {
						std::string str = fill_state_name(_cpu, state, line);
						linebuf += str;
						if (linebuf.length() < (unsigned int)ctr + 10)
							linebuf.append(ctr + 10 - linebuf.length(), ' ');
						first = 0;
						ctr += 12;
					}
					linebuf += fill_state_line(_cpu, state, line);
					ctr += 10;
					if (linebuf.length() < (unsigned int)ctr)
						linebuf.append(ctr - linebuf.length(), ' ');

				}
				linebuf += "\n";
				wprintw(win, "%s", linebuf.c_str());
			}
			wprintw(win, "\n");
			first_pkg++;
		}
	}

#ifndef ENABLE_TEST_FRAMEWORK
	{
		const char *tabname;
		if (state == PSTATE)
			tabname = "Frequency stats";
		else
			tabname = "Idle stats";
		class tab_window *tw = tab_windows[tabname];
		if (tw)
			tw->update_content_size();
	}
#endif
}

void w_display_cpu_pstates(void)
{
	impl_w_display_cpu_states(PSTATE);
}

void w_display_cpu_cstates(void)
{
	impl_w_display_cpu_states(CSTATE);
}

struct power_entry {
#ifndef __i386__
	int dummy;
#endif
	int64_t	type;
	int64_t	value;
} __attribute__((packed));


void perf_power_bundle::handle_trace_point(void *trace, int cpunr, uint64_t time)
{
	struct tep_event *event;
        struct tep_record rec; /* holder */
	class abstract_cpu *cpu;
	int type;

	rec.data = trace;

	type = tep_data_type(perf_event::tep, &rec);
	event = tep_find_event(perf_event::tep, type);

	if (!event)
		return;

	if (cpunr < 0 || cpunr >= (int)all_cpus.size()) {
		fprintf(stderr, _("INVALID cpu nr in handle_trace_point\n"));
		return;
	}

	cpu = all_cpus[cpunr];
	if (!cpu)
		return;

	unsigned long long val;
	int ret;
	if (strcmp(event->name, "cpu_idle")==0) {

		ret = tep_get_field_val(nullptr, event, "state", &rec, &val, 0);
                if (ret < 0) {
                        fprintf(stderr, _("cpu_idle event returned no state?\n"));
                        exit(-1);
                }

		if (val == (unsigned int)-1)
			cpu->go_unidle(time);
		else
			cpu->go_idle(time);
	}

	if (strcmp(event->name, "power_frequency") == 0
	|| strcmp(event->name, "cpu_frequency") == 0){

		ret = tep_get_field_val(nullptr, event, "state", &rec, &val, 0);
		if (ret < 0) {
			fprintf(stderr, _("power or cpu_frequency event returned no state?\n"));
			exit(-1);
		}

		cpu->change_freq(time, val);
	}

	if (strcmp(event->name, "power_start")==0)
		cpu->go_idle(time);
	if (strcmp(event->name, "power_end")==0)
		cpu->go_unidle(time);

#if 0
	unsigned int i;
	for (i = 0; i < system_level.children.size(); i++)
		if (system_level.children[i])
			system_level.children[i]->validate();
#endif
}

void process_cpu_data(void)
{
	system_level.reset_pstate_data();

	perf_events->process();

	for (auto &child : system_level.children)
		if (child)
			child->validate();

}

void end_cpu_data(void)
{
	system_level.reset_pstate_data();

	perf_events->clear();
}

void clear_cpu_data(void)
{
	if (perf_events)
		perf_events->release();
	delete perf_events;
}


void clear_all_cpus(void)
{
	system_level.children.clear();
	all_cpus.clear();
}
