/*
 * 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 <vector>
#include <string.h>
#include <stdlib.h>
#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;

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);

};


static class abstract_cpu * new_package(int package, int cpu, char * vendor, int family, int model)
{
	class abstract_cpu *ret = NULL;
	class cpudevice *cpudev;
	class cpu_rapl_device *cpu_rapl_dev;
	class dram_rapl_device *dram_rapl_dev;

	char packagename[128];
	if (strcmp(vendor, "GenuineIntel") == 0)
		if (family == 6)
			if (is_supported_intel_cpu(model, cpu)) {
				ret = new class nhm_package(model);
				ret->set_intel_MSR(true);
			}

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

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

	snprintf(packagename, sizeof(packagename), _("cpu package %i"), cpu);
	cpudev = new class cpudevice(_("cpu package"), packagename, ret);
	all_devices.push_back(cpudev);

	snprintf(packagename, sizeof(packagename), _("package-%i"), cpu);
	cpu_rapl_dev = new class cpu_rapl_device(cpudev, _("cpu rapl package"), packagename, ret);
	if (cpu_rapl_dev->device_present())
		all_devices.push_back(cpu_rapl_dev);
	else
		delete cpu_rapl_dev;

	snprintf(packagename, sizeof(packagename), _("package-%i"), cpu);
	dram_rapl_dev = new class dram_rapl_device(cpudev, _("dram rapl package"), packagename, 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, char * vendor, int family, int model)
{
	class abstract_cpu *ret = NULL;

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

	if (!ret) {
		ret = new class 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 = NULL;

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

	return ret;
}

static class abstract_cpu * new_cpu(int number, char * vendor, int family, int model)
{
	class abstract_cpu * ret = NULL;

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

	if (!ret) {
		ret = new class 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, char *vendor, int family, int model)
{
	char filename[PATH_MAX];
	ifstream file;
	unsigned int package_number = 0;
	unsigned int core_number = 0;
	class abstract_cpu *package, *core, *cpu;

	snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/topology/core_id", number);
	file.open(filename, ios::in);
	if (file) {
		file >> core_number;
		file.close();
	}

	snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%i/topology/physical_package_id", number);
	file.open(filename, ios::in);
	if (file) {
		file >> package_number;
		if (package_number == (unsigned int) -1)
			package_number = 0;
		file.close();
	}


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

	if (!system_level.children[package_number]) {
		system_level.children[package_number] = new_package(package_number, number, vendor, family, model);
		system_level.childcount++;
	}

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

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

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

	core = package->children[core_number];
	core->parent = package;

	if (core->children.size() <= number)
		core->children.resize(number + 1, NULL);
	if (!core->children[number]) {
		core->children[number] = new_cpu(number, vendor, family, model);
		core->childcount++;
	}

	cpu = core->children[number];
	cpu->parent = core;

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

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


	package = system_level.children[0];

	core_number = package->children.size();

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

	if (!package->children[core_number]) {
		package->children[core_number] = new_i965_gpu();
		package->childcount++;
	}
}


void enumerate_cpus(void)
{
	ifstream file;
	char line[4096];

	int number = -1;
	char vendor[128];
	int family = 0;
	int model = 0;

	file.open("/proc/cpuinfo",  ios::in);

	if (!file)
		return;
	/* Not all /proc/cpuinfo include "vendor_id\t". */
	vendor[0] = '\0';

	while (file) {

		file.getline(line, sizeof(line));
		if (strncmp(line, "vendor_id\t",10) == 0) {
			char *c;
			c = strchr(line, ':');
			if (c) {
				c++;
				if (*c == ' ')
					c++;
				pt_strcpy(vendor, c);
			}
		}
		if (strncmp(line, "processor\t",10) == 0) {
			char *c;
			c = strchr(line, ':');
			if (c) {
				c++;
				number = strtoull(c, NULL, 10);
			}
		}
		if (strncmp(line, "cpu family\t",11) == 0) {
			char *c;
			c = strchr(line, ':');
			if (c) {
				c++;
				family = strtoull(c, NULL, 10);
			}
		}
		if (strncmp(line, "model\t",6) == 0) {
			char *c;
			c = strchr(line, ':');
			if (c) {
				c++;
				model = strtoull(c, NULL, 10);
			}
		}
		/* on x86 and others 'bogomips' is last
		 * on ARM it *can* be bogomips, or 'CPU revision'
		 * on POWER, it's revision
		 */
		if (strncasecmp(line, "bogomips\t", 9) == 0
		    || strncasecmp(line, "CPU revision\t", 13) == 0
		    || strncmp(line, "revision", 8) == 0) {
			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;
			}
		}
	}


	file.close();

	if (access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK) == 0)
		handle_i965_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 void expand_string(char *string, unsigned int newlen)
{
	while (strlen(string) < newlen)
		strcat(string, " ");
}

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 const char * fill_state_name(class abstract_cpu *acpu, int state, int line, char *buf)
{
	switch (state) {
		case PSTATE:
			return acpu->fill_pstate_name(line, buf);
			break;
		case CSTATE:
			return acpu->fill_cstate_name(line, buf);
			break;
	}
	return "-EINVAL";
}

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

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

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

		/* walk package cstates and get largest cstates number */
		for (i = 0; i < _package->cstates.size(); i++)
			cstates_num = std::max(cstates_num,
						(_package->cstates[i])->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];
			if (_core == NULL)
				continue;

			for (i = 0; i <  _core->cstates.size(); i++)
				cstates_num = std::max(cstates_num,
						(_core->cstates[i])->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];
				if (_cpu == NULL)
					continue;

				for (i = 0; i < _cpu->cstates.size(); i++)
					cstates_num = std::max(cstates_num,
						(_cpu->cstates[i])->line_level);
			}
		}
	}

	return cstates_num;
}

