/*
 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * 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; version 2 of the License (not later!)
 *
 * 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>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#ifndef _TRACE_GRAPH_H
#define _TRACE_GRAPH_H

#include <gtk/gtk.h>
#include "trace-cmd.h"
#include "trace-filter-hash.h"
#include "trace-xml.h"

struct graph_info;

typedef void (graph_select_cb)(struct graph_info *ginfo, guint64 time);
typedef void (graph_filter_cb)(struct graph_info *ginfo,
			       struct filter_task *task_filter,
			       struct filter_task *hide_tasks);

/* Used for quereing what plots are defined */
enum graph_plot_type {
	PLOT_TYPE_OTHER,
	PLOT_TYPE_CPU,
	PLOT_TYPE_TASK,
};

struct graph_plot;

struct plot_info {
	gboolean		line;
	int			lcolor;
	unsigned long long	ltime;
	gboolean		box;
	int			bcolor;
	unsigned long long	bstart;
	unsigned long long	bend;
	gboolean		bfill;
};

/*
 * match_time:
 *   Return true if a selected time should expose plot.
 *   Should only return true if an event has the exact time that
 *   is passed in.
 *
 * start:
 *   Initialize for plotting. This is called with the start time
 *   to start plotting.
 *
 * plot_event:
 *   This is called by the plotter.
 *   color returns the color that should be printed.
 *   line returns 1 or 0 if a line should be drawn.
 *   ltime returns the time that the line should be drawn at
 *    (ignored if line is 0)
 *   box returns 1 or 0 if a box should be drawn
 *    bstart is the time the box starts at
 *    bend is the time the box ends at
 *     (bstart and bend are ignored if box is 0)
 *    bfill whether or not to fill the box (default TRUE)
 *   time is the time of the current event
 *
 * end:
 *   called at the end of the plotting in case the plotter needs to
 *   release any resourses.
 * display_last_event:
 *   If enough space between the event before and the event after
 *   a event, the plot may ask to display that event.
 *   The time will be given to find the event, the time may be before
 *   the given event.
 *
 * find_record:
 *   return a tracecmd record for a given time.
 *
 * display_info:
 *   display information about a given time. A resolution is
 *   passed in to show how much time is in 1 pixel.
 *
 * destroy:
 *   destructor routine. Cleans up all resourses that the plot allocated.
 */
struct plot_callbacks {
	int (*match_time)(struct graph_info *, struct graph_plot *,
			  unsigned long long time);
	void (*start)(struct graph_info *, struct graph_plot *,
		      unsigned long long time);
	int (*plot_event)(struct graph_info *ginfo,
			  struct graph_plot *plot,
			  struct pevent_record *record,
			  struct plot_info *info);
	void (*end)(struct graph_info *, struct graph_plot *);
	int (*display_last_event)(struct graph_info *ginfo, struct graph_plot *plot,
				  struct trace_seq *s, unsigned long long time);
	struct pevent_record *(*find_record)(struct graph_info *, struct graph_plot *,
				      unsigned long long time);
	int (*display_info)(struct graph_info *, struct graph_plot *,
			    struct trace_seq *s,
			    unsigned long long time);
	void (*destroy)(struct graph_info *, struct graph_plot *);
};

struct graph_plot {
	enum graph_plot_type		type;
	int				pos;
	char				*label;
	const struct plot_callbacks	*cb;
	void				*private;

	/* Used for drawing */
	gint				 last_color;
	gint				p1, p2, p3;
	GdkGC				*gc;
};

struct graph_callbacks {
	graph_select_cb		*select;
	graph_filter_cb		*filter;
};

struct plot_list {
	struct plot_list	*next;
	struct graph_plot	*plot;
};

struct plot_hash {
	struct plot_hash	*next;
	struct plot_list	*plots;
	gint			val;
};

#define PLOT_HASH_SIZE 1024
#define TASK_HASH_SIZE 1024
struct task_list;

struct graph_info {
	struct tracecmd_input	*handle;
	struct pevent		*pevent;
	gint			cpus;

	gint			plots;
	struct graph_plot	**plot_array;	/* all plots */
	struct graph_plot	*plot_clicked;	/* plot that was clicked on */

	gint			nr_task_hash;
	struct plot_hash	*task_hash[PLOT_HASH_SIZE];
	struct plot_hash	*cpu_hash[PLOT_HASH_SIZE];
	struct plot_list	*all_recs;

	struct task_list	 *tasks[TASK_HASH_SIZE];

