#!/usr/bin/python
# -*- coding: koi8-r -*-
# This file is part of the metromap
#
# Copyright(c) 2010, Sergey Pinaev
# http://metromap.antex.ru/
#
# This file may be licensed under the terms of of the
# GNU General Public License Version 2 (the ``GPL'').
#
# Software distributed under the License is distributed
# on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
# express or implied. See the GPL for the specific language
# governing rights and limitations.
#
# You should have received a copy of the GPL along with this
# program. If not, go to http://www.gnu.org/licenses/gpl.html
# or write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import gtk
import gobject
import pango

import os, time

from math import sqrt, atan, degrees, radians, log, ceil, cos, sin, pi

def pmetro_ellipse_to_lines(points, angle):
    res = []
    if len(points) != 2:
        return res

    xc = float(0.0)
    yc = float(0.0)

    x1 = float(points[0][0])
    y2 = float(points[0][1])
    x2 = float(points[1][0])
    y1 = float(points[1][1])

    rotAngle = angle * pi / 180.0

    rx = (x2 - x1) / 2
    ry = (y2 - y1) / 2
    xc  = x1 + rx
    yc = y1 + ry
    sAngle = sin(-rotAngle)
    cAngle = cos(-rotAngle)
    xRot = xc + (x1 - xc) * cAngle - (y1 - yc) * sAngle
    yRot = yc + (x1 - xc) * sAngle + (y1 - yc) * cAngle

    x1 = xRot
    y1 = yRot

    xRot = xc + (x2 - xc) * cAngle - (y2 - yc) * sAngle
    yRot = yc + (x2 - xc) * sAngle + (y2 - yc) * cAngle

    x2 = xRot
    y2 = yRot

    if x1 > x2:
        xtemp = x2
        x2 = x1
        x1 = xtemp

    if y1 > y2:
        ytemp = y2
        y2 = y1
        y1 = ytemp

    coeff = float((x2 - x1) / (y2 - y1))

    if ((coeff < 1.1) and (coeff > 1.0)):
        yc = y1 + (y2 - y1) / 2
        y1 = int(yc - (x2 - x1) / 2)
        y2 = int(yc + (x2 - x1) / 2)
    else:
        if ((coeff > 0.9) and (coeff < 1.0)):
            xc = x1 + (x2 - x1) / 2
            x1 = int(xc - (y2 - y1) / 2)
            x2 = int(xc + (y2 - y1) / 2)

    rx = 0.0
    ry = 0.0
    sAngle = 0.0
    cAngle = 0.0
    rotAngle = 0.0
    k = 0.0
    x = 0.0
    y = 0.0
    xRot = 0.0
    yRot = 0.0

    step = int((x2 - x1 + y2 - y1) * 3.0)
    rx = (x2 - x1) / 2
    ry = (y2 - y1) / 2
    xc  = x1 + rx
    yc = y1 + ry
    k = (360 * pi / 180) / step
    sAngle = sin(rotAngle)
    cAngle = cos(rotAngle)
    for i in range(0, step):
        theta = i * k
        x = xc + rx * cos(theta)
        y = yc + ry * sin(theta)
        xRot = xc + (x - xc) * cAngle - (y - yc) * sAngle
        yRot = yc + (x - xc) * sAngle + (y - yc) * cAngle
        res.append([int(xRot), int(yRot)])

    return res

def pmetro_spline_to_lines(points):
        res = []
        if len(points) < 3:
                res.append([points[0][0], points[0][1]])
                if len(points) < 2:
                        res.append([points[0][0], points[0][1]])
                else:
                        res.append([points[1][0], points[1][1]])
        else:
                max_delta = 0
                for i in xrange(1, len(points)):
                        max_delta = max(abs(points[i][0] - points[i - 1][0]), max_delta)
                        max_delta = max(abs(points[i][1] - points[i - 1][1]), max_delta)
                steps = 2 ** int(round(log(max_delta / 4.0)) + 1)
                for i in xrange((len(points) - 1) * steps + 1):
                        res.append([0, 0])
                for i in xrange(len(points)):
                        res[i * steps][0] = points[i][0]
                        res[i * steps][1] = points[i][1]
                clen = (len(points) - 1) * steps
                step = steps
                while step > 1:
                        res[step / 2][0] = (res[0][0] * 3 + res[step][0] * 6 - res[step * 2][0]) / 8.0
                        res[step / 2][1] = (res[0][1] * 3 + res[step][1] * 6 - res[step * 2][1]) / 8.0
                        res[clen - step / 2][0] = (res[clen][0] * 3 + res[clen - step][0] * 6 - res[clen - step * 2][0]) / 8.0
                        res[clen - step / 2][1] = (res[clen][1] * 3 + res[clen - step][1] * 6 - res[clen - step * 2][1]) / 8.0
                        for i in xrange(1, (len(points) - 1) * steps / step - 1):
                                res[i * step + step / 2][0] = (-res[i * step - step][0] + res[i * step][0] * 9 + res[i * step + step][0] * 9 - res[ i * step + 2 * step][0]) / 16.0
                                res[i * step + step / 2][1] = (-res[i * step - step][1] + res[i * step][1] * 9 + res[i * step + step][1] * 9 - res[ i * step + 2 * step][1]) / 16.0
                        step /= 2
        return res

