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

const GdkPoint p_quadratic_1 = {50, 100};
const GdkPoint p_quadratic_2 = {400,175};
const GdkPoint p_quadratic_c = {175, 10};

const GdkPoint p_cubic_1 = {50, 300};
const GdkPoint p_cubic_2 = {400,375};
const GdkPoint p_cubic_c1 = {175, 210};
const GdkPoint p_cubic_c2 = {375, 310};

#define segments 25
GdkPoint quadratic[segments + 1];
GdkPoint cubic[segments + 1];

gint segcount = 0;

static gboolean
layouy_expose_event (GtkWidget      *widget,
                     GdkEventExpose *event,
                     gpointer        user_data)
{
	GdkWindow *window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
	GdkGC *gc = gdk_gc_new (GDK_DRAWABLE (window));
	gint width;
	gint height;
	GdkColor color;
	gdk_gc_set_clip_region (gc, event->region);

	color.pixel = 0;
	color.red = 0xffff;
	color.green = 0xffff;
	color.blue = 0xffff;

	gdk_gc_set_rgb_fg_color (gc, &color);

	gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);

	gdk_draw_rectangle
	(
		GDK_DRAWABLE (window),
		gc,
		TRUE,
		0,
		0,
		width,
		height
	);

	{ /* quadratic Bézier curve */
		GdkPoint a[4];

		GdkPoint p;
		GdkPoint q[2];

		p.x = (p_quadratic_2.x - p_quadratic_1.x)*segcount / (segments) + p_quadratic_1.x;
		p.y = (p_quadratic_2.y - p_quadratic_1.y)*segcount / (segments) + p_quadratic_1.y;

		q[0].x = (p_quadratic_c.x - p_quadratic_1.x) * segcount / segments + p_quadratic_1.x;
		q[0].y = (p_quadratic_c.y - p_quadratic_1.y) * segcount / segments + p_quadratic_1.y;
		q[1].x = (p_quadratic_2.x - p_quadratic_c.x) * segcount / segments + p_quadratic_c.x;
		q[1].y = (p_quadratic_2.y - p_quadratic_c.y) * segcount / segments + p_quadratic_c.y;

		quadratic[segcount].x = (q[1].x - q[0].x) * segcount / segments + q[0].x;
		quadratic[segcount].y = (q[1].y - q[0].y) * segcount / segments + q[0].y;

		a[0].x = p_quadratic_1.x;
		a[0].y = p_quadratic_1.y;
		a[1].x = p_quadratic_2.x;
		a[1].y = p_quadratic_2.y;
		a[2].x = p_quadratic_c.x;
		a[2].y = p_quadratic_c.y;
		a[3].x = p_quadratic_1.x;
		a[3].y = p_quadratic_1.y;

		color.pixel = 0;
		color.red = 0xafff;
		color.green = 0xafff;
		color.blue = 0xafff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 1;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                a, 4);

		color.pixel = 0;
		color.red = 0x7fff;
		color.green = 0xffff;
		color.blue = 0x7fff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                q,
		                2);

		color.pixel = 0;
		color.red = 0xffff;
		color.green = 0x7fff;
		color.blue = 0xffff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_line (window,
		                gc,
		                p.x, p.y,
		                quadratic[segcount].x, quadratic[segcount].y);



		color.pixel = 0;
		color.red = 0xffff;
		color.green = 0x7fff;
		color.blue = 0x7fff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                quadratic, segcount+1);
	}

	{ /* cubic Bézier curve */
		GdkPoint a[5];
		GdkPoint p;
		GdkPoint q[3];
		GdkPoint r[2];

		p.x = (p_cubic_2.x - p_cubic_1.x)*segcount / (segments) + p_cubic_1.x;
		p.y = (p_cubic_2.y - p_cubic_1.y)*segcount / (segments) + p_cubic_1.y;

		q[0].x = (p_cubic_c1.x - p_cubic_1.x) * segcount / segments + p_cubic_1.x;
		q[0].y = (p_cubic_c1.y - p_cubic_1.y) * segcount / segments + p_cubic_1.y;
		q[1].x = (p_cubic_c2.x - p_cubic_c1.x) * segcount / segments + p_cubic_c1.x;
		q[1].y = (p_cubic_c2.y - p_cubic_c1.y) * segcount / segments + p_cubic_c1.y;
		q[2].x = (p_cubic_2.x - p_cubic_c2.x) * segcount / segments + p_cubic_c2.x;
		q[2].y = (p_cubic_2.y - p_cubic_c2.y) * segcount / segments + p_cubic_c2.y;

		r[0].x = (q[1].x - q[0].x) * segcount / segments + q[0].x;
		r[0].y = (q[1].y - q[0].y) * segcount / segments + q[0].y;
		r[1].x = (q[2].x - q[1].x) * segcount / segments + q[1].x;
		r[1].y = (q[2].y - q[1].y) * segcount / segments + q[1].y;

		cubic[segcount].x = (r[1].x - r[0].x) * segcount / segments + r[0].x;
		cubic[segcount].y = (r[1].y - r[0].y) * segcount / segments + r[0].y;

		a[0].x = p_cubic_1.x;
		a[0].y = p_cubic_1.y;
		a[1].x = p_cubic_2.x;
		a[1].y = p_cubic_2.y;
		a[2].x = p_cubic_c2.x;
		a[2].y = p_cubic_c2.y;
		a[3].x = p_cubic_c1.x;
		a[3].y = p_cubic_c1.y;
		a[4].x = p_cubic_1.x;
		a[4].y = p_cubic_1.y;

		color.pixel = 0;
		color.red = 0xafff;
		color.green = 0xafff;
		color.blue = 0xafff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 1;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                a, 5);

		color.pixel = 0;
		color.red = 0x7fff;
		color.green = 0xffff;
		color.blue = 0x7fff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                q,
		                3);

		color.pixel = 0;
		color.red = 0x7fff;
		color.green = 0x7fff;
		color.blue = 0xffff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                r,
		                2);

		color.pixel = 0;
		color.red = 0xffff;
		color.green = 0x7fff;
		color.blue = 0xffff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_line (window,
		               gc,
		               p.x, p.y,
		               cubic[segcount].x, cubic[segcount].y);

		color.pixel = 0;
		color.red = 0xffff;
		color.green = 0x7fff;
		color.blue = 0x7fff;

		gdk_gc_set_rgb_fg_color (gc, &color);
		{
			GdkGCValues v;
			v.line_width = 2;
			gdk_gc_set_values (gc, &v, GDK_GC_LINE_WIDTH);
		}

		gdk_draw_lines (window,
		                gc,
		                cubic, segcount+1);
	}

	gdk_gc_unref (gc);

	return FALSE;
}

static gboolean
window_delete_event (GtkWidget *widget,
                     GdkEvent  *event,
                     gpointer   user_data)
{
	gtk_main_quit ();
	return FALSE;
}

static gboolean
timer (gpointer user_data)
{
	GdkWindow *window = gtk_layout_get_bin_window (GTK_LAYOUT (user_data));

	segcount = (segcount + 1) % (segments + 1);

	gdk_window_invalidate_rect (window, NULL, FALSE);

	return TRUE;
}

int main(int argc, char *argv[])
{
	GtkWidget *window;
	GtkWidget *layout;

	gtk_init (&argc, &argv);

	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
	layout = gtk_layout_new (NULL, NULL);
	gtk_container_add (GTK_CONTAINER (window), layout);

	g_signal_connect (window, "delete-event", G_CALLBACK (window_delete_event), NULL);
	g_signal_connect (layout, "expose-event", G_CALLBACK (layout_expose_event), NULL);

	gtk_widget_show_all (window);

	g_timeout_add (200, timer, layout);

	gtk_main ();

	return 0;
}