void report_display_cpu_cstates(void)
{
	char buffer[512], buffer2[512], tmp_num[50];
	unsigned int package, core, cpu;
	int line, cstates_num, title=0, core_num=0;
	class abstract_cpu *_package, *_core = NULL, * _cpu;
	const char* core_type = NULL;

	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;
	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];
		if (!_package)
			continue;
		/* Tables for PKG, CORE, CPU */
		pkg_tbl_size.cols=2;
		pkg_tbl_size.rows= ((cstates_num+1)-LEVEL_HEADER)+1;
		string *pkg_data = new string[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();
		string *core_data = new string[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];
			if (!_core)
				continue;
			core_type = _core->get_type();
			if (core_type != NULL)
				if (strcmp(core_type, "Core") == 0 )
					num_cores+=1;

			for (cpu = 0; cpu < _core->children.size(); cpu++) {
				_cpu = _core->children[cpu];
				if (!_cpu)
					continue;
				num_cpus+=1;
			}
		}
		cpu_tbl_size.cols=(2 * (num_cpus / num_cores)) + 1;
		cpu_tbl_size.rows = ((cstates_num+1-LEVEL_HEADER) * _package->children.size())
				+ _package->children.size();
		string *cpu_data = new string[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];

			if (!_core)
				continue;

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

				/* *** CORE STARTS *** */
				if (!_core->can_collapse()) {
					buffer[0] = 0;
					buffer2[0] = 0;
					
					/*
						* 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 != NULL) {
							if (strcmp(core_type, "Core") == 0 ) {
								core_data[idx2]="";
								idx2+=1;
								snprintf(tmp_num, sizeof(tmp_num), __("Core %d"), _core->get_number());
								core_data[idx2]=string(tmp_num);
								idx2+=1;
								core_num+=1;
							} else {
								core_data[idx2]="";
								idx2+=1;
								snprintf(tmp_num, sizeof(tmp_num), __("GPU %d"), _core->get_number());
								core_data[idx2]=string(tmp_num);
								idx2+=1;
							}
						}
					} else {

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

					if (!_cpu)
						continue;
					if (line == LEVEL_HEADER) {
						cpu_data[idx3] = __("CPU");
						idx3+=1;
						sprintf(tmp_num,"%d",_cpu->get_number());
						cpu_data[idx3]=string(tmp_num);
						idx3+=1;
						continue;
					}

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

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

					if (line != LEVEL_C0){
						tmp_str=string(_cpu->fill_cstate_time(line, buffer));
						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.size() > 0)
			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->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);
		delete [] pkg_data;
		delete [] core_data;
		delete [] cpu_data;
	}
	report.end_div();
}

void report_display_cpu_pstates(void)
{
	char buffer[512], buffer2[512], tmp_num[50];
	unsigned int package, core, cpu;
	int line, title=0;
	class abstract_cpu *_package, *_core = NULL, * _cpu;
	unsigned int i, pstates_num;
	const char* core_type = NULL;

	/* 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;
        string tmp_str;

	for (i = 0, pstates_num = 0; i < all_cpus.size(); i++) {
		if (all_cpus[i])
			pstates_num = std::max<unsigned int>(pstates_num,
				all_cpus[i]->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];
		if (!_package)
			continue;

		/*  Tables for PKG, CORE, CPU */
		pkg_tbl_size.cols=2;
		pkg_tbl_size.rows=((pstates_num+1)-LEVEL_HEADER)+2;
		string *pkg_data = new string[pkg_tbl_size.cols * pkg_tbl_size.rows];

		core_tbl_size.cols=2;
		core_tbl_size.rows=((pstates_num+2) *_package->children.size());
		string *core_data = new string[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];
			if (!_core)
				continue;

			core_type = _core->get_type();
			if (core_type != NULL)
				if (strcmp(core_type, "Core") == 0 )
					num_cores+=1;

			for (cpu = 0; cpu < _core->children.size(); cpu++) {
				_cpu = _core->children[cpu];
				if (!_cpu)
					continue;
				num_cpus+=1;
			}
		}
		cpu_tbl_size.cols= (num_cpus/ num_cores) + 1;
		cpu_tbl_size.rows= (pstates_num+2) * _package->children.size()
				+ _package->children.size();
		string *cpu_data = new string[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];
			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;

				buffer[0] = 0;
				buffer2[0] = 0;
				if (first_core) {
					if (line == LEVEL_HEADER) {
						pkg_data[idx1]=__("Package");
						idx1+=1;
						sprintf(tmp_num,"%d",  _package->get_number());
						pkg_data[idx1]= string(tmp_num);
						idx1+=1;
					} else {
						tmp_str=string(_package->fill_pstate_name(line, buffer));
						pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx1+=1;
						tmp_str=string(_package->fill_pstate_line(line, buffer2));
						pkg_data[idx1]=(tmp_str=="" ? "&nbsp;" : tmp_str);
						idx1+=1;
					}
				}


				if (!_core->can_collapse()) {
					buffer[0] = 0;
					buffer2[0] = 0;
					if (line == LEVEL_HEADER) {
						core_data[idx2]="";
						idx2+=1;
						snprintf(tmp_num, sizeof(tmp_num), __("Core %d"), _core->get_number());
						core_data[idx2]=string(tmp_num);
						idx2+=1;
					} else {
						tmp_str=string(_core->fill_pstate_name(line, buffer));
						core_data[idx2]= (tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
						tmp_str=string(_core->fill_pstate_line(line, buffer2));
						core_data[idx2]= (tmp_str=="" ? "&nbsp;" : tmp_str);
						idx2+=1;
					}
				}

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

					if (line == LEVEL_HEADER) {
						snprintf(tmp_num, sizeof(tmp_num), __("CPU %d"), _cpu->get_number());
						cpu_data[idx3] = string(tmp_num);
						idx3+=1;
						continue;
					}

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

					buffer[0] = 0;
					tmp_str=string(_cpu->fill_pstate_line(line, buffer));
					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);
		delete [] pkg_data;
		delete [] core_data;
		delete [] cpu_data;
	}
	report.end_div();
}

