# -*- coding: utf-8 -*-
#
#  Copyright (C) 2015 by Igor E. Novikov
#
#  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 math
from copy import deepcopy

import wal
from colorbtn import PDColorButton
from colorctrls import SwatchCanvas
from patterns import PATTERN_PRESETS
from sk1 import _
from sk1.resources import icons, get_bmp
from uc2 import libgeom, libimg, sk2const
from uc2.utils import fsutils
from unitctrls import UnitSpin, StaticUnitLabel


class PatternPaletteSwatch(wal.VPanel, SwatchCanvas):
    callback = None
    pattern = None

    def __init__(self, parent, cms, pattern, size=(40, 20), onclick=None):
        self.color = None
        self.cms = cms
        wal.VPanel.__init__(self, parent)
        SwatchCanvas.__init__(self)
        self.pack(size)
        if onclick:
            self.callback = onclick
        self.set_pattern(pattern)

    def set_pattern(self, pattern):
        self.pattern = pattern
        self.fill = [0, sk2const.FILL_PATTERN,
                     [sk2const.PATTERN_IMG, pattern]]
        self.refresh()

    def mouse_left_up(self, point):
        if self.callback:
            self.callback(deepcopy(self.pattern))


class PatternMiniPalette(wal.VPanel):
    callback = None
    cells = []

    def __init__(self, parent, cms, onclick=None):
        wal.VPanel.__init__(self, parent)
        self.set_bg(wal.BLACK)
        grid = wal.GridPanel(parent, 2, 6, 1, 1)
        grid.set_bg(wal.BLACK)
        self.cells = []

        for item in range(12):
            self.cells.append(PatternPaletteSwatch(grid, cms,
                                                   PATTERN_PRESETS[item],
                                                   onclick=self.on_click))
            grid.pack(self.cells[-1])
        self.pack(grid, padding_all=1)
        if onclick:
            self.callback = onclick

    def on_click(self, pattern):
        if self.callback:
            self.callback(pattern)


class PatternSwatch(wal.VPanel, SwatchCanvas):
    callback = None
    pattern = None

    def __init__(self, parent, cms, pattern_def, size=(130, 130)):
        self.color = None
        self.cms = cms
        wal.VPanel.__init__(self, parent)
        SwatchCanvas.__init__(self, border='news')
        self.pack(size)
        self.set_pattern_def(pattern_def)

    def set_pattern_def(self, pattern_def):
        self.fill = [0, sk2const.FILL_PATTERN, pattern_def]
        self.refresh()


class PatternColorEditor(wal.HPanel):
    image_style = []
    callback = None

    def __init__(self, parent, dlg, cms, image_style, onchange=None):
        self.image_style = deepcopy(image_style)
        self.callback = onchange
        wal.HPanel.__init__(self, parent)

        self.pack(wal.Label(self, _('Fg:')))
        txt = _('Change pattern foreground color')
        self.fg_btn = PDColorButton(self, dlg, cms, self.image_style[0], txt,
                                    onchange=self.fg_changed)
        self.pack(self.fg_btn, padding=5)

        self.pack((10, 1))

        self.pack(wal.Label(self, _('Bg:')))
        txt = _('Change pattern background color')
        self.bg_btn = PDColorButton(self, dlg, cms, self.image_style[1], txt,
                                    onchange=self.bg_changed)
        self.pack(self.bg_btn, padding=5)

    def fg_changed(self, color):
        self.image_style[0] = deepcopy(color)
        if self.callback:
            self.callback(self.get_image_style())

    def bg_changed(self, color):
        self.image_style[1] = deepcopy(color)
        if self.callback:
            self.callback(self.get_image_style())

    def set_image_style(self, image_style):
        self.image_style = deepcopy(image_style)
        self.fg_btn.set_color(self.image_style[0])
        self.bg_btn.set_color(self.image_style[1])

    def get_image_style(self):
        return deepcopy(self.image_style)


