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

#include "shared.h"

VALUE cEvas;

static VALUE
rb_evas_initialize(VALUE self, VALUE arghash) {
	Ruby_Evas *re;
	Display *dpy;
	Window win;

	Data_Get_Struct(self, Ruby_Evas, re);

	rb_iv_set(self, "@display", VARARG("display"));
	rb_iv_set(self, "@width",   VARARG("width"));
	rb_iv_set(self, "@height",  VARARG("height"));

	dpy = evas_get_display(re->evas);
	win = evas_get_window(re->evas);
	XSelectInput(dpy, win, 
			ButtonPressMask | ButtonReleaseMask |
			ExposureMask | PointerMotionMask |
			EnterWindowMask | LeaveWindowMask);

	xlib_set_size_hints(dpy, win,
			NUM2INT(rb_iv_get(self, "@width")),
			NUM2INT(rb_iv_get(self, "@height")));

	XMapWindow(dpy, win);

	return self;
}

static VALUE
rb_evas_set_window_title(VALUE self, VALUE title) {
	Ruby_Evas *re;
	Display *dpy;

	Data_Get_Struct(self, Ruby_Evas, re);

	dpy = evas_get_display(re->evas);
	XStoreName(dpy, evas_get_window(re->evas), STR2CSTR(title));
	return Qnil;
}

static VALUE
rb_evas_font_add_path(VALUE self, VALUE path) {
	Ruby_Evas *re;
	Data_Get_Struct(self, Ruby_Evas, re);
	evas_font_add_path(re->evas, STR2CSTR(path));
	return Qnil;
}

static VALUE
rb_evas_new(VALUE class, VALUE arghash) {
	VALUE obj;
	Evas evas;
	Ruby_Evas *re;
	Display *dpy;
	int width, height;

	VALUE rdpy, rwidth, rheight;

	if (rb_obj_is_kind_of(arghash, rb_cHash) != Qtrue) {
		rb_raise(rb_eTypeError, "evas must be created with hash of arguments");
		return Qnil;
	} 
	VARARG_CHECK(rdpy,    "display");
	VARARG_CHECK(rwidth,  "width");
	VARARG_CHECK(rheight, "height");

	dpy = xlib_dpy_from_rb(rdpy);
	width = NUM2INT(rwidth);
	height = NUM2INT(rheight);

	/* create the evas.  copied mostly from evas_misc.c from the evas
	 * source. */
	evas = evas_new();
	evas_set_output_method(evas, RENDER_METHOD_ALPHA_SOFTWARE);
	evas_set_output_colors(evas, 256);

	{
		XSetWindowAttributes att;
		Visual *visual;
		Colormap colormap;
		Window window;
		int x = 0, y = 0;

		/* this call apparently fails on my XF86/Windows display.
		 * but it fails in the provided evas demos, too, so
		 * i guess it's acceptable. */
		visual = evas_get_optimal_visual(evas, dpy);
		colormap = evas_get_optimal_colormap(evas, dpy);
		att.background_pixmap = None;
		att.colormap = colormap;
		att.border_pixel = 0;
		att.event_mask = 0;
		window = XCreateWindow(dpy,
						RootWindow(dpy, DefaultScreen(dpy)),
						x, y, width, height, 0,
						imlib_get_visual_depth(dpy, visual),
						InputOutput,
						visual,
						CWColormap | CWBorderPixel | CWEventMask | CWBackPixmap,
						&att);
		/* this was in the evas source.  is it necessary? */
		/* evas->current.created_window = window;         */
		evas_set_output(evas, dpy, window, visual, colormap);
	}

	evas_set_output_size(evas, width, height);
	evas_set_output_viewport(evas, 0, 0, width, height);
	evas_set_font_cache(evas, 4 * 1024 * 1024);  /* 4mb */
	evas_set_image_cache(evas, 8 * 1024 * 1024); /* 8mb */
	evas_event_leave(evas);

	obj = Data_Make_Struct(class, Ruby_Evas, 0, 0, re);
	re->evas = evas;

	rb_obj_call_init(obj, 1, &arghash);

	if (VARARG("fontpath") != Qnil)
		rb_evas_font_add_path(obj, VARARG("fontpath"));
	if (VARARG("title") != Qnil)
		rb_evas_set_window_title(obj, VARARG("title"));

	return obj;
}

static VALUE
rb_evas_render(VALUE self) {
	Ruby_Evas *re;

	Data_Get_Struct(self, Ruby_Evas, re);
	evas_render(re->evas);
	return Qnil;
}

static VALUE
rb_evas_handle_x_events(VALUE self) {
	Ruby_Evas *re;
	Display *dpy;

	Data_Get_Struct(self, Ruby_Evas, re);
	dpy = evas_get_display(re->evas);

	do {
		XEvent ev;

		XNextEvent(dpy, &ev);

		switch (ev.type) {
			case Expose:
				evas_update_rect(re->evas, 
						ev.xexpose.x, ev.xexpose.y, 
						ev.xexpose.width, ev.xexpose.height);
				break;
			case MotionNotify:
				evas_event_move(re->evas, 
						ev.xmotion.x, ev.xmotion.y);
				break;
			case EnterNotify:
				evas_event_enter(re->evas);
				break;
			case LeaveNotify:
				evas_event_leave(re->evas);
				break;
			case ButtonPress:
				evas_event_button_down(re->evas, 
						ev.xbutton.x, ev.xbutton.y, 
						ev.xbutton.button);
				break;
			case ButtonRelease:
				evas_event_button_up(re->evas, 
						ev.xbutton.x, ev.xbutton.y, 
						ev.xbutton.button);
				break;
		}
	} while (XPending(dpy)); 

	return Qnil;
}

void
Init_evas() {
	xlib_init();

	cEvas = rb_define_class("Evas", rb_cObject);
	rb_define_singleton_method(cEvas, "new", rb_evas_new, 1);
	rb_define_method(cEvas, "initialize", rb_evas_initialize, 1);
	rb_define_method(cEvas, "set_window_title", rb_evas_set_window_title, 1);
	rb_define_method(cEvas, "window_title=", rb_evas_set_window_title, 1);
	rb_define_method(cEvas, "font_add_path", rb_evas_font_add_path, 1);
	rb_define_method(cEvas, "handle_x_events", rb_evas_handle_x_events, 0);
	rb_define_method(cEvas, "render", rb_evas_render, 0);

	rb_eo_init();
}
