#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# gtk3.glal.py
"""This module provides a graphic library abstraction layer for Cyclograph"""

# Copyright (C) 2008-2016 Federico Brega, Pierluigi Villani

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

import math
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, Gdk, GLib
import cairo
from . import gui

GObject.threads_init()


def initapp():
    return Gtk.main()


def bind(cgfunc, menuopt):
    """Connect gui menu option with cg function"""
    return menuopt.connect("activate", cgfunc)


def bind_close(cg_exit, gui):
    """Connect gui close event with cg exit function"""
    gui.cg_exit = cg_exit
    bind(gui.exit, gui.menu_item_exit)


def ToolbarBind(fun, button):
    """Connect gui toolbar button with cg function"""
    return button.connect("clicked", fun)


def OptionCheck(guiopt, option):
    """Set a gui menu option to selected/unselected """
    guiopt.set_active(option)


def enable_saving(gui, bool_val):
    """Enable or disable saving options according to bool_val"""
    gui.menu_item_save.set_sensitive(bool_val)
    gui.menu_item_save_as.set_sensitive(bool_val)
    gui.action_add.set_sensitive(bool_val)
    gui.action_edit.set_sensitive(bool_val)
    gui.action_delete.set_sensitive(bool_val)
    gui.action_plot.set_sensitive(bool_val)
    gui.action_map.set_sensitive(bool_val)
    gui.action_properties.set_sensitive(bool_val)


def addstatusbartext(maingui, text):
    """Add text to main gui status bar"""
    maingui.addstatusbartext(text)
    maingui.sbtimer.start()


def signalbug(event=None):
    """Open webpage to signal bugs"""
    bugUrl = 'http://sourceforge.net/project/memberlist.php?group_id=227295'
    # Looks like gtk hasn't any function to launch an external browser,
    # the best we can do is use python webbrowser
    import webbrowser
    webbrowser.open(bugUrl, new=True, autoraise=True)


class Message():
    """ Gtk3 message"""
    def __init__(self):
        class Messenger(GObject.GObject):
            __gsignals__ = {
                str("UPDATE_TAB"): (GObject.SignalFlags.ACTION, None,
                                    (int, int, )),
                str("SLOPE_ADD"): (GObject.SignalFlags.ACTION, None,
                                   (int, int, )),
                str("SLOPE_DEL"): (GObject.SignalFlags.ACTION, None,
                                   (int, int, )),
                }
        self.gobj = Messenger()

    def send(self, message, slope_number, row_num):
        """ Send message"""
        self.gobj.emit(message, slope_number, row_num)

    def subscribe(self, function, emitter, message):
        """ Subscribe message"""
        self.handler = function
        emitter.gobj.connect(message, self._reformat_handler_param)

    def _reformat_handler_param(self, emitter, arg1, arg2):
        self.handler(arg1, arg2)


class Notebook:
    """Gtk3 notebook"""
    def __init__(self, notebook, fun_close):
        self.notebook = notebook
        self.fun_close = fun_close
        self.notebook.set_scrollable(True)

    def Page(self):
        """Creates page"""
        return gui.Page(self.notebook)

    def set_page_label(self, page_num,  text):
        """Set page label text"""
        lab = self.notebook.get_tab_label(self.get_page(page_num))
        lab.label.set_text(text)

    def add_page(self, page, title):
        """Add page to the notebook"""
        box = gui.tab_label(page, title)
        page.button_close = box.closebtn
        page.button_close.connect("clicked", self.fun_close, page)
        self.notebook.append_page(page, box)
        self.notebook.show_all()
        nbpages = self.notebook.get_n_pages()
        self.notebook.set_current_page(nbpages-1)

    def remove_page(self, page_num):
        """Remove page from the notebook"""
        self.notebook.remove_page(page_num)

    def getselpagnum(self):
        """Return current selected page"""
        return self.notebook.get_current_page()

    def setselpagnum(self, page_num):
        """Set current selected page"""
        self.notebook.set_current_page(page_num)

    def get_page(self, page_num):
        """Return page at page_num"""
        return self.notebook.get_nth_page(page_num)

    def get_pagenum(self, argument):
        """Return page_num from page passed as argument"""
        return self.notebook.page_num(argument[1])