class TransformEditor(wal.VPanel):
    app = None
    callback = None
    transforms = []

    def __init__(self, parent, app, trafo=[] + sk2const.NORMAL_TRAFO,
                 transforms=[] + sk2const.PATTERN_TRANSFORMS, onchange=None):
        self.app = app
        self.callback = onchange
        wal.VPanel.__init__(self, parent)

        grid = wal.GridPanel(self, 3, 7, 3, 2)

        # ---Origin X
        txt = _('Horizontal origin shift')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_ORIGIN_X, txt))
        self.origin_x = UnitSpin(app, grid, can_be_negative=True,
                                 onchange=self.changes, onenter=self.changes)
        grid.pack(self.origin_x)
        grid.pack(StaticUnitLabel(app, grid))

        grid.pack((10, 5))

        # ---Origin Y
        txt = _('Vertical origin shift')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_ORIGIN_Y, txt))
        self.origin_y = UnitSpin(app, grid, can_be_negative=True,
                                 onchange=self.changes, onenter=self.changes)
        grid.pack(self.origin_y)
        grid.pack(StaticUnitLabel(app, grid))

        # ---Scale X
        txt = _('Scale horizontally')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_SCALE_X, txt))
        self.scale_x = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                     step=1.0, onchange=self.changes,
                                     onenter=self.changes)
        grid.pack(self.scale_x)
        grid.pack(wal.Label(grid, '%'))

        grid.pack((10, 5))

        # ---Scale Y
        txt = _('Scale vertically')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_SCALE_Y, txt))
        self.scale_y = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                     step=1.0, onchange=self.changes,
                                     onenter=self.changes)
        grid.pack(self.scale_y)
        grid.pack(wal.Label(grid, '%'))

        # ---Shear X
        txt = _('Shear horizontally')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_SHEAR_X, txt))
        self.shear_x = wal.FloatSpin(grid, range_val=(0.0, 85.0),
                                     step=1.0, onchange=self.changes,
                                     onenter=self.changes)
        grid.pack(self.shear_x)
        grid.pack(wal.Label(grid, u'°'))

        grid.pack((10, 5))

        # ---Shear X
        txt = _('Shear vertically')
        grid.pack(get_bmp(grid, icons.PD_PATTERN_SHEAR_Y, txt))
        self.shear_y = wal.FloatSpin(grid, range_val=(0.0, 85.0),
                                     step=1.0, onchange=self.changes,
                                     onenter=self.changes)
        grid.pack(self.shear_y)
        grid.pack(wal.Label(grid, u'°'))

        self.pack(grid)

        # ---Rotate
        rot_panel = wal.HPanel(self)
        txt = _('Rotate pattern')
        rot_panel.pack(get_bmp(rot_panel, icons.PD_PATTERN_ROTATE, txt))
        self.rotate = wal.FloatSpin(rot_panel, range_val=(-360.0, 360.0),
                                    step=1.0, onchange=self.changes,
                                    onenter=self.changes)
        rot_panel.pack(self.rotate, padding=3)
        rot_panel.pack(wal.Label(rot_panel, u'°'))

        self.pack(rot_panel, padding=5)

        self.set_trafo(trafo, transforms)

    def set_trafo(self, trafo, transforms):
        x0, y0 = trafo[-2:]
        sx, sy, shx, shy, rotate = transforms
        self.origin_x.set_point_value(x0)
        self.origin_y.set_point_value(y0)
        self.scale_x.set_value(sx * 100.0)
        self.scale_y.set_value(sy * 100.0)
        self.shear_x.set_value(shx * 180.0 / math.pi)
        self.shear_y.set_value(shy * 180.0 / math.pi)
        self.rotate.set_value(rotate * 180.0 / math.pi)
        self.transforms = transforms

    def get_trafo(self):
        x0 = self.origin_x.get_point_value()
        y0 = self.origin_y.get_point_value()
        sx = self.scale_x.get_value() / 100.0
        sy = self.scale_y.get_value() / 100.0
        shx = self.shear_x.get_value()
        shy = self.shear_y.get_value()

        if shx + shy > 85:
            if shx == self.transforms[3]:
                shy = 85 - shx
            else:
                shx = 85 - shy

        shx = math.pi * shx / 180.0
        shy = math.pi * shy / 180.0

        angle = math.pi * self.rotate.get_value() / 180.0

        trafo = [sx, 0.0, 0.0, sy, x0, y0]
        if angle:
            trafo2 = [math.cos(angle), math.sin(angle),
                      - math.sin(angle), math.cos(angle), 0.0, 0.0]
            trafo = libgeom.multiply_trafo(trafo, trafo2)
        if shx or shy:
            trafo2 = [1.0, math.tan(shy), math.tan(shx), 1.0, 0.0, 0.0]
            trafo = libgeom.multiply_trafo(trafo, trafo2)

        self.transforms = [sx, sy, shx, shy, angle]
        return trafo, [sx, sy, shx, shy, angle]

    def changes(self):
        if self.callback:
            self.callback(*self.get_trafo())