def draw_lines_cairo(cairo, width, col, bg_col, dashed, lines):
        cairo.set_line_width(width)
        if dashed == 0:
                cairo.set_dash([], 0)
        else:
                cairo.set_dash([5], 1)
        cairo.move_to(lines[0][0], lines[0][1])
        for line in lines[1:]:
                cairo.line_to(line[0], line[1])
        if bg_col:
                cairo.close_path()
                cairo.set_source_rgb(bg_col[0], bg_col[1], bg_col[2])
                cairo.fill_preserve()
        cairo.set_source_rgb(col[0], col[1], col[2])
        cairo.stroke()

def draw_vectors_cairo(da, cairo, vectors):
        bg = (1, 1, 1)
        fg = (0, 0, 0)
        for i in vectors:
                if i[0] == 'set_rgb_fg_color':
                        fg = i[1]
                elif i[0] == 'set_rgb_bg_color':
                        bg = i[1]
                elif i[0] == 'spline':
                        w = 2
                        if i[1] != 0:
                                w = i[1]
                        points = pmetro_spline_to_lines(i[2])
                        draw_lines_cairo(cairo, w, fg, False, 0, points)
                elif i[0] == 'ellipse':
                        w = 2
                        if i[1] != 0:
                                w = i[1]
                        points = pmetro_ellipse_to_lines(i[2], i[3])
                        if points != []:
                                draw_lines_cairo(cairo, w, fg, False, 0, points)
                elif i[0] == 'text':
                        pango_context = da.create_pango_context()
                        pango_layout = pango.Layout(pango_context)
                        pango_layout.set_font_description(pango.FontDescription(i[1]['font'] + " " + str(i[1]['size'] / 1.5)))
                        pango_layout.set_text(i[1]['text'])
                        cairo.move_to(i[1]['x'], i[1]['y'])
                        if i[1]['angle'] != 0:
                                cairo.rotate(radians(-int(i[1]['angle'])))
                        cairo.set_source_rgb(fg[0], fg[1], fg[2])
                        cairo.update_layout(pango_layout)
                        cairo.show_layout(pango_layout)
                        if i[1]['angle'] != 0:
                                cairo.rotate(radians(int(i[1]['angle'])))
                elif i[0] == 'lines':
                        w = 2
                        if i[2][0] != 0:
                                w = i[2][0]
                        draw_lines_cairo(cairo, w, fg, False, i[1], i[2][1])
                elif i[0] == 'poly':
                        w = 2
                        if i[1][0] != 0:
                                w = i[1][0]
                        draw_lines_cairo(cairo, w, fg, bg, 0, i[1][1])
                else:
                        print _('unimplemented:'), i[0]

