/**************************************************************

cmpack_chart_view.h (C-Munipack project)
Widget which can draw a chart (derived from GtkWidget)
Copyright (C) 2011 David Motl, dmotl@volny.cz

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

$Id: cmpack_chart_view.h,v 1.5 2016/05/08 09:14:54 dmotl Exp $

**************************************************************/

#ifndef CMPACK_CHART_VIEW_H
#define CMPACK_CHART_VIEW_H

#include <gtk/gtk.h>

#include "cmpack_chart_data.h"
#include "cmpack_image_data.h"
#include "cmpack_entity.h"

G_BEGIN_DECLS

#define CMPACK_TYPE_CHART_VIEW            (cmpack_chart_view_get_type ())
#define CMPACK_CHART_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CMPACK_TYPE_CHART_VIEW, CmpackChartView))
#define CMPACK_CHART_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CMPACK_TYPE_CHART_VIEW, CmpackChartViewClass))
#define CMPACK_IS_CHART_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CMPACK_TYPE_CHART_VIEW))
#define CMPACK_IS_CHART_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CMPACK_TYPE_CHART_VIEW))
#define CMPACK_CHART_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), CMPACK_TYPE_CHART_VIEW, CmpackChartViewClass))

typedef struct _CmpackChartView           CmpackChartView;
typedef struct _CmpackChartViewClass      CmpackChartViewClass;

/* Mouse control mode */
typedef enum {
	CHART_MOUSE_NONE,
	CHART_MOUSE_ZOOM,
	CHART_MOUSE_SELECT,
	CHART_MOUSE_SHIFT,
	CHART_MOUSE_HSHIFT,
	CHART_MOUSE_VSHIFT,
	CHART_MOUSE_NEW_PROFILE,
	CHART_MOUSE_MOVE_PROFILE,
	CHART_MOUSE_MOVE_START,
	CHART_MOUSE_MOVE_END
} CmpackChartMouseMode;

/* Mouse position */
typedef enum {
	CHART_MOUSE_OUTSIDE	= 0,
	CHART_MOUSE_HSCALE  = (1<<0),
	CHART_MOUSE_VSCALE	= (1<<1),
	CHART_MOUSE_CANVAS	= (1<<2),
	CHART_MOUSE_CHART	= (1<<3),
	CHART_MOUSE_PROFILE	= (1<<4),
	CHART_MOUSE_PSTART	= (1<<5),
	CHART_MOUSE_PEND	= (1<<6)
} CmpackChartMousePos;

/* Cursor types */
typedef enum {
	CHART_CURSOR_DEFAULT	= 0,
	CHART_CURSOR_ARROW_WE	= 1,
	CHART_CURSOR_ARROW_NS	= 2,
	CHART_CURSOR_ARROW_NSWE = 3,
	CHART_CURSOR_CROSSHAIR	= 4
} CmpackChartCursor;

/* Profile point */
typedef enum {
	CHART_PROFILE_START,			// Profile start point
	CHART_PROFILE_END,				// Profile end point
	CHART_PROFILE_NPOINTS			// Number of profile definition points
} CmpackChartProfilePoint;

/* Mapping parameters */
typedef struct _CmpackChartViewAxis
{
	gdouble		Min, Max;			// Scroll limits in chart units
	gdouble		ProjMin, ProjMax;	// Scroll limits in projection units
	gboolean	Reverse;			// Flip axis direction
	gdouble		ProjPos;			// Center of viewfield in projection units
} CmpackChartViewAxis;

/* Item data */
typedef struct _CmpackChartViewItem
{
	gboolean    visible;			// Is this item valid?
	gboolean	enabled;			// Item can be activated or selected
	gboolean	selected;			// Is this point selected?
	gboolean	filled;				// Filled marker
	gboolean	topmost;			// Topmost item
	gboolean	selected_before_rubberbanding;	// Has this point been selected before rubberbanding?
	gdouble		xproj, yproj;		// Object's center in projection units
	gdouble		size;				// Position and size of the marker
	gchar		*tag_text;			// Tag (displayed text)
	CmpackColor	color;				// Color
} CmpackChartViewItem; 

/* Overlay object definition */
typedef struct _CmpackChartEntity
{
	gint		handle;             // Entity identifier
	gint		layer;              // Layer identifier
	CmpackEntity *entity;           // Entity definition
} CmpackChartEntity;

/* Layer definition */
typedef struct _CmpackChartLayer
{
	gint		handle;             // Layer identifier
	gboolean	visible;            // Show objects from this layer
    gboolean    show_tags;          // Show object's tag 
	GSList		*entities;          // List of items
} CmpackChartLayer;