class TrafoEditor(wal.VPanel):
    app = None
    callback = None

    def __init__(self, parent, app, onchange=None):
        self.app = app
        self.callback = onchange
        wal.VPanel.__init__(self, parent)

        self.pack(wal.Label(self, _('Transformation matrix:')), padding=5)

        grid = wal.GridPanel(self, 3, 5, 3, 1)

        # ---M11
        grid.pack(wal.Label(grid, 'm11:'))
        self.m11 = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                 step=0.01, onchange=self.changes,
                                 onenter=self.changes)
        grid.pack(self.m11)

        grid.pack((5, 5))

        # ---M12
        grid.pack(wal.Label(grid, 'm12:'))
        self.m12 = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                 step=0.01, onchange=self.changes,
                                 onenter=self.changes)
        grid.pack(self.m12)

        # ---M21
        grid.pack(wal.Label(grid, 'm21:'))
        self.m21 = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                 step=0.01, onchange=self.changes,
                                 onenter=self.changes)
        grid.pack(self.m21)

        grid.pack((5, 5))

        # ---M22
        grid.pack(wal.Label(grid, 'm22:'))
        self.m22 = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                 step=0.01, onchange=self.changes,
                                 onenter=self.changes)
        grid.pack(self.m22)

        # ---dx
        grid.pack(wal.Label(grid, 'dx:'))
        self.dx = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                step=0.01, onchange=self.changes,
                                onenter=self.changes)
        grid.pack(self.dx)

        grid.pack((5, 5))

        # ---dy
        grid.pack(wal.Label(grid, 'dy:'))
        self.dy = wal.FloatSpin(grid, range_val=(-1000000.0, 1000000.0),
                                step=0.01, onchange=self.changes,
                                onenter=self.changes)
        grid.pack(self.dy)

        self.pack(grid)

    def set_trafo(self, *args):
        trafo = args[0]
        self.m11.set_value(trafo[0])
        self.m12.set_value(trafo[1])
        self.m21.set_value(trafo[2])
        self.m22.set_value(trafo[3])
        self.dx.set_value(trafo[4])
        self.dy.set_value(trafo[5])

    def get_trafo(self):
        m11 = self.m11.get_value()
        m12 = self.m12.get_value()
        m21 = self.m21.get_value()
        m22 = self.m22.get_value()
        dx = self.dx.get_value()
        dy = self.dy.get_value()
        return [m11, m12, m21, m22, dx, dy], []

    def changes(self):
        if self.callback:
            self.callback(*self.get_trafo())


class PatternTrafoEditor(wal.VPanel):
    app = None
    callback = None
    transforms = []
    active_panel = None

    def __init__(self, parent, app, onchange=None):
        wal.VPanel.__init__(self, parent)
        self.transfom_editor = TransformEditor(self, app,
                                               onchange=onchange)
        self.trafo_editor = TrafoEditor(self, app,
                                        onchange=onchange)
        self.transfom_editor.set_visible(False)
        self.pack(self.trafo_editor)
        self.active_panel = self.trafo_editor

    def set_trafo(self, trafo, transforms):
        if not transforms:
            m11, m12, m21, m22 = trafo[:4]
            if not m12 and not m21:
                transforms = [m11, m22, 0.0, 0.0, 0.0]
        if transforms:
            if not self.active_panel == self.transfom_editor:
                self.remove(self.active_panel)
                self.active_panel.set_visible(False)
                self.active_panel = self.transfom_editor
                self.pack(self.transfom_editor)
                self.active_panel.set_visible(True)
        else:
            if not self.active_panel == self.trafo_editor:
                self.remove(self.active_panel)
                self.active_panel.set_visible(False)
                self.active_panel = self.trafo_editor
                self.pack(self.active_panel)
                self.active_panel.set_visible(True)
        self.active_panel.set_trafo(trafo, transforms)