class MapDisplay:
        def __init__(self, lines, stations, vectors, selcb, spbhack, cachedir = None, debug = False, pb = None, one_button = False):
                self.DEBUG = debug
                self.Progress = pb
                self.gc = None
                self.scb = selcb
                self.menu_cb = None
                self.menu_items = None
                self.toggle_trans_cb = None
                self.Lines = lines
                self.Stations = stations
                self.Vectors = vectors
                self.spbhack = spbhack

                self.pixbuf = None
                self.pixbuf_created = False
                self.cachedir = cachedir
                self.last_endpoint = 1
                self.one_button = one_button

                #count picture area
                #minx = min(map(lambda st: min(st['x'] - st['diameter'] / 2 - 5, st['namerect'][0] - 5), self.Stations.values()))
                #miny = min(map(lambda st: min(st['y'] - st['diameter'] / 2 - 5, st['namerect'][1] - 5), self.Stations.values()))
                maxx = max(map(lambda st: max(st['x'] + st['diameter'] / 2 + 5, st['namerect'][0] + st['namerect'][2] + 20), self.Stations.values()))
                maxy = max(map(lambda st: max(st['y'] + st['diameter'] / 2 + 5, st['namerect'][1] + st['namerect'][3] + 20), self.Stations.values()))

                self.da = gtk.DrawingArea()
                self.da.set_size_request(maxx + 1, maxy + 1)
                self.da.set_events(gtk.gdk.EXPOSURE_MASK |
                                   gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK |
                                   gtk.gdk.KEY_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK)
                self.da.connect("expose_event", self.expose)
                self.da.connect("button_press_event", self.bp)
                self.da.connect("button_release_event", self.bp)
                self.da.connect("motion_notify_event", self.bp)
                self.da.connect('configure_event', self.__configure_cb)

                self.button_pressed = None
                self.button_pressed_coords = [0, 0]
                self.button_press_treshold = 5

                self.daw = self.da.get_allocation().width
                self.dah = self.da.get_allocation().height
                self.pw = maxx + 1
                self.ph = maxy + 1
                self.dx = 0
                self.dy = 0

                self.stfrom = None
                self.stto = None
                self.pathlist = None
                self.drawpath = False
                if self.Vectors.has_key('lineswidth'):
                        self.pathwidth = self.Vectors['lineswidth'] # - 5
                else:
                        self.pathwidth = self.Stations[0]['diameter'] / 2 #4

                if self.pathwidth == 0:
                        self.pathwidth = 1

                self.timeout = gobject.timeout_add(400, self.redraw, False)
                self.step = 0

                self.STATE_OFF  = 0
                self.STATE_FROM = 1
                self.STATE_TO   = 2

                self.cpb_timeout = gobject.timeout_add(100, self.create_pixbuf, False)

        def __draw_dash_rect(self, step, c1, c2, x, y, w, h):
                cs = c1
                ce = c2
                if step == 0:
                        cs = c2
                        ce = c1
                cairo = self.da.window.cairo_create()
                cairo.set_line_width(2)
                cairo.rectangle(x, y, w, h)
                cairo.set_source_rgb(cs[0], cs[1], cs[2])
                cairo.set_dash([], 0)
                cairo.stroke_preserve()
                cairo.set_source_rgb(ce[0], ce[1], ce[2])
                cairo.set_dash([5], 1)
                cairo.stroke()

        def __draw_dash_arc(self, step, c1, c2, x, y, d):
                cs = c1
                ce = c2
                if step == 0:
                        cs = c2
                        ce = c1
                cairo = self.da.window.cairo_create()
                cairo.set_line_width(2)
                cairo.arc(x, y, d / 2, 0, 6.28)
                cairo.set_source_rgb(cs[0], cs[1], cs[2])
                cairo.set_dash([], 0)
                cairo.stroke_preserve()
                cairo.set_source_rgb(ce[0], ce[1], ce[2])
                cairo.set_dash([5], 1)
                cairo.stroke()

        def __draw_station_name(self, text, coldesc, x, y, w, h):
                r = int(coldesc[0:2], 16) / 256.0
                g = int(coldesc[2:4], 16) / 256.0
                b = int(coldesc[4:6], 16) / 256.0
                self.pango_layout.set_text(text)
                if w < h:
                        self.pango_layout.set_width(h * pango.SCALE)
                        self.cairo.move_to(x, y + h)
                        self.cairo.rotate(radians(-90))
                else:
                        self.pango_layout.set_width(w * pango.SCALE)
                        self.cairo.move_to(x, y)
                self.cairo.update_layout(self.pango_layout)
                self.cairo.layout_path(self.pango_layout)
                self.cairo.set_line_width(2)
                self.cairo.set_source_rgb(1, 1, 1)
                #self.cairo.set_source_rgb(0, 0, 0)
                self.cairo.stroke_preserve()
                self.cairo.set_source_rgb(r, g, b)
                self.cairo.fill()
                if w < h:
                        self.cairo.rotate(radians(90))

        def draw_station(self, num, state):
                if num == None:
                        return

                sx, sy, sd = self.Stations[num]['x'], self.Stations[num]['y'], self.Stations[num]['diameter']
                ov = self.is_st_overlap([num] + self.Stations[num]['transfers'])
                if ov[0]:
                        sx, sy, sd = ov[1], ov[2], ov[3]
                snx, sny, snw, snh = self.Stations[num]['namerectact']
                snx -= 2
                sny -= 2
                snw += 4
                snh += 4

                if state == self.STATE_OFF:
                        self.draw_pic(sx - sd / 2 - 1, sy - sd / 2 - 1, sd + 2, sd + 2)
                        self.draw_pic(snx - 1, sny  - 1, snw + 2, snh + 2)
                else:
                        c = [0, 0, 1]
                        if state == self.STATE_FROM:
                                c = [1, 0, 0]
                        if self.Stations[num]['namerectact'][0] != 0 or self.Stations[num]['namerectact'][1] != 0:
                                self.__draw_dash_rect(self.step, c, [1, 1, 1], snx + self.dx, sny + self.dy, snw, snh)
                        self.__draw_dash_arc(self.step, c, [1, 1, 1], self.dx + sx, self.dy + sy, sd)

        def draw_path(self, path, state):
                points = list()
                gpoints = list()
                fr = path[0]
                for p in path[1:]:
                        pts, p_type = self._make_segments(fr, p)
                        if p_type == "spline":
                                pts = pmetro_spline_to_lines(pts)
                        points.append(pts)
                        fr = p

                for seg in points:
                        if seg:
                                for next in seg:
                                        gpoints.append((next[0], next[1]))

                if state:
                        points = map(lambda a: (a[0] + self.dx, a[1] + self.dy), gpoints)
                        cairo = self.da.window.cairo_create()
                        draw_lines_cairo(cairo, self.pathwidth - 6, [1, 1, 1], False, False, points)
                        draw_lines_cairo(cairo, self.pathwidth - 4, [0, 0, 0], False, True, points)
                else:
                        minx = max(min(map(lambda a: a[0], gpoints)) - self.pathwidth - 2, 0)
                        miny = max(min(map(lambda a: a[1], gpoints)) - self.pathwidth - 2, 0)
                        maxx = min(max(map(lambda a: a[0], gpoints)) + self.pathwidth + 2, self.pw - 1)
                        maxy = min(max(map(lambda a: a[1], gpoints)) + self.pathwidth + 2, self.ph - 1)
                        self.draw_pic(int(minx), int(miny), int(ceil(maxx - minx)), int(ceil(maxy - miny)))

        def _make_segments(self, fr, to):
                xf, yf = self.Stations[fr]['x'], self.Stations[fr]['y']
                xt, yt = self.Stations[to]['x'], self.Stations[to]['y']
                if self.Stations[fr]['add'].has_key(to):
                        if len(self.Stations[fr]['add'][to]['coords']) > 1:
                                len1 = abs(xf - self.Stations[fr]['add'][to]['coords'][0][0]) + \
                                       abs(yf - self.Stations[fr]['add'][to]['coords'][0][1])
                                len2 = abs(xf - self.Stations[fr]['add'][to]['coords'][1][0]) + \
                                       abs(yf - self.Stations[fr]['add'][to]['coords'][1][1])
                                if len1 > len2:
                                        self.Stations[fr]['add'][to]['coords'].reverse()
                        point = [(xf, yf)] + self.Stations[fr]['add'][to]['coords'] + [(xt, yt)]
                        p_type = self.Stations[fr]['add'][to]['type']
                else:
                        point = [(xf, yf), (xt, yt)]
                        p_type = "line"

                return [point, p_type]

        def draw_st_arc(self, snum, xc, yc, d, num, pos):
                s = self.Stations[snum]
                coldesc = self.Lines[s['line']]['color']
                a2 = 360 / num
                a1 = 90 + a2 * pos
                if a1 >= 360:
                        a1 -= 360
                self.cairo.set_source_rgb(int(coldesc[0:2], 16) / 256.0,
                                          int(coldesc[2:4], 16) / 256.0,
                                          int(coldesc[4:6], 16) / 256.0)
                self.cairo.move_to(xc, yc)
                self.cairo.arc(xc, yc, d / 2, radians(a1), radians(a2 + a1))
                self.cairo.fill()
                return a1, a2

        def draw_st_circle(self, snum):
                s = self.Stations[snum]
                coldesc = self.Lines[s['line']]['color']
                self.cairo.arc(s['x'], s['y'], s['diameter'] / 2, 0, 6.28)
                self.cairo.set_source_rgb(int(coldesc[0:2], 16) / 256.0,
                                          int(coldesc[2:4], 16) / 256.0,
                                          int(coldesc[4:6], 16) / 256.0)
                self.cairo.fill()
                if s['uc']: # station is under construction
                        nd = s['diameter'] - 8
                        if nd <= 0:
                                nd = s['diameter'] / 2
                        self.cairo.arc(s['x'], s['y'], nd / 2, 0, 6.28)
                        self.cairo.set_source_rgb(1, 1, 1)
                        self.cairo.fill()

        def is_st_overlap(self, slist):
                extrad = -2
                if self.spbhack:
                        extrad = 4
                overlap = False
                s = self.Stations[slist[0]]
                d = s['diameter']
                minx = s['x'] - d / 2
                miny = s['y'] - d / 2
                maxx = minx + d
                maxy = miny + d
                for n in xrange(len(slist)):
                        s = self.Stations[slist[n]]
                        d = s['diameter']
                        minx = min(minx, s['x'] - d / 2)
                        miny = min(miny, s['y'] - d / 2)
                        maxx = max(maxx, s['x'] - d / 2 + d)
                        maxy = max(maxy, s['y'] - d / 2 + d)
                        if not overlap:
                                r = gtk.gdk.Rectangle(s['x'] - d / 2, s['y'] - d / 2, d + extrad, d + extrad)
                                for nd in slist[n + 1:]:
                                        sd = self.Stations[nd]
                                        dd = sd['diameter']
                                        rd = gtk.gdk.Rectangle(sd['x'] - dd / 2, sd['y'] - dd / 2, dd + extrad, dd + extrad)
                                        rr = r.intersect(rd)
                                        if rr.width > 0 and rr.height > 0:
                                                overlap = True
                                                break
                d = max(maxx - minx, maxy - miny)
                x = minx + (maxx - minx) / 2
                y = miny + (maxy - miny) / 2
                return overlap, x, y, d

        def create_pixbuf(self, additional = None):
                if self.pixbuf_created or not self.da.window:
                        return True
                self.da.hide()
                self.pixbuf_created = True

                if self.cachedir != None:
                        import hashlib, pprint
                        m = hashlib.md5()
                        pp = pprint.PrettyPrinter()
                        m.update(pp.pformat(self.Stations))
                        m.update(pp.pformat(self.Vectors))
                        hd = m.hexdigest()
                        if os.path.isfile(self.cachedir + '/' + hd + '.png') and \
                           os.path.isfile(self.cachedir + '/' + hd + '.data'):
                                self.Progress.progress_start(_("Loading map"))
                                self.pixbuf = gtk.gdk.Pixmap(self.da.window, self.pw, self.ph, -1)
                                try:
                                        pb = gtk.gdk.pixbuf_new_from_file(self.cachedir + '/' + hd + '.png')
                                        self.Progress.progress_set_percentage(50)
                                        self.pixbuf.draw_pixbuf(self.gc, pb, 0, 0, 0, 0, self.pw, self.ph)
                                        self.Progress.progress_set_percentage(75)
                                        f = file(self.cachedir + '/' + hd + '.data', 'r')
                                        for l in f.readlines():
                                                la = map(lambda a: int(a), l.split(','))
                                                self.Stations[la[0]]['namerectact'] = [la[1], la[2], la[3], la[4]]
                                        self.Progress.progress_end()
                                        self.da.show()
                                        return False
                                except:
                                        self.Progress.progress_end()

                self.Progress.progress_start(_("Generating map"))
                percentage = 0
                if self.DEBUG:
                        create_start_time = time.time()
                self.pixbuf = gtk.gdk.Pixmap(self.da.window, self.pw, self.ph, -1)
                self.cairo = self.pixbuf.cairo_create()

                self.pango_context = self.da.create_pango_context()
                self.pango_layout = pango.Layout(self.pango_context)

                self.cairo.set_source_rgb(255, 255, 255)
                self.cairo.rectangle(0, 0, self.pw, self.ph)
                self.cairo.fill()
                self.Progress.update()

                if self.DEBUG:
                        start_time = time.time()
                draw_vectors_cairo(self.da, self.cairo, self.Vectors['instructions'])
                percentage += 5
                self.Progress.progress_set_percentage(percentage)
                if self.DEBUG:
                        print _("Draw vectors time:"), time.time() - start_time

                self.pango_layout.set_wrap(pango.WRAP_WORD)
                self.pango_layout.set_spacing(-1 * pango.SCALE)
                self.pango_layout.set_alignment(pango.ALIGN_LEFT)

                #FIXME: make font family configurable?
                self.Progress.progress_set_text(_("Calculating font size"))
                if self.DEBUG:
                        start_time = time.time()
                fn = 'Sans'
                max_font_w = 30
                for k in xrange(len(self.Stations.keys())):
                        percentage += 10.0 / len(self.Stations.keys())
                        self.Progress.progress_set_percentage(percentage)
                        s = self.Stations[self.Stations.keys()[k]]
                        if s['namerect'][0] == 0 and s['namerect'][1] == 0:
                                continue
                        w = s['namerect'][2] + 4
                        h = s['namerect'][3] + 4
                        if h > w:
                                hs = h
                                h = w
                                w = hs
                        self.pango_layout.set_width(w * pango.SCALE)
                        self.pango_layout.set_text(s['name'])
                        for fw in xrange(max_font_w, 3, -1):
                                self.pango_layout.set_font_description(pango.FontDescription(fn + ' ' + str(fw)))
                                (l1, b1, w1, h1), logicalRect = self.pango_layout.get_pixel_extents()
                                if w1 <= w and h1 <= h:
                                        break
                        max_font_w = fw
                self.pango_layout.set_font_description(pango.FontDescription(fn + ' ' + str(max_font_w)))
                if self.DEBUG:
                        print _("Font size calc time:"), time.time() - start_time

                #draw lines
                self.Progress.progress_set_text(_("Draw lines"))
                if self.DEBUG:
                        start_time = time.time()
                for l in self.Lines.values():
                        percentage += 10.0 / len(self.Lines)
                        self.Progress.progress_set_percentage(percentage)
                        col = (int(l['color'][0:2], 16) / 256.0, int(l['color'][2:4], 16) / 256.0, int(l['color'][4:6], 16) / 256.0)
                        points = dict()
                        for s in l['stations']:
                                for n in self.Stations[s]['neighbours']:
                                        if not points.has_key((s, n)) and not points.has_key((n, s)):
                                                uc = False
                                                if self.Stations[s]['uc'] or self.Stations[n]['uc']:
                                                        uc = True
                                                points[(s, n)] =  [self._make_segments(s, n), uc]
                        for seg in points.values():
                                if seg[0]:
                                        if seg[0][1].lower() == "spline":
                                                seg[0][0] = pmetro_spline_to_lines(seg[0][0])
                                        draw_lines_cairo(self.cairo, self.pathwidth, col, False, seg[1], seg[0][0])
                                        #draw_lines_cairo(self.cairo, self.pathwidth, col, False, seg[1], seg[0])
                if self.DEBUG:
                        print _("Draw lines time:"), time.time() - start_time

                #draw transfers
                self.Progress.progress_set_text(_("Draw transfers"))
                if self.DEBUG:
                        start_time = time.time()
                for par in [[[0, 0, 0], 4, 0], [[1, 1, 1], 2, 4]]:
                        drawn = dict() #avoid drawing same things many times
                        for st in self.Stations.values():
                                percentage += 10.0 / len(self.Stations)
                                self.Progress.progress_set_percentage(percentage)
                                if len(st['transfers']) and not drawn.has_key(st['number']):
                                        self.cairo.set_line_width(st['diameter'] - par[2] - 3)
                                        trlist = [st['number']] + st['transfers']
                                        ov = self.is_st_overlap(trlist)
                                        if ov[0]:
                                                d = ov[3] + par[1] * 2
                                                x = ov[1]
                                                y = ov[2]
                                                self.cairo.set_source_rgb(par[0][0], par[0][1], par[0][2])
                                                self.cairo.arc(x, y, d / 2, 0, 6.28)
                                                self.cairo.fill()
                                                for n in trlist:
                                                        drawn[n] = 1
                                        else:
                                                for n in xrange(len(trlist)):
                                                        ts = trlist[n]
                                                        s = self.Stations[ts]
                                                        d = s['diameter']
                                                        self.cairo.set_source_rgb(par[0][0], par[0][1], par[0][2])
                                                        self.cairo.arc(s['x'], s['y'], (d + par[1] * 2) / 2, 0, 6.28)
                                                        self.cairo.fill()
                                                        for dst in trlist[n + 1:]:
                                                                ds = self.Stations[dst]
                                                                self.cairo.move_to(s['x'], s['y'])
                                                                self.cairo.line_to(ds['x'], ds['y'])
                                                        self.cairo.stroke()
                                                        drawn[ts] = 1
                if self.DEBUG:
                        print _("Draw transfers time:"), time.time() - start_time

                #draw circles
                self.Progress.progress_set_text(_("Draw circles"))
                if self.DEBUG:
                        start_time = time.time()
                drawn = dict() #avoid drawing same things many times
                for st in self.Stations.keys():
                        percentage += 10.0 / len(self.Stations)
                        self.Progress.progress_set_percentage(percentage)
                        if not len(self.Stations[st]['transfers']):
                                self.draw_st_circle(st)
                        elif not drawn.has_key(st):
                                trlist = [st] + self.Stations[st]['transfers']
                                trlist.sort(lambda a, b: cmp(self.Stations[a]['x'],  self.Stations[b]['x']))
                                ov = self.is_st_overlap(trlist)
                                for n in range(len(trlist)):
                                        if ov[0]:
                                                a1, a2 = self.draw_st_arc(trlist[n], ov[1], ov[2], ov[3], len(trlist), n)
                                                self.Stations[trlist[n]]['trrect'] = (ov[1], ov[2], ov[3], a1, a2)
                                        else:
                                                self.draw_st_circle(trlist[n])
                                        drawn[trlist[n]] = 1
                if self.DEBUG:
                        print _("Draw circles time:"), time.time() - start_time

                #draw station names
                self.Progress.progress_set_text(_("Draw station names"))
                if self.DEBUG:
                        start_time = time.time()
                        snum = 0
                        max_time = 0
                        max_time_s_name = ""
                for l in self.Lines.values():
                        percentage += 30.0 / len(self.Lines)
                        self.Progress.progress_set_percentage(percentage)
                        for st in l['stations']:
                                self.Progress.update()
                                s = self.Stations[st]
                                s['namerectact'] = s['namerect']
                                # x == 0 and y == 0 mean that station name shouldnt be displayed
                                if s['namerect'][0] == 0 and s['namerect'][1] == 0:
                                        continue
                                # if station name is on the right (or upper) side of line - align text to the left,
                                # otherwise align to the right
                                if (s['namerect'][2] >= s['namerect'][3] and s['namerect'][0] < s['x']) or \
                                   (s['namerect'][2] < s['namerect'][3]  and s['namerect'][1] > s['y']):
                                        self.pango_layout.set_alignment(pango.ALIGN_RIGHT)
                                else:
                                        self.pango_layout.set_alignment(pango.ALIGN_LEFT)
                                if self.DEBUG:
                                        snum += 1
                                        draw_start_time = time.time()
                                self.__draw_station_name(s['name'], l['labelscolor'], s['namerect'][0], s['namerect'][1], s['namerect'][2], s['namerect'][3])
                                if self.DEBUG:
                                        draw_time = time.time() - draw_start_time
                                        if draw_time > max_time:
                                                max_time = draw_time
                                                max_time_s_name = s['name']
                                inkRect, (x1, y1, w1, h1) = self.pango_layout.get_pixel_extents()
                                if s['namerect'][2] < s['namerect'][3]:
                                        if s['namerect'][1] > s['y']:
                                                s['namerectact'] = [s['namerect'][0] + y1, s['namerect'][1], h1 + 1, w1 + 1]
                                        else:
                                                s['namerectact'] = [s['namerect'][0] + y1, s['namerect'][1] + (s['namerect'][3] - w1), h1 + 1, w1 + 1]
                                else:
                                        s['namerectact'] = [s['namerect'][0] + x1, s['namerect'][1] + y1, w1 + 1, h1 + 1]
                if self.DEBUG:
                        print _("Draw station names time:"), time.time() - start_time
                        print _("Number of stations:"), snum
                        print _("Max draw station name time:"), max_time, "station:", max_time_s_name

                if self.DEBUG:
                        print _("Create pixbuf total time:"), time.time() - create_start_time

                if self.cachedir != None:
                        try:
                                self.Progress.progress_set_text(_("Saving map"))
                                pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, self.pw, self.ph)
                                self.Progress.update()
                                pb.get_from_drawable(self.pixbuf, self.pixbuf.get_colormap(), 0, 0, 0, 0, self.pw, self.ph)
                                self.Progress.update()
                                pb.save(self.cachedir + '/' + hd + '.png', 'png', {})
                                self.Progress.update()
                                f = file(self.cachedir + '/' + hd + '.data', 'w')
                                for l in self.Lines.values():
                                        for st in l['stations']:
                                                self.Progress.update()
                                                s = self.Stations[st]
                                                f.write(str(s['number']) + ',' +
                                                        str(s['namerectact'][0]) + ',' +
                                                        str(s['namerectact'][1]) + ',' +
                                                        str(s['namerectact'][2]) + ',' +
                                                        str(s['namerectact'][3]) + '\n')
                                f.close()
                        except:
                                for f in [self.cachedir + '/' + hd + '.png',
                                          self.cachedir + '/' + hd + '.data']:
                                        try:
                                                os.unlink(f)
                                        except:
                                                pass
                self.Progress.progress_end()
                self.da.show()
                return False

        def redraw(self, full):
                if not self.da.window or not self.gc or not self.pixbuf_created:
                        return True

                if full and self.pathlist and len(self.pathlist):
                        self.draw_path(self.pathlist, self.drawpath)
                self.step = 1 - self.step
                self.draw_station(self.stfrom, self.STATE_FROM)
                self.draw_station(self.stto, self.STATE_TO)
                return True

        def set_path_list(self, pathlist):
                if self.pathlist and len(self.pathlist):
                        self.drawpath = False
                        self.redraw(True)
                self.pathlist = pathlist
                self.drawpath = True
                self.redraw(True)

        def set_start_station(self, st):
                if self.pathlist and self.stto == st:
                        self.stto = None
                        self.draw_path(self.pathlist, False)
                self.draw_station(self.stfrom, self.STATE_OFF)
                self.stfrom = st
                self.redraw(True)

        def set_stop_station(self, st):
                if self.pathlist and self.stfrom == st:
                        self.stfrom = None
                        self.draw_path(self.pathlist, False)
                self.draw_station(self.stto, self.STATE_OFF)
                self.stto = st
                self.redraw(True)

        def set_selection_cb(self, cb):
                self.scb = cb

        def set_menu(self, callback, items):
                self.menu_cb = callback
                self.menu_items = items

        def menu_item_act(self, widget, data, adddata = None):
                if self.menu_cb:
                        self.menu_cb(data, adddata)

        def set_dtcb(self, cb):
                self.toggle_trans_cb = cb

        def set_one_button(self, one_button):
                self.one_button = one_button
                self.last_endpoint = 1

        def toggle_transfer(self, widget, fr, to):
                if self.toggle_trans_cb:
                        self.toggle_trans_cb('set', sfrom = fr, sto = to)

        def menu(self, st):
                def st_line(fr):
                        return self.Stations[fr]['name'] + " " + self.Lines[self.Stations[fr]['line']]['name']

                if self.button_pressed:
                        self.button_pressed = None
                        menu = gtk.Menu()
                        dtdict = dict()
                        if self.toggle_trans_cb:
                                dtdict = self.toggle_trans_cb('get')

                        if st and len(self.Stations[st]['transfers']):
                                for t in self.Stations[st]['transfers']:
                                        if dtdict.has_key((st, t)):
                                                msg = _("Enable")
                                        else:
                                                msg = _("Disable")

                                        i = gtk.MenuItem(msg + _(' transfer from "') +
                                                         st_line(st) + _('" to "') +
                                                         st_line(t) + '"')
                                        i.connect('activate', self.toggle_transfer, st, t)
                                        menu.append(i)

                        if len(dtdict.keys()):
                                if st and len(self.Stations[st]['transfers']):
                                        i = gtk.SeparatorMenuItem()
                                        menu.append(i)
                                i = gtk.MenuItem(_('Enable disabled transfers'))
                                dtmenu = gtk.Menu()
                                if len(dtdict.keys()) > 2:
                                        di = gtk.MenuItem(_("All"))
                                        di.connect('activate', self.toggle_transfer, None, None)
                                        dtmenu.append(di)

                                added = dict()
                                for dt in dtdict.keys():
                                        if not added.has_key((dt[0], dt[1])):
                                                di = gtk.MenuItem(_('From "') +
                                                                  st_line(dt[0]) + _('" to "') +
                                                                  st_line(dt[1]) + '"')
                                                di.connect('activate', self.toggle_transfer, dt[0], dt[1])
                                                dtmenu.append(di)
                                                added[(dt[0], dt[1])] = 1
                                                added[(dt[1], dt[0])] = 1

                                i.set_submenu(dtmenu)
                                menu.append(i)

                        if len(dtdict.keys()) or (st and len(self.Stations[st]['transfers'])):
                                        i = gtk.SeparatorMenuItem()
                                        menu.append(i)

                        for item in self.menu_items:
                                if type(item[0]) == str or type(item[0]) == unicode:
                                        if item[2] or st != None:
                                                i = gtk.MenuItem(item[0])
                                                i.connect('activate', self.menu_item_act, item[1], st)
                                                menu.append(i)
                                else:
                                        i = gtk.MenuItem(item[0][0])
                                        submenu = gtk.Menu()
                                        for foo in item[0][1]:
                                                si = gtk.MenuItem(foo[0])
                                                si.connect('activate', self.menu_item_act, item[1] + ' ' + str(foo[1]))
                                                submenu.append(si)

                                        i.set_submenu(submenu)
                                        menu.append(i)

                        menu.show_all()
                        menu.popup(None, None, None, 3, 0)
                return False

        def find_st_at_pos(self, x, y):
                #first - search in transfers
                for st in filter(lambda a: a.has_key('trrect'), self.Stations.values()):
                        d = sqrt((x - st['trrect'][0]) ** 2 + (y - st['trrect'][1]) ** 2)
                        if d <= st['trrect'][2] / 2:
                                if x == st['trrect'][0]:
                                        a = 90
                                else:
                                        a = degrees(atan((y - st['trrect'][1]) / (x - st['trrect'][0])))
                                if a < 0:
                                        a = -a
                                elif a != 0 or x < st['trrect'][0]:
                                        a = 180 - a
                                if y > st['trrect'][1]:
                                        a += 180
                                a *= 64
                                a1 = st['trrect'][3]
                                a2 = a1 + st['trrect'][4]
                                if ((a >= a1 and a <= a2) or (a2 > 360 * 64 and a < a2 - 360 * 64)):
                                        return st['number']

                for st in self.Stations.values():
                        sx, sy, sd = st['x'], st['y'], st['diameter'] / 2 + 2
                        snx, sny, snw, snh = st['namerectact']
                        if (x > sx - sd and x < sx + sd and y > sy - sd and y < sy + sd) or\
                           (x > snx and x < snx + snw and y > sny and y < sny + snh):
                                return st['number']
                return None

        def bp(self, widget, event):
                if (event.type == gtk.gdk.BUTTON_RELEASE and event.button != self.button_pressed) or \
                   (event.type == gtk.gdk.BUTTON_PRESS and self.button_pressed != None):
                        self.button_pressed = None
                        return

                if event.type == gtk.gdk.BUTTON_PRESS:
                        self.button_pressed = event.button
                        self.button_pressed_coords = [event.x_root, event.y_root]
                        if event.button == 3 or (event.button == 1 and self.one_button):
                                gobject.timeout_add(200, self.menu, self.find_st_at_pos(event.get_coords()[0] - self.dx, event.get_coords()[1] - self.dy))
                        return

                if self.button_pressed_coords[0] > event.x_root + self.button_press_treshold or \
                   self.button_pressed_coords[0] < event.x_root - self.button_press_treshold or \
                   self.button_pressed_coords[1] > event.y_root + self.button_press_treshold or \
                   self.button_pressed_coords[1] < event.y_root - self.button_press_treshold:
                        self.button_pressed = None
                        return

                if event.type == gtk.gdk.MOTION_NOTIFY or (event.button != 1 and event.button != 3):
                        return

                self.button_pressed = None
                if self.one_button:
                        start = self.last_endpoint
                else:
                        start = False
                        if event.button == 1:
                                start = True

                n = self.find_st_at_pos(event.get_coords()[0] - self.dx, event.get_coords()[1] - self.dy)
                if n != None and self.scb:
                        if self.one_button:
                                self.last_endpoint = 1 - self.last_endpoint
                        elif n == self.stto or n == self.stfrom:
                                return
                        self.scb(n, start)

        def __update_vars(self):
                self.daw = self.da.get_allocation().width
                self.dah = self.da.get_allocation().height
                self.dx = (self.daw - self.pw) / 2
                self.dy = (self.dah - self.ph) / 2

        def __configure_cb(self, foo, bar):
                self.__update_vars()

        def expose(self, widget, event):
                if not self.gc:
                        self.gc = gtk.gdk.GC(self.da.window)
                        self.gc.set_fill(gtk.gdk.SOLID)

                x, y, w, h = event.area.x, event.area.y, event.area.width, event.area.height

                #clip event region
                if x < self.dx:
                        w -= self.dx - x
                        x = self.dx
                if y < self.dy:
                        h -= self.dy - y
                        y = self.dy

                if x + w > self.dx + self.pw:
                        w = self.pw + self.dx - x

                if y + h > self.dy + self.ph:
                        h = self.ph + self.dy - y

                if w > 0 and h > 0 and self.pixbuf:
	        	self.da.window.draw_drawable(self.gc, self.pixbuf, x - self.dx, y - self.dy, x, y, w, h)
                        self.redraw(True)

        def draw_pic(self, x, y, w, h):
		self.da.window.draw_drawable(self.gc, self.pixbuf, x, y, self.dx + x, self.dy + y, w, h)

