# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
### BEGIN LICENSE
# Copyright (C) 2012 David Planella <david.planella@ubuntu.com>
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
### END LICENSE

import os
import tempfile
import atexit
import cairo
import math
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject  # pylint: disable=E0611
import logging
logger = logging.getLogger('qreator')

from qreator_lib.i18n import _
from qreator_lib import Window
from qreator_lib.helpers import get_media_file

from .QRCode import QRCode
from .QRCode import QRCodeOutput

from qreator.qrcodes.QRCodeType import QRCodeType

COL_DESC = 0
COL_PIXBUF = 1
COL_ID = 2

PAGE_NEW = 0
#PAGE_HISTORY = 1
PAGE_ABOUT = 1
PAGE_QR = 2


# See qreator_lib.Window.py for more details about how this class works
class QreatorWindow(Window):   # pylint: disable=W0232
    __gtype_name__ = "QreatorWindow"

    def finish_initializing(self, builder):  # pylint: disable=E1002
        """Set up the main window"""
        super(QreatorWindow, self).finish_initializing(builder)

        # Code for other initialization actions should be added here.

        # Initialize the clipboard
        # pylint: disable=W0201
        self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)

        # Initialize the style for the main toolbar
        context = self.ui.toolbar1.get_style_context()
        context.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)

        self.ui.toolbuttonNew.set_sensitive(False)
        self.ui.mnu_new.set_sensitive(False)

        # Initialize the Cairo surface that will contain the QR code
        self.surface = None

        # Hide the notebook tabs
        self.ui.notebook1.set_show_tabs(False)

        # Initialize about dialog
        self.init_about_dialog()

        # Create printing dialog
        self.reset_print_dialog()

        # Initialize the QR types icon view
        self.init_qr_types()

        # Load the background texture in the QR code page
        self.texture = cairo.ImageSurface.create_from_png(
            get_media_file("pattern.png"))

        # Time to wait in milliseconds before switching to the next view
        # after having clicked once on an iconview icon
        self.iconview_activation_delay = 200

        # Initialize color editing
        self.current_color_bg = Gdk.Color(65535, 65535, 65535)  # White
        self.previous_color_bg = Gdk.Color(65535, 65535, 65535)
        self.current_color_fg = Gdk.Color(0, 0, 0)  # Black
        self.previous_color_fg = Gdk.Color(0, 0, 0)

        # Add an initial text, so that there is an initial QR code
        self.qr_code_placeholder = 'http://launchpad.net/qreator'

        # Prepare the qrcode for drag n drop
        self.tempfilepath = None

        # pylint: disable=W0613
        def on_drag_data_get(widget, drag_context, selection_data,
                             info, time):
            if self.tempfilepath is None:
                # pylint: disable=W0612
                handle, self.tempfilepath = tempfile.mkstemp(
                    suffix=".png", prefix="qreator_")
                pixbuf = self.get_pixbuf_from_drawing_area()
                pixbuf.savev(self.tempfilepath, 'png', [], [])
            selection_data.set_uris(["file://{}".format(self.tempfilepath), ])
            return True

        def remove_tempfile(path):
            if os.path.exists(path):
                os.remove(path)
            return False

        def on_drag_end(widget, drag_context):
            atexit.register(remove_tempfile, self.tempfilepath)
            self.tempfilepath = None

        self.ui.qr_drawingarea.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                                               [], Gdk.DragAction.COPY)
        self.ui.qr_drawingarea.drag_source_add_uri_targets()
        self.ui.qr_drawingarea.connect("drag_data_get", on_drag_data_get)
        self.ui.qr_drawingarea.connect("drag_end", on_drag_end)

    def init_qr_types(self):
        # Set up the QR types shown in the main icon view
        self.ui.qr_types_view.set_text_column(COL_DESC)
        self.ui.qr_types_view.set_pixbuf_column(COL_PIXBUF)

        # By importing the different qrcode dataformats in the __init__ file,
        # they have automatically registered themselves in the base class.
        self.qr_types = [d(self.update_qr_code) for d in QRCodeType.dataformats]
        # pylint: disable=W0201
        self.qr_types_store = Gtk.ListStore(str, GdkPixbuf.Pixbuf, int)
        # ^ desc, icon, id ^
        self.qr_types_store.set_sort_column_id(COL_DESC,
                                               Gtk.SortType.ASCENDING)
        self.ui.qr_types_view.set_model(self.qr_types_store)
        self.fill_qr_types_store()

        self.curr_height = 0
        self.curr_width = 0

        for qr_type in self.qr_types:
            # keep the window from growing with each qrtype
            qr_type.widget.grid.hide()
            self.ui.qr_input_box.add(qr_type.widget.grid)
            qr_type.widget.on_prepared()

    def fill_qr_types_store(self):
        self.qr_types_store.clear()

        for qr_id, qr_type in enumerate(self.qr_types):
            icon = GdkPixbuf.Pixbuf.new_from_file_at_size(qr_type.icon_path, 64, 64)
            self.qr_types_store.append([qr_type.description,
                                        icon,
                                        qr_id])

    def on_qreator_window_check_resize(self, widget):
        '''We need this function to fix the issue described at
        http://bit.ly/LW94BO whereby the number of columns of the icon view
        widget stays fixed at the number set for the initial width of the
        window'''
        # Get the new size
        new_width = widget.get_size()[0]
        new_height = widget.get_size()[1]
        # If the size has changed...
        if(new_width != self.curr_width or new_height != self.curr_height):
            # Remember new size
            self.curr_width = new_width  # pylint: disable=W0201
            self.curr_height = new_height  # pylint: disable=W0201
            # and refill iconviews with icons to adjust columns number
            self.fill_qr_types_store()

    def init_about_dialog(self):
        # Initialize about dialog
        about = Gtk.AboutDialog()
        about.set_program_name("Qreator")
        about.set_copyright(
            "Copyright (c) 2012-2013 David Planella")
        about.set_website("http://davidplanella.org/project-showcase/qreator/")

        #FIXME: the version should be picked up from setup.py
        about.set_version('16.06.2')
        about.set_authors([
            'David Planella <david.planella@ubuntu.com>',
            'Stefan Schwarzburg <stefan.schwarzburg@googlemail.com>',
            'Michael Hall <mhall119@ubuntu.com>',
            'Andrew Starr-Bochicchio <andrewsomething@ubuntu.com>',
            'Jef Spaleta <jspaleta@fedoraproject.org>',
            'Fong LH <fonglh@gmail.com>',
            'Ilya Volchanetskiy <volchanetskiy@ukr.net>',
            'Felix Eckhofer <felix@eckhofer.com>'
            ])
        about.set_license(_('Distributed under the GPL v3 license.') +
                '\nhttp://www.opensource.org/licenses/gpl-3.0.html')
        about.set_website(None)
        about.set_logo_icon_name(None)

        # Workaround for bug #1031657, remove David from translation credits
        # unless the locale is Catalan
        import locale
        # pylint: disable=W0612
        default_locale, encoding = locale.getdefaultlocale()
        # Gtk.AboutDialog does not recognize https and would not render the
        # markup otherwise
        translator_credits = _("translator-credits").replace('https', 'http')
        try:
            language_code = default_locale.split('_')[0]
        except AttributeError:
            language_code = default_locale
        if language_code != 'ca':
            translator_credits = translator_credits.replace(
                'David Planella' + ' ' + 'http://launchpad.net/~dpm',
                '').strip()
        about.set_translator_credits(translator_credits)

        box = self.ui.about_box
        about.vbox.reparent(box)

        # Get rid of the 'Close' button
        for button in about.action_area.get_children():
            if button.get_property('label') == 'gtk-close':
                button.destroy()