	GtkWidget		*widget;	/* Box to hold graph */
	GtkWidget		*status_hbox;	/* hbox holding status info */
	GtkWidget		*pointer_time;	/* time that pointer is at */
	GtkWidget		*cursor_label;	/* label showing cursor time */
	GtkWidget		*marka_label;	/* label showing Marker A time */
	GtkWidget		*markb_label;	/* label showing Marker B time */
	GtkWidget		*delta_label;	/* label showing delta of B - A */
	GtkWidget		*scrollwin;	/* graph scroll window */
	GtkWidget		*info_scrollwin; /* graph scroll window (for info widget) */
	GtkWidget		*info;		/* info window */
	GtkWidget		*draw;
	GdkPixmap		*curr_pixmap;	/* pixmap backstore */
	GdkPixmap		*info_pixmap;	/* pixmap backstore */
	GtkAdjustment		*hadj;		/* scrollwindow horizontal adjust */
	guint64			start_time;	/* True start time of trace */
	guint64			end_time;	/* True end time of trace */
	guint64			view_start_time; /* visible start time */
	guint64			view_end_time;	/* visible end time */
	gint			start_x;	/* virutal start of visible area */

	guint64			cursor;		/* time of cursor (double clicked) */

	gdouble			resolution;	/* pixels / time */

	gint			press_x;	/* x where button is pressed */
	gint			last_x;		/* last x seen while moving mouse */
	gboolean		line_active;	/* set when button is pressed */
	guint64			line_time;	/* time line of where line_active is set */
	guint64			marka_time;	/* time that marker A is at */
	guint64			markb_time;	/* time that marker B is at */
	gboolean		show_marka;	/* draw marker A line */
	gboolean		show_markb;	/* draw marker B line */
	gboolean		zoom;		/* set when shift button is pressed */

	gdouble			hadj_value;	/* value to set hadj width */
	gdouble			hadj_page_size;	/* visible size to set hadj */

	gint			draw_width;	/* width of pixmap */
	gint			draw_height;	/* height of pixmap */
	gint			full_width;	/* width of full trace in pixels */
						/* This includes non visible part of trace */

	struct graph_callbacks	*callbacks;	/* call back hooks for changes to graph */

	gboolean		filter_enabled;
	gboolean		filter_available;

	gboolean		all_events;	/* all events enabled */
	struct event_filter	*event_filter;	/* filtered events */

	/* cache of event fields */
	gint			ftrace_sched_switch_id;
	gint			event_sched_switch_id;
	gint			event_wakeup_id;
	gint			event_wakeup_new_id;
	struct format_field	*event_prev_state;
	struct format_field	*event_pid_field;
	struct format_field	*event_comm_field;
	struct format_field	*ftrace_pid_field;
	struct format_field	*ftrace_comm_field;
	struct format_field	*wakeup_pid_field;
	struct format_field	*wakeup_success_field;
	struct format_field	*wakeup_new_pid_field;
	struct format_field	*wakeup_new_success_field;

	gboolean		read_comms;	/* Read all comms on first load */

	struct filter_task	*task_filter;
	gint			filter_task_selected;

	struct filter_task	*hide_tasks;

	/* Box info for plot data info window */
	gint			plot_data_x;
	gint			plot_data_y;
	gint			plot_data_w;
	gint			plot_data_h;
};


struct graph_info *
trace_graph_create(struct tracecmd_input *handle);
struct graph_info *
trace_graph_create_with_callbacks(struct tracecmd_input *handle,
				  struct graph_callbacks *cbs);
void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time);

void trace_graph_event_filter_callback(gboolean accept,
				       gboolean all_events,
				       gchar **systems,
				       gint *events,
				       gpointer data);

void trace_graph_adv_filter_callback(gboolean accept,
				     const gchar *text,
				     gint *event_ids,
				     gpointer data);

static inline GtkWidget *trace_graph_get_draw(struct graph_info *ginfo)
{
	return ginfo->draw;
}

static inline struct graph_callbacks *trace_graph_get_callbacks(struct graph_info *ginfo)
{
	return ginfo->callbacks;
}

static inline GtkWidget *trace_graph_get_window(struct graph_info *ginfo)
{
	return ginfo->widget;
}

void trace_graph_refresh(struct graph_info *ginfo);

struct filter_task_item *
trace_graph_filter_task_find_pid(struct graph_info *ginfo, gint pid);
struct filter_task_item *
trace_graph_hide_task_find_pid(struct graph_info *ginfo, gint pid);
void trace_graph_filter_toggle(struct graph_info *ginfo);
void trace_graph_filter_add_remove_task(struct graph_info *info,
					gint pid);
