/*
    lsadl is a frontend/wrapper to the adl_sdk using gtk
    Copyright (C) 2011  Sterling Pickens

    This program 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, 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ls_gtk.h"
#define GRAPH_W 680
#define GRAPH_H 480
//#define FAN_PERCENTAGE 1
int Not_Started = 1;

struct GTK_Widgets gtk;
GdkColor color;
struct Graph_Data{
	int perf[60];
	int usage[60];
	int fan[60];
	double temp[60];
}graphdata;
int graph_idx = 0;
int total_charts = 4;
struct Chart_Vectors{
	double black_top; //start of black
	double black_bottom; //end of black
	double data_top;
	double data_bottom;
}cv[4];


char Perf_Alloc(void){
	int num1 = 0;
	int num2 = 0;
	int ret_val = 0;

	(void)memset(&graphdata, '\0', sizeof(struct Graph_Data));

	return TRUE;
}

static void on_destroy(GtkWidget *widget, gpointer mw){
	gtk_widget_destroy(mw);
}


void *Refresh_Func(void *empty){
	char value[16];
	int ret_val = 0;
	int num1 = 0;
	int seconds = 0;
	int cur_perf = 0;
	gdouble dtmp = 0;


	ADLPMActivity current;
	ADLFanSpeedInfo FanSpeedInfo;
	ADLFanSpeedValue FanSpeedValue;
	gpointer data = NULL;
	GdkEventExpose *event = NULL;

	current.iSize = sizeof(ADLPMActivity);
	FanSpeedInfo.iSize = sizeof(ADLFanSpeedInfo);
	FanSpeedValue.iSize = sizeof(ADLFanSpeedValue);

	while(Not_Started)
		sleep(1);

	while(No_Exit == 1){
		sleep(2);
		gdk_threads_enter();
		seconds += 2;
		if(seconds % 60 == 0){
			graphdata.perf[graph_idx] = cur_perf;
			if(!Disable_Usage)
				graphdata.usage[graph_idx] = current.iActivityPercent;
			if(!Disable_Fan)
				graphdata.fan[graph_idx] = FanSpeedValue.iFanSpeed;
			if(!Disable_Temp)
				graphdata.temp[graph_idx] = dtmp;
			if(graph_idx == 59)
				graph_idx = 0;
			else
				graph_idx++;
			seconds = 0;
			(void)g_signal_emit_by_name(G_OBJECT(gtk.graph),  "expose-event", G_CALLBACK(on_expose_cairo), &data);
		}
		gdk_threads_leave();
	}
	pthread_exit(NULL);
}

void *GTK_Func(void *empty){
	int ret_val = 0;
	int num1 = 0;
	char tmp[10];
	gdouble temp_num = 0;
	gtk.main_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	//gtk_window_set_decorated(GTK_WINDOW(gtk.main_win), FALSE);
	gtk_container_set_border_width(GTK_CONTAINER(gtk.main_win), 2);
	gtk_window_set_default_size(GTK_WINDOW(gtk.main_win), 680, 400);
	gtk_window_set_title(GTK_WINDOW(gtk.main_win), PACKAGE_STRING);
	gtk.main_vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(gtk.main_win), gtk.main_vbox);

	gtk.main_menubar = gtk_menu_bar_new();
	gtk.main_filemenu = gtk_menu_new();
//	gtk.main_file = gtk_menu_item_new_with_label("File");
//	gtk.main_results = gtk_menu_item_new_with_label("API Results");
	gtk.main_quit = gtk_menu_item_new_with_label("Quit");

	gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtk.main_file), gtk.main_filemenu);
//	gtk_menu_shell_append(GTK_MENU_SHELL(gtk.main_filemenu), gtk.main_results);
	gtk_menu_shell_append(GTK_MENU_SHELL(gtk.main_filemenu), gtk.main_quit);
//	gtk_menu_shell_append(GTK_MENU_SHELL(gtk.main_menubar), gtk.main_file);

    gtk.notebook_label1 = gtk_label_new("settings");
    gtk.notebook_label2 = gtk_label_new("graphs");

	gtk.graph = gtk_drawing_area_new();
	gtk_widget_set_size_request(gtk.graph, GRAPH_W, GRAPH_H);

	gtk.graph_scroll = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtk.graph_scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(gtk.graph_scroll), gtk.graph);

	gtk.notebook = gtk_notebook_new();
    ret_val = gtk_notebook_append_page(GTK_NOTEBOOK(gtk.notebook), GTK_WIDGET(gtk.scrolled_win), GTK_WIDGET(gtk.notebook_label1));
    if(ret_val == -1){
        //error
        printf("Error creating notebook!\n");
        pthread_exit(NULL);
    }
    ret_val = gtk_notebook_append_page(GTK_NOTEBOOK(gtk.notebook), GTK_WIDGET(gtk.graph_scroll), GTK_WIDGET(gtk.notebook_label2));
    if(ret_val == -1){
        //error
        printf("Error creating notebook!\n");
        pthread_exit(NULL);
    }

	gtk_box_pack_start(GTK_BOX(gtk.main_vbox), gtk.main_menubar, FALSE, FALSE, 3);
	gtk_box_pack_end(GTK_BOX(gtk.main_vbox), gtk.notebook, TRUE, TRUE, 3);

	g_signal_connect(gtk.main_win, "destroy", G_CALLBACK(exit_cb), NULL);
	g_signal_connect(G_OBJECT(gtk.main_quit), "activate", G_CALLBACK(exit_cb), NULL);
	g_signal_connect(G_OBJECT(gtk.main_results), "activate", G_CALLBACK(API_Funcs_Found_Popup_cb), NULL);
	//Note that the ::expose-event signal has been replaced by a ::draw signal in GTK+ 3. The GTK+ 3 migration guide for hints on how to port from ::expose-event to ::draw.
#ifndef USE_GTK3
	g_signal_connect(G_OBJECT(gtk.graph),  "expose-event", G_CALLBACK(on_expose_cairo), NULL);
#else
	g_signal_connect(G_OBJECT(gtk.graph), "draw", G_CALLBACK(on_expose_cairo), NULL);
#endif
	gtk_widget_show_all(gtk.main_win);
	gtk_widget_realize(gtk.graph);
	Not_Started = 0;
	gtk_main();
	pthread_exit(NULL);
}

gboolean exit_cb(GtkWidget *widget, gpointer data){
	gtk_main_quit();
	No_Exit = 0;
	return TRUE;
}

#ifndef USE_GTK3
void on_expose_cairo(GtkWidget *widget, GdkEventExpose *event, gpointer data){
#else
void on_expose_cairo(GtkWidget *widget, CairoContext *cr, gpointer data){
//Signal handlers connected to this signal can modify the cairo context passed as cr in any way they
//like and don't need to restore it. The signal emission takes care of calling cairo_save() before and cairo_restore() after invoking the handler.
#endif
	cairo_format_t format;
	cairo_surface_t *target;
	cairo_t *context;
	cairo_status_t status;
	double red, green, blue, alpha;
	double x1, y1, x2, y2;
	double last_x[4] = {0, 0, 0, 0};
	double last_y[4] = {0, 0, 0, 0};
	double tmp1 = 0;
	double tmp2 = 0;
	double highest_temp = 0;
	double highest_fan = 0;
	double highest_usage = 0;
	double width = (double)widget->allocation.width;
	double height = (double)widget->allocation.height;
	double white_height = height * 0.03;
	//double black_height = (height - white_height*5) / total_charts;
	double data_w = width * 0.95;
	//double data_h = black_height * 0.86;
	double black_height = 0;
	double data_h = 0;
	double data_s = data_w/60;
	int stride;
	int num1 = 0;
	int num2 = 0;
	int local_idx = graph_idx;
	unsigned char *img_data;
	char buffer[16];

	black_height = height;
	tmp1 = white_height;
	tmp1 *= total_charts + 1;
	black_height -= tmp1;
	black_height /= total_charts;
	data_h = black_height * 0.86;

	for(num1=0; num1<total_charts; num1++){
		cv[num1].black_top = (num1+1)*white_height + black_height*num1;
		cv[num1].black_bottom = cv[num1].black_top + black_height;
		cv[num1].data_top = cv[num1].black_top + black_height * 0.07;
		cv[num1].data_bottom = cv[num1].black_top + black_height - black_height * 0.07;
	}

	format = CAIRO_FORMAT_ARGB32;
	stride = cairo_format_stride_for_width(format, width);
	img_data = malloc(stride * height);
	target = cairo_image_surface_create_for_data(img_data, format, width, height, stride);
	context = gdk_cairo_create(GDK_DRAWABLE((gtk.graph)->window));

	status = cairo_status(context);
	if(status != 0){
		printf("Error: %s\n", cairo_status_to_string(status) );
		free(img_data);
		return;
	}

	red = 0;
	green = 0;
	blue = 0;
	alpha = 1.0;
	cairo_set_source_rgba(context, red, green, blue, alpha);
	cairo_rectangle(context, 0, 0, width, height);
	cairo_fill(context);
	red = 1.0;
	green = 1.0;
	blue = 1.0;
	alpha = 1.0;
	x1 = 0;
	y1 = 0;
	for(num1=0; num1<total_charts+1; num1++){
		cairo_set_source_rgba(context, red, green, blue, alpha);
		cairo_rectangle(context, x1, y1, width, white_height);
		cairo_fill(context);
		y1 += white_height + black_height;
	}

	x1 = 0;
	y1 = 0;
	x2 = width;
	y2 = height;

	red = 0;
	green = 1;
	blue = 0;
	alpha = 1.0;
	cairo_set_dash(context, 0, 0, 0);
	cairo_set_source_rgba(context, red, green, blue, alpha);
	cairo_set_line_width(context, 1);
	cairo_select_font_face(context, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
	cairo_set_font_size(context, 10.0);
	for(num1=0; num1<60; num1++){
		if(!Disable_Fan){
			if(graphdata.fan[num1] > highest_fan)
				highest_fan = graphdata.fan[num1];
		}
		if(!Disable_Temp){
			if(graphdata.temp[num1] > highest_temp)
				highest_temp = graphdata.temp[num1];
		}
		if(!Disable_Usage){
			if(graphdata.usage[num1] > highest_usage)
				highest_usage = graphdata.usage[num1];
		}
	}

	/*
	//cairo labels
	num1 = 0;
	cairo_move_to(context, width/2, cv[num1].data_top);
	cairo_show_text(context, "Perf Level");
	num1++;

	cairo_move_to(context, width/2, cv[num1].data_top);
	cairo_show_text(context, "GPU Usage");
	num1++;
	if(!Disable_Fan){
		cairo_move_to(context, width/2, cv[num1].data_top);
		cairo_show_text(context, "Fan Speed");
		num1++;
	}
	cairo_move_to(context, width/2, cv[num1].data_top);
	cairo_show_text(context, "GPU Temp");
	*/

	//labels and High/low legend
	num1 = 0;
	cairo_move_to(context, width/2, cv[num1].data_top);
	cairo_show_text(context, "Perf Level");
	cairo_move_to(context, data_w, cv[num1].data_top);
	(void)memset(buffer, '\0', 16);
	(void)sprintf(buffer, "%d", (int)PerfLevels-1);
	cairo_show_text(context, buffer);
	cairo_move_to(context, data_w, cv[num1].data_bottom);
	cairo_show_text(context, "0");
	num1++;

	if(!Disable_Usage){
		cairo_move_to(context, width/2, cv[num1].data_top);
		cairo_show_text(context, "GPU Usage");
		cairo_move_to(context, data_w, cv[num1].data_top);
		cairo_show_text(context, "100");
		cairo_move_to(context, data_w, cv[num1].data_bottom);
		cairo_show_text(context, "0");
		num1++;
	}

	if(!Disable_Fan){
		cairo_move_to(context, width/2, cv[num1].data_top);
		cairo_show_text(context, "Fan Speed");
		cairo_move_to(context, data_w, cv[num1].data_top);
		(void)memset(buffer, '\0', 16);
		(void)sprintf(buffer, "%u", (unsigned int)highest_fan);
		cairo_show_text(context, buffer);
		cairo_move_to(context, data_w, cv[num1].data_bottom);
		cairo_show_text(context, "0");
		num1++;
	}

	if(!Disable_Temp){
		cairo_move_to(context, width/2, cv[num1].data_top);
		cairo_show_text(context, "GPU Temp");
		cairo_move_to(context, data_w, cv[num1].data_top);
		(void)memset(buffer, '\0', 16);
		(void)sprintf(buffer, "%.01lf", highest_temp);
		cairo_show_text(context, buffer);
		cairo_move_to(context, data_w, cv[num1].data_bottom);
		cairo_show_text(context, "0");
	}

	for(num1=0; num1<60; num1++){
		num2 = 0;

		tmp1 = cv[num2].data_bottom;
		tmp2 = (double)graphdata.perf[local_idx]/(PerfLevels-1);
		tmp2 *= data_h;
		tmp1 -= tmp2;
		if(last_x[num2] + last_y[num2] != 0){
			cairo_move_to(context, last_x[num2], last_y[num2]);
			cairo_line_to(context, (double)num1*data_s, tmp1);
			cairo_stroke(context);
		}
		last_x[num2] = num1*data_s;
		last_y[num2] = tmp1;
		num2++;

		if(!Disable_Usage){
			tmp1 = cv[num2].data_bottom;
			tmp2 = (double)graphdata.usage[local_idx]/100;
			tmp2 *= data_h;
			tmp1 -= tmp2;
			if(last_x[num2] + last_y[num2] != 0){
				cairo_move_to(context, last_x[num2], last_y[num2]);
				cairo_line_to(context, (double)num1*data_s, tmp1);
				cairo_stroke(context);
			}
			last_x[num2] = num1*data_s;
			last_y[num2] = tmp1;
			num2++;
		}

		if(!Disable_Fan){
			tmp1 = cv[num2].data_bottom;
			tmp2 = (double)graphdata.fan[local_idx]/highest_fan;
			tmp2 *= data_h;
			tmp1 -= tmp2;
			if(last_x[num2] + last_y[num2] != 0){
				cairo_move_to(context, last_x[num2], last_y[num2]);
				cairo_line_to(context, (double)num1*data_s, tmp1);
				cairo_stroke(context);
			}
			last_x[num2] = num1*data_s;
			last_y[num2] = tmp1;
			num2++;
		}

		if(!Disable_Temp){
			tmp1 = cv[num2].data_bottom;
			tmp2 = (double)graphdata.temp[local_idx]/highest_temp;
			tmp2 *= data_h;
			tmp1 -= tmp2;
			if(last_x[num2] + last_y[num2] != 0){
				cairo_move_to(context, last_x[num2], last_y[num2]);
				cairo_line_to(context, (double)num1*data_s, tmp1);
				cairo_stroke(context);
			}
			last_x[num2] = num1*data_s;
			last_y[num2] = tmp1;
		}

		if(local_idx == 59)
			local_idx = 0;
		else
			local_idx++;
	}

	cairo_destroy(context);
	cairo_surface_destroy(target);
	free(img_data);
}