class DeviceContext():
    """Device Context Gtk3 class wrapper"""
    def _convcolor(self, color):
        """ Convert color"""
        col = color
        if isinstance(color, str):
            if color.startswith("rgb"):
                col = [(int(component) << 8)/65535.0 for component in color[4:-1].split(',')]
            elif color == "white":
                col = [1, 1, 1]
            else:
                col = [0, 0, 0]
        return col

    def init_draw_surf(self, widget, width, height):
        """Initialize device context"""
        self.size_x = width
        self.size_y = height
        self.devc = widget

        self.FONT_TYP = {
                         "light": cairo.FONT_WEIGHT_NORMAL,
                         "normal": cairo.FONT_WEIGHT_NORMAL,
                         "bold": cairo.FONT_WEIGHT_BOLD,
                        }
        # pen color
        self.pcolor = [0, 0, 0]
        # brush color
        self.bcolor = [0, 0, 0]

    def shear(self, shear):
        """ device shear"""
        matrix = cairo.Matrix(yx = shear)
        self.devc.set_matrix(matrix)

    def getsize(self):
        """Return device context dimensions"""
        return (self.size_x, self.size_y)

    def gradfill(self, rect, startcolor, endcolor):
        """Fill context with gradient"""
        (startx, starty) = (rect[0], rect[1])
        scolor = self._convcolor(startcolor)
        ecolor = self._convcolor(endcolor)
        lg1 = cairo.LinearGradient(0, 160.0,  0, self.size_y)
        lg1.add_color_stop_rgb(0, *scolor)
        lg1.add_color_stop_rgb(1, *ecolor)
        self.devc.rectangle(startx, starty, self.size_x, self.size_y)
        self.devc.set_source(lg1)
        self.devc.fill()

    def setpen(self, color, size):
        """Set pen color and size"""
        self.devc.set_line_width(size)
        self.pcolor = self._convcolor(color)

    def setfont(self, font):
        """Set font"""
        try:
            self.devc.select_font_face(font['des'], cairo.FONT_SLANT_NORMAL,
                                       self.FONT_TYP[font['typ']])
            self.devc.set_font_size(font['dim'])
        except Exception:
            pass

    def drawtext(self, text, pos_x, pos_y):
        """Draw text"""
        self.devc.set_source_rgb(*self.pcolor)
        self.devc.move_to(pos_x, pos_y+12)
        self.devc.show_text(text)

    def gettextwidth(self, text):
        """ Return text length"""
        return int(self.devc.text_extents(text)[2])

    def gettextheight(self, text):
        """ Return text height"""
        return int(self.devc.text_extents(text)[3])

    def drawline(self, pos_x0, pos_y0, pos_x1, pos_y1):
        """Draw line"""
        self.devc.set_source_rgb(*self.pcolor)
        self.devc.move_to(pos_x0, pos_y0)
        self.devc.line_to(pos_x1, pos_y1)
        self.devc.stroke()

    def setlineargradientbrush(self, colorlist, startp, endp):
        """ Get a linear gradient from startp to endp, using colors in colorlist.
        The elments of colorlist are tuple in the format (color, realtive position)."""
        grad = cairo.LinearGradient(startp[0], startp[1], endp[0], endp[1])
        for color in colorlist:
            grad.add_color_stop_rgb(color[1], *self._convcolor(color[0]))
        self.pattern = grad

    def setbrush(self, color):
        """Set brush color"""
        self.pattern = cairo.SolidPattern(*self._convcolor(color))

    def drawrectangle(self, pos_x0, pos_y0, width, height):
        """Draw rectangle"""
        self.devc.set_source_rgb(0, 0, 0)
        self.devc.rectangle(pos_x0, pos_y0, width, height)
        self.devc.stroke_preserve()
        self.devc.set_source(self.pattern)
        self.devc.fill()

    def drawrotatedtext(self, text, pos_x, pos_y, angle):
        """Draw rotated text"""
        self.devc.save()
        self.devc.translate(pos_x, pos_y)
        self.devc.rotate(-math.radians(angle))
        self.drawtext(text, 0, 0)
        self.devc.restore()

    def drawpolygon(self, sequence):
        """Draw polygon"""
        self.devc.set_source_rgb(0, 0, 0)
        self.devc.move_to(*sequence[0])
        for point in sequence:
            self.devc.line_to(*point)
        self.devc.close_path()
        self.devc.stroke_preserve()
        self.devc.set_source(self.pattern)
        self.devc.fill()

    def startpath(self, point):
        """ Start a path in the specified point,"""
        (pointx, pointy) = point
        self.devc.set_source_rgb(0, 0, 0)
        self.devc.move_to(pointx, pointy)
        return None

    def drawpathlineto(self, path, point):
        """ Draw a straight line from the last point to the given point."""
        (pointx, pointy) = point
        self.devc.line_to(pointx, pointy)

    def drawpathcubicto(self, path, controlpoints):
        """ Draw a cubic Beziér from the last point using the given list of
        three control points."""
        points = []
        for pnt in controlpoints:
            points.append(pnt[0])
            points.append(pnt[1])
        self.devc.curve_to(*points)

    def endpath(self, path):
        """ Show the path."""
        self.devc.set_source_rgb(0, 0, 0)
        self.devc.stroke_preserve()
        self.devc.set_source(self.pattern)
        self.devc.fill()

    def end_draw(self):
        """End drawing not used for gtk gui"""
        pass


