#!/usr/bin/env python

import pygtk
pygtk.require('2.0')

import gobject
import pango
import gtk
import math
import time
from gtk import gdk
import cairo

if gtk.pygtk_version < (2,3,93):
    print "PyGtk 2.3.93 or later required"
    raise SystemExit

from math import pi

TEXT = 'peano'
BORDER_WIDTH = 10
peano_order = 1

class PyGtkWidget(gtk.Widget):
    __gsignals__ = { 'realize': 'override',
                     'expose-event' : 'override',
                     'size-allocate': 'override',
                     'size-request': 'override',}

    def __init__(self):
        gtk.Widget.__init__(self)
        self.draw_gc = None
        self.layout = self.create_pango_layout(TEXT)
        self.layout.set_font_description(pango.FontDescription("sans serif 8"))

    def do_realize(self):
        self.set_flags(self.flags() | gtk.REALIZED)
        self.window = gdk.Window(self.get_parent_window(),
                                 width=self.allocation.width,
                                 height=self.allocation.height,
                                 window_type=gdk.WINDOW_CHILD,
                                 wclass=gdk.INPUT_OUTPUT,
                                 event_mask=self.get_events() | gdk.EXPOSURE_MASK)
	self.window.set_user_data(self)
        self.style.attach(self.window)
        self.style.set_background(self.window, gtk.STATE_NORMAL)
        self.window.move_resize(*self.allocation)

    def do_size_request(self, requisition):
	width, height = self.layout.get_size()
	requisition.width = 400 
	requisition.height = 400 

    def do_size_allocate(self, allocation):
        self.allocation = allocation
        if self.flags() & gtk.REALIZED:
            self.window.move_resize(*allocation)

    def _expose_cairo(self, event, cr):
			cairo_draw(self,event,cr)

    def do_expose_event(self, event):
        self.chain(event)
        cr = self.window.cairo_create()
        return self._expose_cairo(event, cr)

def cairo_draw(self, event, cr):

	x, y, w, h = self.allocation

	border = 0.05 # 5% border
	cr.save() #for the scale
	cr.scale(w, h)
	global peano_order

	from math import log,pow
	unitsize = (1.0 - (border * 2)) / pow(3,peano_order)
	iterations = int(pow(3,2 * (peano_order - 1))) + 1

	#align unit size
	#this is to prevent too much anti-aliasing
	nx, ny = cr.user_to_device(unitsize, 1.0)
	unitsize, ny = cr.device_to_user(int(nx),ny)
	border = border + 0.5 * unitsize

	#align the initial drawing point
	#this is to prevent too much anti-aliasing
	x1 = border
	y1 = 1.0 - border
	nx, ny = cr.user_to_device (x1, y1);
	nx1 = int(nx)
	ny1 = int(ny)
        nx, ny = cr.device_to_user (nx1,ny1)

	cr.move_to(nx,ny)
	cr.set_line_width(0.002)
	cr.set_source_rgba(0.0, 0.0, 0.0, 1.0)

	def turn(cr):
		cr.rotate( pi / 2)

	def extend(cr):
		cr.rel_line_to(0, -unitsize)

	def flip(cr):
		cr.transform(cairo.Matrix(-1,0,0,1,0,0))

	def draw_piece(cr):
		cr.rel_line_to(0, - unitsize * 2)
		cr.rel_line_to(unitsize, 0)
		cr.rel_line_to(0, 2.0 * unitsize)
		cr.rel_line_to(unitsize, 0)
		cr.rel_line_to(0, unitsize * -2.0)

	def doturn(b):
		if (not (b % 243 == 0)) and (b % 81 == 0):
			return
		if (not (b % 27 == 0)) and (b % 9 == 0):
			return
		turn(cr)

	for b in range(1,iterations):
		draw_piece(cr)
		if b % 3 == 0:
			doturn(b)
		if not (b == iterations - 1):
			extend(cr)
		if b % 3 == 0:
			doturn(b)
		flip(cr) 

	cr.stroke()

	cr.restore() #scale
	fontw, fonth = self.layout.get_pixel_size()
	cr.move_to((w - fontw - 4), (h - fonth ))
	cr.update_layout(self.layout)
	cr.show_layout(self.layout)
	

win = gtk.Window()
win.set_title('peano')
win.connect('delete-event', gtk.main_quit)

hbox = gtk.HBox(homogeneous=False)

event_box = gtk.EventBox()
event_box.connect("button_press_event", lambda w,e: win.set_decorated(not win.get_decorated()))

w = PyGtkWidget()
event_box.add(w)

def update_peano_order(v):
	global peano_order
	peano_order = int(v)
	win.window.invalidate_rect( win.allocation, True)

hbox.pack_start(event_box, expand=True, fill=True)

slider = gtk.VScrollbar(gtk.Adjustment(value=1,upper=4,lower=1))
slider.get_adjustment().step_increment = 1
slider.get_adjustment().page_increment = 1
slider.connect("value_changed", lambda w: update_peano_order(w.get_adjustment().get_value()))

hbox.pack_start(slider, expand=False)

win.add(hbox)
win.show_all()

gtk.main()
    