##########################################
    # pylint: disable=W0613
    def on_toolbuttonNew_clicked(self, widget, data=None):
        '''Shows the home page'''
        self.ui.qr_types_view.unselect_all()
        self.ui.toolbuttonNew.set_sensitive(False)
        self.ui.mnu_new.set_sensitive(False)
        self.ui.notebook1.set_current_page(PAGE_NEW)

    def on_toolbuttonHistory_clicked(self, widget, data=None):
        '''Shows the history page'''
        pass  # self.ui.notebook1.set_current_page(PAGE_HISTORY)

    # pylint: disable=W0613
    def on_toolbuttonAbout_clicked(self, widget, data=None):
        '''Shows the about page'''
        self.ui.notebook1.set_current_page(PAGE_ABOUT)
        self.ui.toolbuttonNew.set_sensitive(True)
        self.ui.mnu_new.set_sensitive(True)

##########################################
    def on_qr_types_view_selection_changed(self, widget):
        if len(widget.get_selected_items()) > 0:
            GObject.idle_add(self.on_qr_types_view_item_activated,
                             widget, widget.get_selected_items()[0])

    def on_qr_types_view_item_activated(self, widget, item):
        '''Loads the UI for the appropriate QR type'''

        model = widget.get_model()
        qr_id = model[item][COL_ID]
        self.switch_qrcode_view(qr_id)

    def switch_qrcode_view(self, qr_id):
        self.ui.toolbuttonNew.set_sensitive(True)
        self.ui.mnu_new.set_sensitive(True)

        self.qr_types[qr_id].widget.on_activated()

        def switch_callback(page):
            self.ui.notebook1.set_current_page(page)
            return False  # stop this callback from being called again
        GObject.timeout_add(self.iconview_activation_delay, switch_callback,
                            PAGE_QR)

        for child in self.ui.qr_input_box.get_children():
            child.hide()
        self.ui.qr_input_box.get_children()[qr_id].show()

