/*
 * Copyright (C) 2015 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <assert.h>
#include <stdio.h>
#include <gtk/gtk.h>

#include "glue.h"
#include "glue-gui-gtk.h"

#include "chip_st_lsm303dlhc_gui_gtk.h"

#define COMP		"chip_st_lsm303dlhc"
#define COMP_(x)	chip_st_lsm303dlhc_gui_gtk_ ## x

#define ACC_MAX		16.0
#define MAG_MAX		8.1

struct cpssp {
	GtkAdjustment *gui_acc_x;
	GtkAdjustment *gui_acc_y;
	GtkAdjustment *gui_acc_z;
	GtkAdjustment *gui_mag_x;
	GtkAdjustment *gui_mag_y;
	GtkAdjustment *gui_mag_z;

	struct sig_integer *port_acc_x;
	struct sig_integer *port_acc_y;
	struct sig_integer *port_acc_z;
	struct sig_integer *port_mag_x;
	struct sig_integer *port_mag_y;
	struct sig_integer *port_mag_z;
};

/*
 * Simulator Callbacks
 */
static void
COMP_(acc_x_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * ACC_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_acc_x, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_acc_x, "changed");
}

static void
COMP_(acc_y_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * ACC_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_acc_y, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_acc_y, "changed");
}

static void
COMP_(acc_z_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * ACC_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_acc_z, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_acc_z, "changed");
}

static void
COMP_(mag_x_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * MAG_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_mag_x, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_mag_x, "changed");
}

static void
COMP_(mag_y_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * MAG_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_mag_y, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_mag_y, "changed");
}

static void
COMP_(mag_z_sim_set)(void *_cpssp, int value)
{
	struct cpssp *cpssp = _cpssp;
	double val;

	assert(-1023 <= value && value <= 1023);

	val = (double) value * MAG_MAX / 1023;

	gtk_adjustment_set_value(cpssp->gui_mag_z, (gdouble) val);
	g_signal_emit_by_name(cpssp->gui_mag_z, "changed");
}

/*
 * GUI  Callbacks
 */
static void
COMP_(acc_x_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-ACC_MAX <= adj->value && adj->value <= ACC_MAX);

	val = (int) (adj->value / ACC_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_acc_x, cpssp, val);
}

static void
COMP_(acc_y_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-ACC_MAX <= adj->value && adj->value <= ACC_MAX);

	val = (int) (adj->value / ACC_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_acc_y, cpssp, val);
}

static void
COMP_(acc_z_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-ACC_MAX <= adj->value && adj->value <= ACC_MAX);

	val = (int) (adj->value / ACC_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_acc_z, cpssp, val);
}

static void
COMP_(mag_x_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-MAG_MAX <= adj->value && adj->value <= MAG_MAX);

	val = (int) (adj->value / MAG_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_mag_x, cpssp, val);
}

static void
COMP_(mag_y_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-MAG_MAX <= adj->value && adj->value <= MAG_MAX);

	val = (int) (adj->value / MAG_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_mag_y, cpssp, val);
}

static void
COMP_(mag_z_gui_set)(GtkAdjustment *adj, void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	int val;

	assert(-MAG_MAX <= adj->value && adj->value <= MAG_MAX);

	val = (int) (adj->value / MAG_MAX * 1023.0);

	assert(-1023 <= val && val <= 1023);

	sig_integer_set(cpssp->port_mag_z, cpssp, val);
}