/* Chart view */
struct _CmpackChartView
{
	GtkWidget		parent;

	/* Private data */
	gint			width, height;
	GtkSelectionMode selection_mode;
	CmpackActivationMode activation_mode;
	gboolean		mouse_ctrl;

	guint			timer_id;
	guint			cursor_phase;

	gboolean		dirty;
	GdkPixmap		*offscreen_pixmap;

	CmpackChartData	*model;
	CmpackImageData	*image;

	CmpackChartViewItem *items;
	gint			item_count, item_capacity;
	gint			focused_item;
	gint			last_single_clicked; 

	GSList			*layers;
	gint			last_layer, last_entity;
	GHashTable		*ht_layers, *ht_entities;

	GtkAdjustment	*hadjustment;
	GtkAdjustment	*vadjustment;

	guint			mouse_pos;
	CmpackChartCursor cursor;
	CmpackChartMouseMode mouse_mode;
	gint			mouse_start_x, mouse_start_y;
	gint			mouse_pos_x, mouse_pos_y;
	gdouble			mouse_ref_x, mouse_ref_y;
	gint			last_mouse_x, last_mouse_y;

	CmpackChartViewAxis x, y;				// Axis parameters
	gdouble			zoom_base;				// Zoom base
	gdouble			zoom_pos;				// Actual zoom position
	gdouble			zoom_min, zoom_max;		// Zoom limit
	gdouble			pxl_size;				// Size of device unit in projection units

	gboolean		auto_zoom;				// Auto zoom mode
	gboolean		show_labels;			// Show labels
	gboolean		show_grid;				// Show grid lines
	gboolean		negative;				// Negative chart

	GdkRectangle	canvas_rc;
	GdkRectangle	xscale_rc;
	GdkRectangle	yscale_rc;

	GdkColor		*int_colors_dk;			// Colors for charts with dark background and light objects
	GdkColor		*int_colors_lt;			// Colors for charts with light background and dark objects
	GSList			*custom_colors;			// List of pointers to GdkColor, list of allocated custom colors

	gboolean		profile_enabled, profile_valid;
	gboolean		profile_sticky;
	gdouble			profile_x[CHART_PROFILE_NPOINTS], profile_y[CHART_PROFILE_NPOINTS];
};

/* Chart view class */
struct _CmpackChartViewClass
{
  GtkWidgetClass parent_class;