##########################################

    def get_pixbuf_from_drawing_area(self):
        window = self.ui.qr_drawingarea.get_window()

        src_x, src_y = self.get_centered_coordinates(self.ui.qr_drawingarea,
                                                     self.surface)
        image_height = self.surface.get_height()
        image_width = self.surface.get_width()

        return Gdk.pixbuf_get_from_window(window, src_x, src_y,
                                          image_width, image_height)

##########################################
    def reset_print_dialog(self):
        # pylint: disable=W0201
        self.print_dialog = Gtk.PrintOperation()
        self.print_dialog.set_n_pages(1)
        # pylint: disable=W0201
        self.print_handler = self.builder.get_object(
            "toolbuttonPrint").connect(
            "clicked", self.on_toolbuttonPrint_clicked)

    # pylint: disable=W0613
    def on_toolbuttonPrint_clicked(self, widget, data=None):
        self.builder.get_object("toolbuttonPrint").disconnect(
            self.print_handler)
        self.print_dialog.connect(
            "draw_page", self.draw_page)
        result = self.print_dialog.run(
            Gtk.PrintOperationAction.PRINT_DIALOG, None)
        self.reset_print_dialog()

    # pylint: disable=W0613
    def draw_page(self, operation=None, context=None, page_nr=None):
        ctx = context.get_cairo_context()
        ctx.set_source_surface(self.surface)
        # Render the image
        ctx.paint()
        return

    def on_toolbuttonSave_clicked(self, widget, data=None):
        if not self.surface:
            return

        dialog = Gtk.FileChooserDialog(_("Please choose a file"), self,
            Gtk.FileChooserAction.SAVE,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_SAVE, Gtk.ResponseType.OK))

        filter_png = Gtk.FileFilter()
        filter_png.set_name(_("PNG images"))
        filter_png.add_mime_type("image/png")
        dialog.add_filter(filter_png)

        response = dialog.run()

        if response == Gtk.ResponseType.OK:
            # We cannot write directly from the surface, as the
            # Surface.write_to_png() method writes the image in the original
            # size returned by qrencode (i.e. non-scaled), and the
            # SurfacePattern does not have any methods to write to disk.
            # So we read the contents from the Gtk.DrawingArea, put them into
            # a Gdk.Pixbuf and use its 'savev' method to write to disk.

            pixbuf = self.get_pixbuf_from_drawing_area()

            pixbuf.savev(dialog.get_filename(), 'png', [], [])

        dialog.destroy()

    def on_toolbuttonCopy_clicked(self, widget, data=None):
        if not self.surface:
            return

        pixbuf = self.get_pixbuf_from_drawing_area()
        self.clipboard.set_image(pixbuf)

    def on_toolbuttonEdit_clicked(self, widget, data=None):
        if widget.get_active():
            self.ui.toolbar2.show()
        else:
            self.ui.toolbar2.hide()

##########################################

    def set_selector_colors(self):
        '''Preloads the color selector with the current and previous
        QR code colors'''
        colorsel = self.ui.color_selection_dialog.get_color_selection()
        if self.ui.radiobuttonfg.get_active():
            colorsel.set_current_color(self.current_color_fg)
            colorsel.set_previous_color(self.previous_color_fg)
        elif self.ui.radiobuttonbg.get_active():
            colorsel.set_current_color(self.current_color_bg)
            colorsel.set_previous_color(self.previous_color_bg)

    def on_toolbuttonColorSelector_clicked(self, widget, data=None):
        '''Loads the color selector dialog and saves the chosen
        current and previous QR code colors'''
        colorsel = self.ui.color_selection_dialog.get_color_selection()

        self.set_selector_colors()

        colordiag = self.ui.color_selection_dialog
        response = colordiag.run()

        # It seems that when invoked from Glade, the color selector dialog
        # returns an integer instead of Gtk.ResponseType.OK, and we need to
        # set this integer explicitly in the .ui file. The 'OK' button
        # returns '1'.
        if response == 1:
            if self.ui.radiobuttonfg.get_active():
                self.current_color_fg = colorsel.get_current_color()
                self.previous_color_fg = colorsel.get_previous_color()
            elif self.ui.radiobuttonbg.get_active():
                self.current_color_bg = colorsel.get_current_color()
                self.previous_color_bg = colorsel.get_previous_color()

        self.update_qr_code(self.qr_code_placeholder)

        self.ui.color_selection_dialog.hide()

    def on_radiobuttonfg_toggled(self, widget, data=None):
        '''Loads the foreground color in the selector when the foreground
        radio button is toggled'''
        self.set_selector_colors()

    def on_radiobuttonbg_toggled(self, widget, data=None):
        '''Loads the foreground color in the selector when the background
        radio button is toggled'''
        self.set_selector_colors()

    def on_toolbuttonColorSwap_clicked(self, widget, data=None):
        """Swaps the QR code's foreground and background colors"""

        self.previous_color_bg = self.current_color_bg
        self.previous_color_fg = self.current_color_fg

        self.current_color_bg = self.current_color_fg
        self.current_color_fg = self.previous_color_bg

        self.update_qr_code(self.qr_code_placeholder)

    def on_toolbuttonColorReset_clicked(self, widget, data=None):
        '''Reset QR code to defaults. At this point only the colors
        can be edited, so effectively it resets the foreground and
        background colors to black and white, respectively'''
        # pylint: disable=W0201
        self.current_color_bg = Gdk.Color(65535, 65535, 65535)  # White
        self.previous_color_bg = Gdk.Color(65535, 65535, 65535)
        self.current_color_fg = Gdk.Color(0, 0, 0)  # Black
        self.previous_color_fg = Gdk.Color(0, 0, 0)

        self.update_qr_code(self.qr_code_placeholder)

