/* vim: set ts=4 sw=4 : */
#include <ruby.h>
#include <Evas.h>

#include "shared.h"

VALUE cEvasObject, cEORectangle, cEOImage, cEOText;

#define Get_EO() \
	Ruby_Evas *re; Ruby_Evas_Object *reo; \
	Evas_Object obj; \
	Data_Get_Struct(self, Ruby_Evas_Object, reo); \
	re = reo->re; obj = reo->obj;

static VALUE
eo_move(VALUE self, VALUE x, VALUE y) {
	Get_EO();
	evas_move(re->evas, obj, NUM2INT(x), NUM2INT(y));
	return Qnil;
}
static VALUE
eo_resize(VALUE self, VALUE w, VALUE h) {
	Get_EO();
	evas_resize(re->evas, obj, NUM2INT(w), NUM2INT(h));
	return Qnil;
}
static VALUE
eo_get_x(VALUE self) {
	double x;
	Get_EO();
	evas_get_geometry(re->evas, obj, &x, NULL, NULL, NULL);
	return INT2NUM(x);
}
static VALUE
eo_get_y(VALUE self) {
	double x;
	Get_EO();
	evas_get_geometry(re->evas, obj, NULL, &x, NULL, NULL);
	return INT2NUM(x);
}
static VALUE
eo_get_width(VALUE self) {
	double x;
	Get_EO();
	evas_get_geometry(re->evas, obj, NULL, NULL, &x, NULL);
	return INT2NUM(x);
}
static VALUE
eo_get_height(VALUE self) {
	double x;
	Get_EO();
	evas_get_geometry(re->evas, obj, NULL, NULL, NULL, &x);
	return INT2NUM(x);
}
static VALUE
eo_set_layer(VALUE self, VALUE layer) {
	Get_EO();
	evas_set_layer(re->evas, obj, NUM2INT(layer));
	return Qnil;
}
static VALUE
eo_set_color(VALUE self, VALUE red, VALUE green, VALUE blue, VALUE alpha) {
	Get_EO();
	evas_set_color(re->evas, obj, NUM2INT(red), NUM2INT(green), NUM2INT(blue), NUM2INT(alpha));
	return Qnil;
}
static VALUE
eo_set_alpha(VALUE self, VALUE alpha) {
	int red, green, blue;
	Get_EO();
	evas_get_color(re->evas, obj, &red, &green, &blue, NULL);
	evas_set_color(re->evas, obj, red, green, blue, NUM2INT(alpha));
	return Qnil;
}
static VALUE
eo_set_clip(VALUE self, VALUE clip) {
	Ruby_Evas_Object *reo_clip;
	Get_EO();

	Data_Get_Struct(clip, Ruby_Evas_Object, reo_clip);

	evas_set_clip(re->evas, obj, reo_clip->obj);
	return Qnil;
}
static VALUE
eo_show(VALUE self) {
	Get_EO();
	evas_show(re->evas, obj);
	return Qnil;
}
static VALUE
eo_hide(VALUE self) {
	Get_EO();
	evas_hide(re->evas, obj);
	return Qnil;
}
static VALUE
eo_del(VALUE self) {
	Get_EO();
	evas_del_object(re->evas, obj);
	obj = NULL;
	return Qnil;
}
static void
_evas_callback(void *data, Evas _evas, Evas_Object _obj, int b, int x, int y) {
	VALUE cb = (VALUE)data;
	rb_funcall(cb, rb_intern("call"), 3, INT2NUM(b), INT2NUM(x), INT2NUM(y));
}
static VALUE
eo_onevent(int argc, VALUE *argv, VALUE self) {
	VALUE type, cb;
	Get_EO();
	
	if (rb_block_given_p()) {
		rb_scan_args(argc, argv, "1&", &type, &cb);
	} else {
		rb_scan_args(argc, argv, "2", &type, &cb);
	}

	evas_callback_add(re->evas, obj, NUM2INT(type), _evas_callback, (void*)cb);

	if (!reo->callbacks)
		reo->callbacks = rb_ary_new();
	rb_ary_push(reo->callbacks, cb);

	return Qnil;
}
static void
rb_eo_mark(Ruby_Evas_Object *reo) {
	if (reo->callbacks)
		rb_gc_mark(reo->callbacks);
}
static VALUE
rb_eo_new(VALUE class, Ruby_Evas *re, VALUE arghash, Evas_Object eo) {
	VALUE obj;
	Ruby_Evas_Object *reo;

	obj = Data_Make_Struct(class, Ruby_Evas_Object, rb_eo_mark, 0, reo);

	reo->re = re;
	reo->obj = eo;
	/* the callbacks array starts as NULL for efficiency. */

	if (arghash != Qnil) {
		if (VARARG("x") != Qnil && VARARG("y") != Qnil)
			eo_move(obj, VARARG("x"), VARARG("y"));
		if (VARARG("width") != Qnil && VARARG("height") != Qnil)
			eo_resize(obj, VARARG("width"), VARARG("height"));
		if (VARARG("layer") != Qnil)
			eo_set_layer(obj, VARARG("layer"));
	}

	return obj;
}