class PatternEditor(wal.HPanel):
    pattern_def = []
    cms = None
    dlg = None
    callback = None

    def __init__(self, parent, dlg, cms, pattern_def, onchange=None):
        self.dlg = dlg
        self.app = dlg.app
        self.cms = cms
        self.pattern_def = deepcopy(pattern_def)
        self.callback = onchange
        wal.HPanel.__init__(self, parent)
        left_panel = wal.VPanel(self)
        self.pattern_swatch = PatternSwatch(left_panel, self.cms, pattern_def)
        left_panel.pack(self.pattern_swatch)

        button_panel = wal.HPanel(left_panel)
        txt = _('Load pattern from file')
        button_panel.pack(wal.ImageButton(self, icons.PD_OPEN, wal.SIZE_16,
                                          tooltip=txt, flat=False,
                                          onclick=self.load_pattern), padding=1)
        txt = _('Save pattern into file')
        button_panel.pack(wal.ImageButton(self, icons.PD_FILE_SAVE, wal.SIZE_16,
                                          tooltip=txt, flat=False,
                                          onclick=self.save_pattern), padding=1)
        left_panel.pack(button_panel, padding=2)

        self.pack(left_panel, fill=True)

        right_panel = wal.VPanel(self)

        pce = PatternColorEditor(right_panel, dlg, cms, pattern_def[2],
                                 onchange=self.color_changed)
        self.pattern_color_editor = pce
        right_panel.pack(self.pattern_color_editor, padding=5)

        self.trafo_editor = PatternTrafoEditor(right_panel, dlg.app,
                                               onchange=self.trafo_changed)
        right_panel.pack(self.trafo_editor, padding=5)

        self.pack(right_panel, fill=True, expand=True)

    def color_changed(self, image_style):
        self.pattern_def[2] = image_style
        if self.callback:
            self.callback(self.get_pattern_def())

    def trafo_changed(self, trafo, transforms=None):
        transforms = transforms or []
        self.pattern_def[3] = trafo
        self.pattern_def[4] = transforms
        if self.callback:
            self.callback(self.get_pattern_def())

    def set_pattern_def(self, pattern_def):
        self.pattern_def = deepcopy(pattern_def)
        self.update()

    def get_pattern_def(self):
        return deepcopy(self.pattern_def)

    def update(self):
        self.pattern_swatch.set_pattern_def(self.pattern_def)
        self.pattern_color_editor.set_image_style(self.pattern_def[2])
        self.trafo_editor.set_trafo(self.pattern_def[3], self.pattern_def[4])
        self.pattern_color_editor.set_visible(
            self.pattern_def[0] == sk2const.PATTERN_IMG)

    def load_pattern(self):
        img_file = self.app.import_pattern(self.dlg)
        if img_file:
            fobj = fsutils.get_fileptr(img_file)
            pattern, flag = libimg.read_pattern(fobj.read())
            pattern_type = sk2const.PATTERN_TRUECOLOR
            if flag:
                pattern_type = sk2const.PATTERN_IMG
                if flag == 'EPS':
                    pattern_type = sk2const.PATTERN_EPS
            self.pattern_def[0] = pattern_type
            self.pattern_def[1] = pattern
            if self.callback:
                self.callback(self.get_pattern_def())

    def save_pattern(self):
        self.app.extract_pattern(self.dlg, self.pattern_def[1],
                                 self.pattern_def[0] == sk2const.PATTERN_EPS)