  /* Signals */
  void (*set_scroll_adjustments)(CmpackChartView *chart_view, 
	  GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
  void (*item_activated)(CmpackChartView *chart_view, gint row);
  void (*selection_changed)(CmpackChartView *chart_view);
  void (*mouse_moved)(CmpackChartView *chart_view);
  void (*mouse_left)(CmpackChartView *chart_view);
  void (*zoom_changed)(CmpackChartView *chart_view);
  void (*profile_changed)(CmpackChartView *chart_view);
};

GType cmpack_chart_view_get_type(void) G_GNUC_CONST;

/* -------------------------   PUBLIC FUNCTIONS   --------------------------------- */

/* Create a new chart view (makes a new data model) 
 * Returns floating reference to the chart view
 */
GtkWidget *cmpack_chart_view_new(void);

/* Create a new chart view and link it to given data model
 * params:
 *	chart_data				- [in] data model
 * Returns floating reference to the chart view
 */
GtkWidget *cmpack_chart_view_new_with_model(CmpackChartData *chart_data);

/* ------------------------   DATA MODELS   -------------------------------------- */

/* Change the data model which the view is connected to
 * params:
 *	chart_view				- [in] chart view
 *	chart_data				- [in] new data model
 */
void cmpack_chart_view_set_model(CmpackChartView *chart_view, 
	CmpackChartData *chart_data);

/* Get the data model with the view is connected to
 * params:
 *	chart_view				- [in] chart view
 * Returns borrowed reference to the current data model
 */
CmpackChartData* cmpack_chart_view_get_model(CmpackChartView *chart_view);

/* Change the image model which the view is connected to
 * params:
 *	chart_view				- [in] chart view
 *	chart_data				- [in] new data model
 */
void cmpack_chart_view_set_image(CmpackChartView *chart_view, 
	CmpackImageData *image_data);

/* Get the image model with the view is connected to
 * params:
 *	chart_view				- [in] chart view
 * Returns borrowed reference to the current image model
 */
CmpackImageData* cmpack_chart_view_get_image(CmpackChartView *chart_view);

/* ------------------------   RENDERING OPTIONS   -------------------------------------- */

/* Set mapping direction
 * params:
 *	chart_view				- [in] chart view
 *	orientation				- [in] packed bitmask of CmpackOrientation flags
 */
void cmpack_chart_view_set_orientation(CmpackChartView *chart_view, CmpackOrientation orientation);

/* Set scale visibility and position for specified axis
 * params:
 *	chart_view				- [in] chart view
 *	show					- [in] show scales
 */
void cmpack_chart_view_show_scales(CmpackChartView *chart_view, gboolean show);

/* Set grid visibility for specified axis
 * params:
 *	chart_view				- [in] chart view
 *	x_axis					- [in] show x-axis grid
 *	y_axis					- [in] show y-axis grid
 */
void cmpack_chart_view_show_grid(CmpackChartView *chart_view, gboolean show);

/* Set drawing style
 * params:
 *	chart_view				- [in] chart view
 *	negative				- [in] TRUE = white sky, black stars, FALSE = black sky, white stars
*/
void cmpack_chart_view_set_negative(CmpackChartView*chart_view, gboolean negative);

/* Get drawing style
 * params:
 *	chart_view				- [in] chart view
 * Returns current drawing style (see cmpack_chart_view_set_negative)
*/
gboolean cmpack_chart_view_get_negative(CmpackChartView *chart_view);

/* ------------------------   VIEWPORT PARAMS   -------------------------------------- */

/* Change zoom position
 * params:
 *	chart_view				- [in] chart view
 *	zoom					- [in] new zoom coefficient
 */
void cmpack_chart_view_set_zoom(CmpackChartView *chart_view, gdouble zoom);

/* Get current zoom position
 * params:
 *	chart_view				- [in] chart view
 * Returns current zoom coefficient
 */
gdouble cmpack_chart_view_get_zoom(CmpackChartView *chart_view);

/* Get minimum and maximum zoom position
 * params:
 *	chart_view				- [in] chart view
 *	min, max				- [out] zoom limits in physical units
 * Returns current zoom coefficient
 */
void cmpack_chart_view_get_zoom_limits(CmpackChartView *chart_view, gdouble *min, gdouble *max);

/* Change magnification
 * params:
 *	chart_view				- [in] chart view
 *	zoom					- [in] new magnification
 */
void cmpack_chart_view_set_magnification(CmpackChartView *chart_view, gdouble magnification);

/* Get current magnification
 * params:
 *	chart_view				- [in] chart view
 * Returns current zoom coefficient
 */
gdouble cmpack_chart_view_get_magnification(CmpackChartView *chart_view);

/* Move chart
 * params:
 *	chart_view				- [in] chart view
 *	x, y					- [in] new position of the center of the viewfield
 */
void cmpack_chart_view_set_offset(CmpackChartView *chart_view, gdouble x, gdouble y);

/* Get position
 * params:
 *	chart_view				- [in] chart view
 *	x, y					- [out] position of the center of the viewfield (in physical units)
 */
gboolean cmpack_chart_view_get_offset(CmpackChartView *chart_view, gdouble *x, gdouble *y);

/* Fit chart to window, enable auto-zoom mode 
 * params:
 *	chart_view				- [in] chart view
 *	enable					- [in] true = enable, false = disable
 */
void cmpack_chart_view_set_auto_zoom(CmpackChartView *chart_view, gboolean enable);

/* Read current status of auto-zoom mode
 * params:
 *	chart_view				- [in] chart view
 *	Returns TRUE if auto-zoom is enabled, FALSE otherwise
 */
gboolean cmpack_chart_view_get_auto_zoom(CmpackChartView *chart_view);

/* Convert physical coordinates to viewport coordinates
 * params:
 *	chart_view				- [in] chart view
 *	x, y					- [in] coordinates in physical units
 *	u, v					- [out] coordinates in pixels
 */
gboolean cmpack_chart_view_to_viewport(CmpackChartView *chart_view, gdouble x, gdouble y, gdouble *u, gdouble *v);

/* Convert viewport coordinates to physical coordinates
 * params:
 *	chart_view				- [in] chart view
 *	u, v					- [in] coordinates in pixels
 *	x, y					- [out] coordinates in physical units
 */
gboolean cmpack_chart_view_from_viewport(CmpackChartView *chart_view, gdouble u, gdouble v, gdouble *x, gdouble *y);

/* -------------------------------   USER INTERACTION   ----------------------------------- */

/* Set selection mode
 * params:
 *	chart_view				- [in] chart view
 *	mode					- [in] new selection mode
*/
void cmpack_chart_view_set_selection_mode(CmpackChartView*chart_view, GtkSelectionMode mode);

/* Get current selection mode
 * params:
 *	chart_view				- [in] chart view
 * Returns one of GTK_SELECTION_xxx constants
*/
GtkSelectionMode cmpack_chart_view_get_selection_mode(CmpackChartView *chart_view);

/* Set activation mode. Items can be activated by single or double click. The default
 * is double-click activation.
 * params:
 *	graph_view				- [in] graph view
 *	mode					- [in] activation mode, one of CmpackActivationXXX constants
*/
void cmpack_chart_view_set_activation_mode(CmpackChartView *chart_view, CmpackActivationMode mode);

/* Get current activation mode.
 * params:
 *	graph_view				- [in] graph view
 * Returns one of CmpackActivationXXX constants
*/
CmpackActivationMode cmpack_chart_view_get_activation_mode(CmpackChartView *chart_view);

/* Enable/disable zooming and panning of the chart by mouse
 * params:
 *	chart_view				- [in] chart view
 *	enable					- [in] TRUE=enable, FALSE=disable
*/
void cmpack_chart_view_set_mouse_control(CmpackChartView *chart_view, gboolean enable);

/* Convert pixel coordinates to chart coordinates
 * params:
 *	chart_view				- [in] chart view
 *	x, y					- [in/out] pixel coordinates, chart coordinates
 * Returns TRUE if the point is inside a chart area
 */
gboolean cmpack_chart_view_mouse_pos(CmpackChartView *chart_view, gdouble *x, gdouble *y);

/* Get number of selected rows
 * params:
 *	chart_view				- [in] chart view
 * Returns number of selected rows
*/
gint cmpack_chart_view_get_selected_count(CmpackChartView *chart_view);

/* Get index of selected item (meaningful in single selection mode)
 * params:
 *	chart_view				- [in] chart view
 * Returns index of item or negative value if no item is focused
*/
gint cmpack_chart_view_get_selected(CmpackChartView *chart_view);

/* Get list of selected rows
 * params:
 *	chart_view				- [in] chart view
 * Returns pointer to the list. Cast list's data members to gint to get row indices.
 * The caller is responsible to call g_list_free on to destroy the list
*/
GList *cmpack_chart_view_get_selected_rows(CmpackChartView *chart_view);

/* Check if an item is selected
 * params:
 *	chart_view				- [in] chart view
 *	row						- [in] row index
 * Returns TRUE if the row is selected, FALSE otherwise
*/
gboolean cmpack_chart_view_is_selected(CmpackChartView *chart_view, gint row);

/* Select an item
 * chart_view				- [in] chart view
 * row						- [in] row index
 */
void cmpack_chart_view_select(CmpackChartView *chart_view, gint row);

/* Unselect an item
 * chart_view				- [in] chart view
 * row						- [in] row index
 */
void cmpack_chart_view_unselect(CmpackChartView *chart_view, gint row);

/* Select all items
 * chart_view				- [in] chart view
*/
void cmpack_chart_view_select_all(CmpackChartView *chart_view);

/* Clear the selection
 * chart_view				- [in] chart view
*/
void cmpack_chart_view_unselect_all(CmpackChartView *chart_view);

/* Get index of focused item
 * params:
 *	chart_view				- [in] chart view
 * Returns index of item or negative value if no item is selected
*/
gint cmpack_chart_view_get_focused(CmpackChartView *chart_view);

/* Emits the signal that an item was activated.
 * params:
 *	chart_view				- [in] chart view
 *	row						- [in] row index
*/
void cmpack_chart_view_item_activate(CmpackChartView *chart_view, gint row);

/* -------------------------------   LAYERS - MISC DRAWING   ----------------------------------- */

/* Add an overlay layer over the chart 
 * params:
 *	chart_view				- [in] chart view
 *	returns layer handle (>=0), which you can use in other functions 
*/
gint cmpack_chart_view_add_layer(CmpackChartView *chart_view);

/* Show and hide a layer
 * params:
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	show					- [in] true = show, false = hide
*/
void cmpack_chart_view_show_layer(CmpackChartView *chart_view, gint layer, gboolean show);

/* Show and hide object tags
 * params:
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	show					- [in] true = show, false = hide
*/
void cmpack_chart_view_show_tags(CmpackChartView* chart_view, gint layer, gboolean show);

/* Delete a layer
 * params:
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
*/
void cmpack_chart_view_delete_layer(CmpackChartView *chart_view, gint layer);

/* Remove all objects from a layer
 * params:
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
*/
void cmpack_chart_view_clear_layer(CmpackChartView *chart_view, gint layer);

/* Add a circle into a layer
 * params:
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	left, top				- [in] position of the circle in chart coordinates
 *	width, height			- [in] size of the circle
 *	color					- [in] pen color
 *	filled					- [in] if true, the circle is filled
 * Returns object handle, that can be used in subsequent operations
*/
gint cmpack_chart_view_add_circle(CmpackChartView *chart_view, gint layer, 
	gdouble left, gdouble top, gdouble width, gdouble height, CmpackColor color,
	gboolean filled);

/* Add qudratic polynomial curve into a layer
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	coeff					- [in] six polynom coefficients
 *	t0, t1					- [in] start and end point of the curve
 *	color					- [in] pen color
 * Polynomial function (x,y) = f(t), t in (t0, t1)
 *	x = coeff[0] + coeff[1] * t + coeff[2] * t^2
 *	y = coeff[3] + coeff[4] * t + coeff[5] * t^2
 * Returns object handle, that can be used in subsequent operations
 */
gint cmpack_chart_view_add_qpcurve(CmpackChartView *chart_view, gint layer, const gdouble *coeff,
	gdouble t0, gdouble t1, CmpackColor color);

/* Add a mark 
 * The marks are used to highlight positions of a moving target along the track.
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	x, y					- [in] origin in chart coordinates
 *	angle					- [in] tilt angle in degrees
 * Returns object handle, that can be used in subsequent operations
 */
gint cmpack_chart_view_add_mark(CmpackChartView *chart_view, gint layer, gdouble x, gdouble y,
	gdouble tilt, CmpackColor color);

/* Add an object 
 * This is used to show positions and names of objects from a catalog of stars, e.g. VSX 
 * over a frame. Objects are denoted by crossbars which does not scale.
 *	chart_view				- [in] chart view
 *	layer					- [in] layer handle
 *	x, y					- [in] origin in chart coordinates
 *	color					- [in] pen color
 *  caption					- [in] name of the object, label shown next to the mark (optional)
 * Returns object handle, that can be used in subsequent operations
 */
gint cmpack_chart_view_add_object(CmpackChartView *chart_view, gint layer, gdouble x, gdouble y,
	CmpackColor color, const gchar *caption);

/* -------------------------------   OBJECTS   ----------------------------------- */

/* Move an object
 * params:
 *	chart_view				- [in] chart view
 *	object					- [in] object handle
 *	left, top				- [in] new position of the reference point of the object
*/
void cmpack_chart_view_move_object(CmpackChartView *chart_view, gint object, 
	gdouble left, gdouble top);

/* Change a radius of a circle
 * params:
 *	chart_view				- [in] chart view
 *	object					- [in] object handle
 *	width, height			- [in] new size of the object in chart coordinates
*/
void cmpack_chart_view_resize_object(CmpackChartView *chart_view, gint object, 
	gdouble width, gdouble height);

/* Delete an object 
 * params:
 *	chart_view				- [in] chart view
 *	object					- [in] object handle
*/
void cmpack_chart_view_delete_object(CmpackChartView *chart_view, gint object);

/* Find object near specified position
 * params:
 *	chart_view				- [in] chart view
 *	x, y					- [in] chart coordinates 
 *	tolerance				- [in] max. distance from specified point
 *	object_id				- [out] object id
 * Returns TRUE if the object was found, FALSE otherwise
*/
gboolean cmpack_chart_view_object_at_pos(CmpackChartView *chart_view,
	gdouble x, gdouble y, gdouble tolerance, gint *object_id);

/* Change custom color for an object 
 * params:
 *	chart_view				- [in] chart view
 *	object					- [in] object handle
 *  red, green, blue		- [in] color components (0 - 1)
*/
void cmpack_chart_view_set_object_custom_color(CmpackChartView *chart_view, gint object,
	gdouble red, gdouble green, gdouble blue);

/* -------------------------------   PROFILES   ----------------------------------- */

/* Enable/disable profile
 * params:
 *	chart_view				- [in] chart view
 *	enable					- [in] true = enable, false = disable
*/
void cmpack_chart_view_profile_enable(CmpackChartView *chart_view, gboolean enable);

/* Clear the profile
 * params:
 *	chart_view				- [in] chart view
*/
void cmpack_chart_view_profile_clear(CmpackChartView *chart_view);

/* Get profile point
 * params:
 *	chart_view				- [in] chart view
 *	point					- [in] point to be retrieved
 *	x, y					- [out] point coordinates
 * Returns TRUE if the point is defined, FALSE otherwise
*/
gboolean cmpack_chart_view_profile_get_pos(CmpackChartView *chart_view, 
	CmpackChartProfilePoint point, gint *x, gint *y);

G_END_DECLS

#endif