/**************/
/* evas image */
/**************/
static VALUE
ei_set_fill(VALUE self, VALUE x, VALUE y, VALUE w, VALUE h) {
	Get_EO();
	evas_set_image_fill(re->evas, obj, 
			NUM2INT(x), NUM2INT(y), NUM2INT(w), NUM2INT(h));
	return Qnil;
}
static VALUE
rb_evas_new_image(int argc, VALUE *argv, VALUE self) {
	Ruby_Evas *re;
	VALUE obj;
	VALUE arghash;
	VALUE path;

	rb_scan_args(argc, argv, "01", &arghash);

	VARARG_CHECK(path, "path");

	Data_Get_Struct(self, Ruby_Evas, re);

	obj = rb_eo_new(cEOImage, re, arghash,
			evas_add_image_from_file(re->evas, STR2CSTR(path)));

	if (VARARG("fill_x") != Qnil && VARARG("fill_y") != Qnil
			&& VARARG("fill_width") != Qnil && VARARG("fill_height") != Qnil)
		ei_set_fill(obj, VARARG("fill_x"), VARARG("fill_y"),
				VARARG("fill_width"), VARARG("fill_height"));

	//rb_obj_call_init(obj, 1, &rpath);
	return obj;
}

static VALUE
rb_evas_new_rectangle(int argc, VALUE *argv, VALUE self) {
	Ruby_Evas *re;
	VALUE obj;
	VALUE arghash;

	rb_scan_args(argc, argv, "01", &arghash);

	Data_Get_Struct(self, Ruby_Evas, re);

	obj = rb_eo_new(cEORectangle, re, arghash,
			evas_add_rectangle(re->evas));
	//rb_obj_call_init(obj, 1, &self);
	return obj;
}

static VALUE
et_set_text(VALUE self, VALUE text) {
	Get_EO();
	evas_set_text(re->evas, obj, STR2CSTR(text));
	return Qnil;
}

static VALUE
rb_evas_new_text(int argc, VALUE *argv, VALUE self) {
	Ruby_Evas *re;
	VALUE arghash, rfont, rsize, rtext, obj;

	rb_scan_args(argc, argv, "01", &arghash);

	Data_Get_Struct(self, Ruby_Evas, re);

	VARARG_CHECK(rfont, "fontname");
	VARARG_CHECK(rsize, "fontsize");
	VARARG_CHECK(rtext, "text");

	obj = rb_eo_new(cEOText, re, arghash,
			evas_add_text(re->evas, 
				STR2CSTR(rfont), NUM2INT(rsize), STR2CSTR(rtext)));
	//rb_obj_call_init(obj, 1, &self);
	return obj;
}

void
rb_eo_init() {
	rb_define_method(cEvas, "new_rectangle", rb_evas_new_rectangle, -1);
	rb_define_method(cEvas, "new_image",     rb_evas_new_image, -1);
	rb_define_method(cEvas, "new_text",      rb_evas_new_text, -1);

	cEvasObject = rb_define_class("EvasObject", rb_cObject);
#define DEFCALLBACK(x) rb_define_const(cEvasObject, "EVENT_" #x, INT2NUM(CALLBACK_ ## x))
	DEFCALLBACK(MOUSE_IN);
	DEFCALLBACK(MOUSE_OUT);
	DEFCALLBACK(MOUSE_DOWN);
	DEFCALLBACK(MOUSE_UP);
	DEFCALLBACK(MOUSE_MOVE);

	/* no new method-- evas objects are created by the evas class. */
#define DEFMETHOD(m, x) rb_define_method(cEvasObject, #m, eo_##m, x)
	DEFMETHOD(move,         2);
	DEFMETHOD(resize,       2);
	DEFMETHOD(set_layer,    1);
	DEFMETHOD(set_clip,     1);
	rb_define_method(cEvasObject, "layer=", eo_set_layer,  1);
	rb_define_method(cEvasObject, "x",      eo_get_x,      0);
	rb_define_method(cEvasObject, "y",      eo_get_y,      0);
	rb_define_method(cEvasObject, "width",  eo_get_width,  0);
	rb_define_method(cEvasObject, "height", eo_get_height, 0);
	DEFMETHOD(set_color,    4);
	DEFMETHOD(set_alpha,    1);
	rb_define_method(cEvasObject, "alpha=", eo_set_alpha, 1);
	DEFMETHOD(show,         0);
	DEFMETHOD(hide,         0);
	DEFMETHOD(del,          0);

	rb_define_method(cEvasObject, "onevent", eo_onevent, -1);

	cEORectangle = rb_define_class("EORectangle", cEvasObject);

	cEOImage = rb_define_class("EOImage", cEvasObject);
	rb_define_method(cEOImage, "set_fill", ei_set_fill, 4);

	cEOText = rb_define_class("EOText", cEvasObject);
	rb_define_method(cEOText, "set_text", et_set_text, 1);
	rb_define_method(cEOText, "text=",    et_set_text, 1);
}
