using GLib;
using Gtk;

class CustomScrollableWidgetTest {
	public static int main (string[] args)
	{
		Gtk.init (ref args);
		var win = new Window ();
		win.title = "Custom Scrollable Widget Test";
		win.destroy.connect (Gtk.main_quit);

		var scroll = new ScrolledWindow (null, null);

		var cw = new CustomScrollableWidget ("This one label that is repeated");
		scroll.add (cw);

		win.add (scroll);
		win.default_width = 200;
		win.default_height = 200;
		win.show_all ();
		Gtk.main ();
		return 0;
	}
}

class CustomScrollableWidget : Widget, Scrollable {
	private int num_rows = 20;
	private string label;
	private Pango.Layout layout;

	public CustomScrollableWidget (string custom_label)
	{
		base ();
		label = custom_label;
		layout = null;
		this.set_has_window (false);
	}

	private Pango.Layout Layout {
		get {
			if (layout == null)
				layout = create_pango_layout (label);
			return layout;
		}
	}

	private Gdk.Rectangle ContentArea {
		get {
			Gdk.Rectangle area = Gdk.Rectangle();
			Allocation alloc;
			get_allocation(out alloc);
			area.x = alloc.x;
			area.y = alloc.y;
			
			int layoutWidth, layoutHeight;
			Layout.get_pixel_size (out layoutWidth, out layoutHeight);
			area.width = layoutWidth;
			area.height = layoutHeight * num_rows;
			
			return area;
		}
	}

	protected override bool draw (Cairo.Context cr)
	{
		int layout_x = - HadjustmentValue;
		int layout_y = - VadjustmentValue;
		int layoutWidth, layoutHeight;
		Layout.get_pixel_size (out layoutWidth, out layoutHeight);

		for (int i = 0; i < num_rows; i++) {
			Layout.set_text ("%s %d".printf(label, i), 200);
			this.get_style_context().render_layout (cr, layout_x, layout_y, Layout);
			layout_y += layoutHeight;
		}
		
		return false;
	}

	public override void size_allocate (Allocation allocation)
	{
		base.size_allocate (allocation);

		if (hadjustment != null) {
			hadjustment.set_page_size (allocation.width);
			hadjustment.set_page_increment (allocation.width);
			UpdateAdjustments ();
		}
		
		if (vadjustment != null) {
			vadjustment.page_size = allocation.height;
			vadjustment.page_increment = allocation.height;
			UpdateAdjustments ();
		}
		parent.queue_draw ();
	}

	
	private Adjustment hadjust;
	public Adjustment hadjustment {
		get { return hadjust; }
		set {
			if (value == hadjust) {
				return;
			}
			hadjust = value;
			if (hadjust == null) {
				return;
			}
			hadjust.value_changed.connect (OnHadjustmentChanged);
			UpdateAdjustments ();
		}
	}
	
	private Adjustment vadjust;
	public Adjustment vadjustment {
		get { return vadjust; }
		set {
			if (value == vadjust) {
				return;
			}
			vadjust = value;
			if (vadjust == null) {
				return;
			}
			vadjust.value_changed.connect (OnVadjustmentChanged);
			UpdateAdjustments ();
		}
	}
	
	private int HadjustmentValue {
		get { return hadjustment == null ? 0 : (int)hadjustment.value; }
	}

	private int VadjustmentValue {
		get { return vadjustment == null ? 0 : (int)vadjustment.value; }
	}

	public ScrollablePolicy hscroll_policy {
		get; set;
	}
	
	public ScrollablePolicy vscroll_policy {
		get; set;
	}
	
	private void UpdateAdjustments ()
	{
		int layoutWidth, layoutHeight;
		Layout.get_pixel_size (out layoutWidth, out layoutHeight);
		
		if (hadjustment != null) {
			hadjustment.upper = ContentArea.width;
			hadjustment.step_increment = 10.0;
			if (hadjustment.value + hadjustment.page_size > hadjustment.upper) {
				hadjustment.value = hadjustment.upper - hadjustment.page_size;
			}
			hadjustment.changed ();
		}
		
		if (vadjustment != null) {
			vadjustment.upper = ContentArea.height;
			vadjustment.step_increment = layoutHeight;
			if (vadjustment.value + vadjustment.page_size > vadjustment.upper) {
				vadjustment.value = vadjustment.upper - vadjustment.page_size;
			}
			vadjustment.changed ();
		}
	}
	
	private void OnHadjustmentChanged (Adjustment o)
	{
		UpdateAdjustments ();
		queue_draw ();
	}
	
	private void OnVadjustmentChanged (Adjustment o)
	{
		UpdateAdjustments ();
		queue_draw ();
	}
}
