#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
# ---------------------------------------------------------------------------
#
# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
# Copyright (C) 2003 Mt. Hood Playing Card Co.
# Copyright (C) 2005-2009 Skomoroh
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# ---------------------------------------------------------------------------

import tkinter

from pysollib.mfxutil import Image, ImageTk
from pysollib.ui.tktile.Canvas2 import CanvasText, Group, Line, Rectangle
from pysollib.ui.tktile.Canvas2 import ImageItem as ImageItem2
from pysollib.ui.tktile.tkutil import loadImage, unbind_destroy


# ************************************************************************
# * canvas items
# ************************************************************************

class MfxCanvasGroup(Group):
    def __init__(self, canvas, tag=None):
        Group.__init__(self, canvas=canvas, tag=tag)
        # register ourself so that we can unbind from the canvas
        assert self.id not in self.canvas.items
        self.canvas.items[self.id] = self

    def addtag(self, tag, option="withtag"):
        self.canvas.addtag(tag, option, self.id)

    def delete(self):
        del self.canvas.items[self.id]
        Group.delete(self)

    def gettags(self):
        return self.canvas.tk.splitlist(self._do("gettags"))


class MfxCanvasImage(ImageItem2):
    def __init__(self, canvas, x, y, **kwargs):
        self.init_coord = x, y
        group = None
        if 'group' in kwargs:
            group = kwargs['group']
            del kwargs['group']
        if 'image' in kwargs:
            self._image = kwargs['image']
        ImageItem2.__init__(self, canvas, x, y, **kwargs)
        if group:
            self.addtag(group)

    def moveTo(self, x, y):
        c = self.coords()
        self.move(x - int(c[0]), y - int(c[1]))

    def show(self):
        self.config(state='normal')

    def hide(self):
        self.config(state='hidden')


MfxCanvasLine = Line


class MfxCanvasRectangle(Rectangle):
    def __init__(self, canvas, *args, **kwargs):
        group = None
        if 'group' in kwargs:
            group = kwargs['group']
            del kwargs['group']
        Rectangle.__init__(self, canvas, *args, **kwargs)
        if group:
            self.addtag(group)


class MfxCanvasText(CanvasText):
    def __init__(self, canvas, x, y, preview=-1, **kwargs):
        self.init_coord = x, y
        self.x, self.y = x, y
        if preview < 0:
            preview = canvas.preview
        if preview > 1:
            return
        if "fill" not in kwargs:
            kwargs["fill"] = canvas._text_color
        group = None
        if 'group' in kwargs:
            group = kwargs['group']
            del kwargs['group']
        CanvasText.__init__(self, canvas, x, y, **kwargs)
        self.text_format = None
        canvas._text_items.append(self)
        if group:
            self.addtag(group)

    def moveTo(self, x, y):
        dx, dy = x - self.x, y - self.y
        self.x, self.y = x, y
        self.move(dx, dy)


# ************************************************************************
# * canvas
# ************************************************************************