class StationDisplay:
        def __init__(self, vectors):
                if not vectors.has_key('width'):
                        self.da = None
                        return
                self.gc = None
                self.pixbuf = None
                self.vectors = vectors
                self.da = gtk.DrawingArea()
                self.da.set_size_request(self.vectors['width'], self.vectors['height'])
                self.da.set_events(gtk.gdk.EXPOSURE_MASK |
                                   gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK |
                                   gtk.gdk.KEY_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK)
                self.da.connect("expose_event", self.expose)
                self.da.connect('configure_event', self.__configure_cb)

                self.daw = self.da.get_allocation().width
                self.dah = self.da.get_allocation().height
                self.pw = self.vectors['width']
                self.ph = self.vectors['height']
                self.dx = 0
                self.dy = 0

        def __configure_cb(self, foo, bar):
                self.daw = self.da.get_allocation().width
                self.dah = self.da.get_allocation().height
                self.dx = (self.daw - self.pw) / 2
                self.dy = (self.dah - self.ph) / 2

        def create_pixbuf(self):
                self.pixbuf = gtk.gdk.Pixmap(self.da.window, self.vectors['width'], self.vectors['height'], -1)
                self.cairo = self.pixbuf.cairo_create()
                self.cairo.set_source_rgb(255, 255, 255)
                self.cairo.rectangle(0, 0, self.pw, self.ph)
                self.cairo.fill()
                draw_vectors_cairo(self.da, self.cairo, self.vectors['instructions'])

        def expose(self, widget, event):
                if not self.gc:
                        self.gc = gtk.gdk.GC(self.da.window)
                        self.gc.set_fill(gtk.gdk.SOLID)

                x, y, w, h = event.area.x, event.area.y, event.area.width, event.area.height

                #clip event region
                if x < self.dx:
                        w -= self.dx - x
                        x = self.dx
                if y < self.dy:
                        h -= self.dy - y
                        y = self.dy

                if x + w > self.dx + self.pw:
                        w = self.pw + self.dx - x

                if y + h > self.dy + self.ph:
                        h = self.ph + self.dy - y

                if w > 0 and h > 0:
                        if not self.pixbuf:
                                self.create_pixbuf()
	        	self.da.window.draw_drawable(self.gc, self.pixbuf, x - self.dx, y - self.dy, x, y, w, h)