void trace_graph_filter_hide_show_task(struct graph_info *ginfo,
				       gint pid);
void trace_graph_clear_tasks(struct graph_info *ginfo);
void trace_graph_free_info(struct graph_info *ginfo);
int trace_graph_load_handle(struct graph_info *ginfo,
			    struct tracecmd_input *handle);

int trace_graph_check_sched_switch(struct graph_info *ginfo,
				   struct pevent_record *record,
				   gint *pid, const char **comm);
int trace_graph_check_sched_wakeup(struct graph_info *ginfo,
				   struct pevent_record *record,
				   gint *pid);
gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid);
gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct pevent_record *record);

void trace_graph_copy_filter(struct graph_info *ginfo,
			     gboolean all_events,
			     struct event_filter *event_filter);
gint *trace_graph_task_list(struct graph_info *ginfo);

int trace_graph_load_filters(struct graph_info *ginfo,
			     struct tracecmd_xml_handle *handle);
int trace_graph_save_filters(struct graph_info *ginfo,
			     struct tracecmd_xml_handle *handle);
void trace_graph_update_filters(struct graph_info *ginfo,
				struct filter_task *task_filter,
				struct filter_task *hide_tasks);
void trace_graph_refresh_filters(struct graph_info *ginfo);

/* plots */
void trace_graph_plot_free(struct graph_info *ginfo);
void trace_graph_plot_init(struct graph_info *ginfo);
struct graph_plot *trace_graph_plot_append(struct graph_info *ginfo,
					   const char *label,
					   enum graph_plot_type type,
					   const struct plot_callbacks *cb,
					   void *data);
struct graph_plot *trace_graph_plot_insert(struct graph_info *ginfo,
					   int pos,
					   const char *label,
					   enum graph_plot_type type,
					   const struct plot_callbacks *cb,
					   void *data);
void trace_graph_plot_remove(struct graph_info *ginfo, struct graph_plot *plot);
struct plot_hash *trace_graph_plot_find_task(struct graph_info *ginfo, gint task);
void trace_graph_plot_add_task(struct graph_info *ginfo, struct graph_plot *plot,
			       gint task);
void trace_graph_plot_remove_task(struct graph_info *ginfo,
				  struct graph_plot *plot,
				  gint task);
struct plot_hash *trace_graph_plot_find_cpu(struct graph_info *ginfo, gint cpu);
void trace_graph_plot_add_cpu(struct graph_info *ginfo, struct graph_plot *plot,
			      gint cpu);
void trace_graph_plot_remove_cpu(struct graph_info *ginfo, struct graph_plot *plot,
				 gint cpu);
void trace_graph_plot_add_all_recs(struct graph_info *ginfo,
				   struct graph_plot *plot);
void trace_graph_plot_remove_all_recs(struct graph_info *ginfo,
				      struct graph_plot *plot);

/* plot callbacks */
int trace_graph_plot_match_time(struct graph_info *ginfo,
				struct graph_plot *plot,
				unsigned long long time);

int trace_graph_plot_display_last_event(struct graph_info *ginfo,
					struct graph_plot *plot,
					struct trace_seq *s,
					unsigned long long time);

void trace_graph_plot_start(struct graph_info *ginfo,
			    struct graph_plot *plot,
			    unsigned long long time);

int trace_graph_plot_event(struct graph_info *ginfo,
			   struct graph_plot *plot,
			   struct pevent_record *record,
			   struct plot_info *info);

void trace_graph_plot_end(struct graph_info *ginfo,
			  struct graph_plot *plot);

struct pevent_record *
trace_graph_plot_find_record(struct graph_info *ginfo,
			     struct graph_plot *plot,
			     unsigned long long time);

int trace_graph_plot_display_info(struct graph_info *ginfo,
				  struct graph_plot *plot,
				  struct trace_seq *s,
				  unsigned long long time);

/* cpu plot */
void graph_plot_init_cpus(struct graph_info *ginfo, int cpus);
void graph_plot_cpus_plotted(struct graph_info *ginfo,
			     gboolean *all_cpus, guint64 **cpu_mask);
void graph_plot_cpus_update_callback(gboolean accept,
				     gboolean all_cpus,
				     guint64 *selected_cpu_mask,
				     gpointer data);

/* task plot */
void graph_plot_task(struct graph_info *ginfo, int pid, int pos);
void graph_plot_task_update_callback(gboolean accept,
				     gint *selected,
				     gint *non_select,
				     gpointer data);
void graph_plot_task_plotted(struct graph_info *ginfo,
			     gint **plotted);

#endif /* _TRACE_GRAPH_H */