void *
COMP_(create)(
	unsigned int page,
	const char *name,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_gnd,
	struct sig_std_logic *port_vdd,
	struct sig_std_logic *port_vdd_io,
	struct sig_std_logic *port_scl,
	struct sig_std_logic *port_sda,
	struct sig_std_logic *port_int1,
	struct sig_std_logic *port_int2,
	struct sig_std_logic *port_c1,
	struct sig_std_logic *port_drdy,
	struct sig_std_logic *port_setp,
	struct sig_std_logic *port_setc,
	struct sig_std_logic *port_R1,
	struct sig_std_logic *port_R2,
	struct sig_std_logic *port_R3,
	struct sig_integer *port_acc_x,
	struct sig_integer *port_acc_y,
	struct sig_integer *port_acc_z,
	struct sig_integer *port_mag_x,
	struct sig_integer *port_mag_y,
	struct sig_integer *port_mag_z
)
{
	static const struct sig_integer_funcs acc_x_funcs = {
		.set = COMP_(acc_x_sim_set),
	};
	static const struct sig_integer_funcs acc_y_funcs = {
		.set = COMP_(acc_y_sim_set),
	};
	static const struct sig_integer_funcs acc_z_funcs = {
		.set = COMP_(acc_z_sim_set),
	};
	static const struct sig_integer_funcs mag_x_funcs = {
		.set = COMP_(mag_x_sim_set),
	};
	static const struct sig_integer_funcs mag_y_funcs = {
		.set = COMP_(mag_y_sim_set),
	};
	static const struct sig_integer_funcs mag_z_funcs = {
		.set = COMP_(mag_z_sim_set),
	};
	struct cpssp *cpssp;
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkWidget *hbox2;
	GtkWidget *label;
	GtkWidget *scale;

	cpssp = shm_alloc(sizeof(*cpssp));
	assert(cpssp);

	hbox = gtk_hbox_new(FALSE, 1);

	vbox = gtk_vbox_new(FALSE, 1);
	label = gtk_label_new("Accelerometer");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

#define SLIDER(v, n1, n2) \
	hbox2 = gtk_hbox_new(FALSE, 1); \
	label = gtk_label_new(n1); \
	gtk_widget_show(label); \
	gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0); \
 \
	cpssp->gui_ ## n2 = (GtkAdjustment *) gtk_adjustment_new(0.0, /* value */ \
			-v, /* lower */ \
			v + v / 10.0, /* upper + pagesize */ \
			v / 20.0, /* step increment */ \
			v / 10.0, /* page increment */ \
			v / 10.0 /* page size */); \
	g_signal_connect(cpssp->gui_ ## n2, "value_changed", \
			G_CALLBACK(COMP_(n2 ## _gui_set)), cpssp); \
	scale = gtk_hscale_new(cpssp->gui_ ## n2); \
	gtk_widget_show(scale); \
	gtk_box_pack_start(GTK_BOX(hbox2), scale, TRUE, TRUE, 0); \
 \
	gtk_widget_show(hbox2); \
	gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);

	SLIDER(ACC_MAX, "X", acc_x)
	SLIDER(ACC_MAX, "Y", acc_y)
	SLIDER(ACC_MAX, "Z", acc_z)

	gtk_widget_show(vbox);
	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, FALSE, 5);

	vbox = gtk_vbox_new(FALSE, 1);
	label = gtk_label_new("Magnetometer");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

	SLIDER(MAG_MAX, "X", mag_x)
	SLIDER(MAG_MAX, "Y", mag_y)
	SLIDER(MAG_MAX, "Z", mag_z)

#undef SLIDER

	gtk_widget_show(vbox);
	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, FALSE, 5);

	gtk_widget_show(hbox);

	gui_gtk_comp_add(page, COMP, name, hbox, FALSE, FALSE, NULL);

	/* Out */
	cpssp->port_acc_x = port_acc_x;
	cpssp->port_acc_y = port_acc_y;
	cpssp->port_acc_z = port_acc_z;
	cpssp->port_mag_x = port_mag_x;
	cpssp->port_mag_y = port_mag_y;
	cpssp->port_mag_z = port_mag_z;

	/* In */
	sig_integer_connect_in(port_acc_x, cpssp, &acc_x_funcs);
	sig_integer_connect_in(port_acc_y, cpssp, &acc_y_funcs);
	sig_integer_connect_in(port_acc_z, cpssp, &acc_z_funcs);
	sig_integer_connect_in(port_mag_x, cpssp, &mag_x_funcs);
	sig_integer_connect_in(port_mag_y, cpssp, &mag_y_funcs);
	sig_integer_connect_in(port_mag_z, cpssp, &mag_z_funcs);

	return cpssp;
}

void
COMP_(destroy)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	/* FIXME */

	shm_free(cpssp);
}

void
COMP_(suspend)(void *_cpssp, FILE *fp)
{
	/* FIXME */
}

void
COMP_(resume)(void *_cpssp, FILE *fp)
{
	/* FIXME */
}