void impl_w_display_cpu_states(int state)
{
	WINDOW *win;
	char buffer[128];
	char linebuf[1024];
	unsigned int package, core, cpu;
	int line, loop, cstates_num, pstates_num;
	class abstract_cpu *_package, * _core, * _cpu;
	int ctr = 0;
	unsigned int i;

	cstates_num = get_cstates_num();

	for (i = 0, pstates_num = 0; i < all_cpus.size(); i++) {
		if (!all_cpus[i])
			continue;

		pstates_num = std::max<int>(pstates_num, all_cpus[i]->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);
        wmove(win, 2,0);

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

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

			for (line = LEVEL_HEADER; line <= loop; line++) {
				int first = 1;
				ctr = 0;
				linebuf[0] = 0;

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

				buffer[0] = 0;
				if (first_pkg == 0) {
					strcat(linebuf, fill_state_name(_package, state, line, buffer));
					expand_string(linebuf, ctr + 10);
					strcat(linebuf, fill_state_line(_package, state, line, buffer));
				}
				ctr += 20;
				expand_string(linebuf, ctr);

				strcat(linebuf, "| ");
				ctr += strlen("| ");

				if (!_core->can_collapse()) {
					buffer[0] = 0;
					strcat(linebuf, fill_state_name(_core, state, line, buffer));
					expand_string(linebuf, ctr + 10);
					strcat(linebuf, fill_state_line(_core, state, line, buffer));
					ctr += 20;
					expand_string(linebuf, ctr);

					strcat(linebuf, "| ");
					ctr += strlen("| ");
				}

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

					if (first == 1) {
						strcat(linebuf, fill_state_name(_cpu, state, line, buffer));
						expand_string(linebuf, ctr + 10);
						first = 0;
						ctr += 12;
					}
					buffer[0] = 0;
					strcat(linebuf, fill_state_line(_cpu, state, line, buffer));
					ctr += 10;
					expand_string(linebuf, ctr);

				}
				strcat(linebuf, "\n");
				wprintw(win, "%s", linebuf);
			}
			wprintw(win, "\n");
			first_pkg++;
		}
	}
}

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 event_format *event;
        struct pevent_record rec; /* holder */
	class abstract_cpu *cpu;
	int type;

	rec.data = trace;

	type = pevent_data_type(perf_event::pevent, &rec);
	event = pevent_find_event(perf_event::pevent, type);

	if (!event)
		return;

	if (cpunr >= (int)all_cpus.size()) {
		cout << "INVALID cpu nr in handle_trace_point\n";
		return;
	}

	cpu = all_cpus[cpunr];

#if 0
	unsigned int i;
	printf("Time is %llu \n", time);
	for (i = 0; i < system_level.children.size(); i++)
		if (system_level.children[i])
			system_level.children[i]->validate();
#endif
	unsigned long long val;
	int ret;
	if (strcmp(event->name, "cpu_idle")==0) {

		ret = pevent_get_field_val(NULL, 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 = pevent_get_field_val(NULL, 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)
{
	unsigned int i;
	system_level.reset_pstate_data();

	perf_events->process();

	for (i = 0; i < system_level.children.size(); i++)
		if (system_level.children[i])
			system_level.children[i]->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)
{
	unsigned int i;
	for (i = 0; i < all_cpus.size(); i++) {
		delete all_cpus[i];
	}
	all_cpus.clear();
}