##########################################

    def update_qr_code(self, text):
        # pylint: disable=W0201
        self.qr_code_placeholder = text
        self.ui.qr_drawingarea.queue_draw()

    def get_centered_coordinates(self, drawing_area, surface):

        drawing_area_height = drawing_area.get_allocated_height()
        drawing_area_width = drawing_area.get_allocated_width()
        image_height = surface.get_height()
        image_width = surface.get_width()

        return (drawing_area_width / 2 - image_width / 2,
                drawing_area_height / 2 - image_height / 2)

    def on_qr_drawingarea_draw(self, widget, ctx, data=None):
        text = self.qr_code_placeholder

        ## Fill the background

        # Create a rounded rectanble
        x = 0.0
        y = 0.0
        width = widget.get_allocated_width()
        height = widget.get_allocated_height()
        aspect = 1.0
        corner_radius = height / 50.0

        radius = corner_radius / aspect
        degrees = math.pi / 180.0

        ctx.new_sub_path()
        ctx.arc(x + width - radius, y + radius, radius, -90 * degrees,
                0 * degrees)
        ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees,
                90 * degrees)
        ctx.arc(x + radius, y + height - radius, radius, 90 * degrees,
                180 * degrees)
        ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees)
        ctx.close_path()

        # Fill the rounded rectangle with a linear gradient
        lg = cairo.LinearGradient(0.0, 0.0, 500.0, 500.0)
        lg.add_color_stop_rgba(0, 0.27, 0.27, 0.27, 1)  # 1,1,1 is white
        lg.add_color_stop_rgba(1, 0.22, 0.22, 0.22, 1)

        ctx.set_source(lg)
        ctx.fill()

        # Load a texture and overlay it to the gradient, tiled
        pattern = cairo.SurfacePattern(self.texture)
        pattern.set_extend(cairo.EXTEND_REPEAT)
        ctx.set_source(pattern)
        ctx.paint()

        if not text == '':
            ## Create the QR code
            qr_code = QRCode()  # output_size = int(min(width, height) * 0.9))

            # Note that we need to convert the 16-bit RGB values from
            # Gdk.Color to the 8-bit RGB values that PIL expects
            # pylint: disable=W0201
            self.surface = qr_code.encode(text,
                                current_color_fg=(
                                    self.current_color_fg.red / 256,
                                    self.current_color_fg.green / 256,
                                    self.current_color_fg.blue / 256),
                                previous_color_fg=(
                                    self.previous_color_fg.red / 256,
                                    self.previous_color_fg.green / 256,
                                    self.previous_color_fg.blue / 256),
                                current_color_bg=(
                                    self.current_color_bg.red / 256,
                                    self.current_color_bg.green / 256,
                                    self.current_color_bg.blue / 256),
                                previous_color_bg=(
                                    self.previous_color_bg.red / 256,
                                    self.previous_color_bg.green / 256,
                                    self.previous_color_bg.blue / 256),
                                output_type=QRCodeOutput.CAIRO_SURFACE)

            # Center the image in the drawing area
            centered_width, centered_height = \
                self.get_centered_coordinates(widget, self.surface)
            ctx.translate(centered_width, centered_height)

            # Set the surface as the context's source
            ctx.set_source_surface(self.surface)

            # Render the image
            ctx.paint()