class Image():
    """Gtk3 image class wrapper"""
    def __init__(self, size_x, size_y, plotfnct):
        (self.size_x, self.size_y) = (size_x,  size_y)
        self.plotfnct = plotfnct
        self.image = cairo.ImageSurface(cairo.FORMAT_RGB24,
                                         self.size_x, self.size_y)

    def plot(self, settings):
        """Plot image"""
        dcwrpp = DeviceContext()
        dcwrpp.init_draw_surf(cairo.Context(self.image),
                              self.size_x, self.size_y)
        self.plotfnct(settings, dcwrpp)

    def savetofile(self, path, format):
        """Save slope to image"""
        if format.lower() == 'png':
            self.image.write_to_png(path)
        if format.lower() == 'bmp':
            width, height = self.image.get_width(), self.image.get_height()
            pixbuf = Gdk.pixbuf_get_from_surface(self.image, 0, 0, width, height)
            pixbuf.savev(path, "bmp", [], [])
        if format.lower() == 'jpg':
            width, height = self.image.get_width(), self.image.get_height()
            pixbuf = Gdk.pixbuf_get_from_surface(self.image, 0, 0, width, height)
            pixbuf.savev(path, "jpeg", ["quality"], ["100"])


class Pdf():
    """Gtk3 pdf class wrapper"""
    def __init__(self, filepath):
        width, height = 793, 1122
        self.dy = 90
        self.dx = 300
        self.y_incr = 30
        self.surface = cairo.PDFSurface(filepath, width, height)
        self.context = cairo.Context(self.surface)
        # white background
        self.context.set_source_rgb(1, 1, 1)
        self.context.rectangle(0, 0, width, height)
        self.context.fill()
        self.context.select_font_face("Arial")
        self.context.set_font_size(18)

    def plot_image(self, settings, size_x, size_y, plotfnct):
        """Plot image"""
        image = cairo.SVGSurface(None, size_x, size_y)
        dcwrpp = DeviceContext()
        dcwrpp.init_draw_surf(cairo.Context(image), size_x, size_y)
        plotfnct(settings, dcwrpp)
        self.context.set_source_surface(image, 0, 0)
        self.context.paint()
        self.dy += size_y

    def addtext(self, text):
        self.dy += self.y_incr
        self.context.set_source_rgb(0, 0, 0)
        self.context.move_to(self.dx, self.dy)
        self.context.show_text(text)

    def addtitle(self, text):
        self.dy += self.y_incr
        self.context.set_source_rgb(0, 0, 0.4)
        self.context.move_to(self.dx-50, self.dy)
        self.context.show_text(text+":")

    def save(self):
        """Save slope to pdf"""
        self.context.show_page()
        self.surface.finish()


class ProgressDialog():
    """Gtk3 Progress dialog"""
    def __init__(self):
        self.pdialog = gui.ProgressDialog(_("Downloading altitudes"),
            _("Please wait, Cyclograph is downloading altitudes."), 1000.0)

    def update(self, value):
        """Update the progress shown and return if user want to abort."""
        return self.pdialog.update(value)

    def destroy(self):
        """Destroy dialog"""
        self.pdialog.destroy()


class Timer():
    """Gtk3 timer"""
    def __init__(self, period, callback):
        self.timer = 0
        self.callback = callback
        self.period = period

    def start(self):
        """Start timer"""
        self.timer = GLib.timeout_add(self.period, self.callback)

    def stop(self):
        """Stop timer"""
        GObject.source_remove(self.timer)
        self.timer = 0

# vim:sw=4:softtabstop=4:expandtab