class MfxCanvas(tkinter.Canvas):
    def __init__(self, *args, **kw):
        tkinter.Canvas.__init__(self, *args, **kw)
        self.preview = 0
        self.busy = False
        # this is also used by lib-tk/Canvas.py
        self.items = {}
        # private
        self.__tileimage = None
        self.__tiles = []           # id of canvas items
        self.__topimage = None
        self.__tops = []            # id of canvas items
        # friend MfxCanvasText
        self._text_color = "#000000"
        self._stretch_bg_image = 0
        self._save_aspect_bg_image = 0
        self._text_items = []
        #
        self.xmargin, self.ymargin = 10, 10
        # resize bg image
        self.bind('<Configure>', self.setBackgroundImage)

    def setBackgroundImage(self, event=None):
        # print 'setBackgroundImage', self._bg_img
        if not hasattr(self, '_bg_img'):
            return
        if not self._bg_img:  # solid color
            return
        stretch = self._stretch_bg_image
        save_aspect = self._save_aspect_bg_image
        if Image:
            if stretch:
                w, h = self._geometry()
                if save_aspect:
                    w0, h0 = self._bg_img.size
                    a = min(float(w0)/w, float(h0)/h)
                    w0, h0 = int(w0/a), int(h0/a)
                    im = self._bg_img.resize((w0, h0))

                    # Center the background image.
                    cpw = (w0 - w) / 2
                    cph = (h0 - h) / 2

                    im = im.crop([max(0, cpw), max(0, cph), w0, h0])
                else:
                    im = self._bg_img.resize((w, h))
                image = ImageTk.PhotoImage(im)
            else:
                image = ImageTk.PhotoImage(self._bg_img)
        else:  # not Image
            stretch = 0
            image = self._bg_img
        for id in self.__tiles:
            self.delete(id)
        self.__tiles = []
        # must keep a reference to the image, otherwise Python will
        # garbage collect it...
        self.__tileimage = image
        if stretch:
            #
            if self.preview:
                dx, dy = 0, 0
            else:
                dx, dy = -self.xmargin, -self.ymargin
            id = self._x_create("image", dx, dy, image=image, anchor="nw")
            self.tag_lower(id)          # also see tag_lower above
            self.__tiles.append(id)
        else:
            iw, ih = image.width(), image.height()
            sw, sh = self._geometry()
            for x in range(-self.xmargin, sw, iw):
                for y in range(-self.ymargin, sh, ih):
                    id = self._x_create(
                        "image", x, y, image=image, anchor="nw")
                    self.tag_lower(id)          # also see tag_lower above
                    self.__tiles.append(id)
        return 1

    def _geometry(self):
        w = self.winfo_width()
        if w == 1:
            w = int(self.cget('width'))
        h = self.winfo_height()
        if h == 1:
            h = int(self.cget('height'))
        scrollregion = self.cget('scrollregion')
        if not scrollregion:
            return w, h
        x, y, sw, sh = [int(i) for i in scrollregion.split()]
        sw -= x
        sh -= y
        w = max(w, sw)
        h = max(h, sh)
        return w, h

    #
    # top-image support
    #

    def _x_create(self, itemType, *args, **kw):
        return tkinter.Canvas._create(self, itemType, args, kw)

    def _create(self, itemType, args, kw):
        # print "_create:", itemType, args, kw
        id = tkinter.Canvas._create(self, itemType, args, kw)
        if self.__tops:
            self.tk.call(self._w, "lower", id, self.__tops[0])
        return id

    def tag_raise(self, id, aboveThis=None):
        # print "tag_raise:", id, aboveThis
        if aboveThis is None and self.__tops:
            self.tk.call(self._w, "lower", id, self.__tops[0])
        else:
            self.tk.call(self._w, "raise", id, aboveThis)

    def tag_lower(self, id, belowThis=None):
        # print "tag_lower:", id, belowThis
        if belowThis is None and self.__tiles:
            self.tk.call(self._w, "raise", id, self.__tiles[-1])
        else:
            self.tk.call(self._w, "lower", id, belowThis)

    def setInitialSize(self, width, height, margins=True, scrollregion=True):
        # print 'Canvas.setInitialSize:', width, height, scrollregion
        if self.preview:
            self.config(width=width, height=height,
                        scrollregion=(0, 0, width, height))
        else:
            # add margins
            dx, dy = self.xmargin, self.ymargin
            if margins:
                w, h = dx+width+dx, dy+height+dy
            else:
                w, h = width, height
            self.config(width=w, height=h)
            if scrollregion:
                self.config(scrollregion=(-dx, -dy, width+dx, height+dy))
            else:
                # no scrolls
                self.config(scrollregion=(-dx, -dy, dx, dy))

    # delete all CanvasItems, but keep the background and top tiles
    def deleteAllItems(self):
        self._text_items = []
        for id in list(self.items.keys()):
            assert id not in self.__tiles   # because the tile is created by id
            unbind_destroy(self.items[id])
            self.items[id].delete()
        assert self.items == {}

    def findCard(self, stack, event):
        if isinstance(stack.cards[0].item, Group):
            current = self.gettags("current")           # get tags
            for i in range(len(stack.cards)):
                if stack.cards[i].item.tag in current:
                    return i
        else:
            #  current = self.find("withtag", "current")   # get item ids
            #  for i in range(len(stack.cards)):
            #      if stack.cards[i].item.id in current:
            #          return i
            if self.preview:
                dx, dy = 0, 0
            else:
                dx, dy = -self.xmargin, -self.ymargin
            x = event.x+dx+self.xview()[0]*int(self.cget('width'))
            y = event.y+dy+self.yview()[0]*int(self.cget('height'))
            # x, y = event.x, event.y
            items = list(self.find_overlapping(x, y, x, y))
            items.reverse()
            for item in items:
                for i in range(len(stack.cards)):
                    if stack.cards[i].item.id == item:
                        return i
        return -1

    def setTextColor(self, color):
        if color is None:
            c = self.cget("bg")
            if not isinstance(c, str) or c[0] != "#" or len(c) != 7:
                return
            v = []
            for i in (1, 3, 5):
                v.append(int(c[i:i+2], 16))
            luminance = (0.212671 * v[0] + 0.715160 * v[1] + 0.072169 * v[2]) \
                / 255
            # print c, ":", v, "luminance", luminance
            color = ("#000000", "#ffffff")[luminance < 0.3]
        if self._text_color != color:
            self._text_color = color
            for item in self._text_items:
                item.config(fill=self._text_color)

    def setTile(self, image, stretch=0, save_aspect=0):
        # print 'setTile:', image, stretch
        if image:
            if Image:
                try:
                    self._bg_img = Image.open(image)
                except Exception:
                    return 0
            else:
                try:
                    self._bg_img = loadImage(file=image, dither=1)
                except Exception:
                    return 0
            self._stretch_bg_image = stretch
            self._save_aspect_bg_image = save_aspect
            self.setBackgroundImage()
        else:
            for id in self.__tiles:
                self.delete(id)
            self.__tiles = []
            self._bg_img = None
        return 1

    def setTopImage(self, image, cw=0, ch=0):
        try:
            if image and isinstance(image, str):
                image = loadImage(file=image)
        except tkinter.TclError:
            return 0
        if len(self.__tops) == 1 and image is self.__tops[0]:
            return 1
        for id in self.__tops:
            self.delete(id)
        self.__tops = []
        # must keep a reference to the image, otherwise Python will
        # garbage collect it...
        self.__topimage = image
        if image is None:
            return 1
        iw, ih = image.width(), image.height()
        if cw <= 0:
            # cw = max(int(self.cget("width")), self.winfo_width())
            cw = self.winfo_width()
        if ch <= 0:
            # ch = max(int(self.cget("height")),  self.winfo_height())
            ch = self.winfo_height()
        # print iw, ih, cw, ch
        x = (cw-iw)//2-self.xmargin+self.xview()[0]*int(self.cget('width'))
        y = (ch-ih)//2-self.ymargin+self.yview()[0]*int(self.cget('height'))
        id = self._x_create("image", x, y, image=image, anchor="nw")
        self.tk.call(self._w, "raise", id)
        self.__tops.append(id)
        return 1

    #
    # Pause support
    #

    def hideAllItems(self):
        for item in list(self.items.values()):
            item.config(state='hidden')

    def showAllItems(self):
        for item in list(self.items.values()):
            item.config(state='normal')

    #
    # restricted but fast _bind and _substitute
    #

    def _bind(self, what, sequence, func, add, needcleanup=1):
        funcid = self._register(func, self._substitute, needcleanup)
        cmd = ('%sif {"[%s %s]" == "break"} break\n' %
               (add and '+' or '', funcid, "%x %y"))
        self.tk.call(what + (sequence, cmd))
        return funcid

    def _substitute(self, *args):
        e = tkinter.Event()
        try:
            # Tk changed behavior in 8.4.2, returning "??" rather more often.
            e.x = int(args[0])
        except ValueError:
            e.x = args[0]
        try:
            e.y = int(args[1])
        except ValueError:
            e.y = args[1]
        return (e,)
