# -*- 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 cairo
import os
from base64 import b64decode
from copy import deepcopy

from uc2.cms import get_registration_black, verbose_color, val_255_to_dec

import wal
from palette_viewer import PaletteViewer
from sk1 import _, config, events
from sk1.resources import icons, get_icon
from uc2 import uc2const, cms, libimg, sk2const, libgeom
from uc2.formats.sk2 import sk2_model, sk2_config

FILL_MODES = [sk2const.FILL_ANY, sk2const.FILL_CLOSED_ONLY]
RULE_MODES = [sk2const.FILL_EVENODD, sk2const.FILL_NONZERO]

FILL_MODE_ICONS = {
    sk2const.FILL_ANY: icons.PD_FILL_ANY,
    sk2const.FILL_CLOSED_ONLY: icons.PD_FILL_CLOSED_ONLY,
}
RULE_MODE_ICONS = {
    sk2const.FILL_EVENODD: icons.PD_EVENODD,
    sk2const.FILL_NONZERO: icons.PD_NONZERO,
}

FILL_MODE_NAMES = {
    sk2const.FILL_ANY: _('Fill any contour'),
    sk2const.FILL_CLOSED_ONLY: _('Fill closed only'),
}
RULE_MODE_NAMES = {
    sk2const.FILL_EVENODD: _('Evenodd fill'),
    sk2const.FILL_NONZERO: _('Nonzero fill'),
}


class FillRuleKeeper(wal.HPanel):
    mode = 0
    fill_keeper = None
    rule_keeper = None

    def __init__(self, parent, mode=sk2const.FILL_EVENODD):
        self.mode = mode
        wal.HPanel.__init__(self, parent)
        self.fill_keeper = wal.HToggleKeeper(self, FILL_MODES,
                                             FILL_MODE_ICONS, FILL_MODE_NAMES)
        self.pack(self.fill_keeper)
        self.rule_keeper = wal.HToggleKeeper(self, RULE_MODES,
                                             RULE_MODE_ICONS, RULE_MODE_NAMES)
        self.pack(self.rule_keeper)
        self.set_mode(self.mode)

    def set_mode(self, mode):
        self.fill_keeper.set_mode(mode & sk2const.FILL_CLOSED_ONLY)
        self.rule_keeper.set_mode(mode & sk2const.FILL_EVENODD)

    def get_mode(self):
        return self.rule_keeper.mode | self.fill_keeper.mode

    def set_enable(self, val):
        self.fill_keeper.set_enable(val)
        self.rule_keeper.set_enable(val)


CMYK_PALETTE = [
    [uc2const.COLOR_CMYK, [1.0, 1.0, 1.0, 1.0], 1.0, 'CMYK Registration'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 1.0], 1.0, 'Black'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 0.7], 1.0, '70% Black'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 0.5], 1.0, '50% Black'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 0.2], 1.0, '20% Black'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 0.0], 1.0, 'White'],
    [uc2const.COLOR_CMYK, [1.0, 0.0, 0.0, 0.0], 1.0, 'Cyan'],
    [uc2const.COLOR_CMYK, [0.0, 1.0, 0.0, 0.0], 1.0, 'Magenta'],
    [uc2const.COLOR_CMYK, [0.0, 0.0, 1.0, 0.0], 1.0, 'Yellow'],
    [uc2const.COLOR_CMYK, [0.0, 1.0, 1.0, 0.0], 1.0, 'Red'],
    [uc2const.COLOR_CMYK, [1.0, 0.0, 1.0, 0.0], 1.0, 'Green'],
    [uc2const.COLOR_CMYK, [1.0, 1.0, 0.0, 0.0], 1.0, 'Blue'], ]

RGB_PALETTE = [
    [uc2const.COLOR_RGB, [0.0, 0.0, 0.0], 1.0, 'Black'],
    [uc2const.COLOR_RGB, [0.2, 0.2, 0.2], 1.0, '80% Black'],
    [uc2const.COLOR_RGB, [0.4, 0.4, 0.4], 1.0, '60% Black'],
    [uc2const.COLOR_RGB, [0.6, 0.6, 0.6], 1.0, '40% Black'],
    [uc2const.COLOR_RGB, [0.8, 0.8, 0.8], 1.0, '20% Black'],
    [uc2const.COLOR_RGB, [1.0, 1.0, 1.0], 1.0, 'White'],
    [uc2const.COLOR_RGB, [1.0, 0.0, 0.0], 1.0, 'Red'],
    [uc2const.COLOR_RGB, [0.0, 1.0, 0.0], 1.0, 'Green'],
    [uc2const.COLOR_RGB, [0.0, 0.0, 1.0], 1.0, 'Blue'],
    [uc2const.COLOR_RGB, [0.0, 1.0, 1.0], 1.0, 'Cyan'],
    [uc2const.COLOR_RGB, [1.0, 0.0, 1.0], 1.0, 'Magenta'],
    [uc2const.COLOR_RGB, [1.0, 1.0, 0.0], 1.0, 'Yellow'], ]

GRAY_PALETTE = [
    [uc2const.COLOR_GRAY, [0.0, ], 1.0, 'Black'],
    [uc2const.COLOR_GRAY, [0.1, ], 1.0, '90% Black'],
    [uc2const.COLOR_GRAY, [0.2, ], 1.0, '80% Black'],
    [uc2const.COLOR_GRAY, [0.3, ], 1.0, '70% Black'],
    [uc2const.COLOR_GRAY, [0.4, ], 1.0, '60% Black'],
    [uc2const.COLOR_GRAY, [0.5, ], 1.0, '50% Black'],
    [uc2const.COLOR_GRAY, [0.6, ], 1.0, '40% Black'],
    [uc2const.COLOR_GRAY, [0.7, ], 1.0, '30% Black'],
    [uc2const.COLOR_GRAY, [0.8, ], 1.0, '20% Black'],
    [uc2const.COLOR_GRAY, [0.9, ], 1.0, '10% Black'],
    [uc2const.COLOR_GRAY, [0.95, ], 1.0, '5% Black'],
    [uc2const.COLOR_GRAY, [1.0, ], 1.0, 'White'],
]

SPOT_PALETTE = [
    cms.get_registration_black(),
    [uc2const.COLOR_SPOT, [[0.7608, 0.6039, 0.2196], []], 1.0, 'Rich Gold'],
    [uc2const.COLOR_SPOT, [[0.6745, 0.6, 0.3804], []], 1.0, 'Metallic Gold'],
    [uc2const.COLOR_SPOT, [[0.8471, 0.6118, 0.4667], []], 1.0, 'Bronze'],
    [uc2const.COLOR_SPOT, [[0.7098, 0.7098, 0.7098], []], 1.0, 'Silver'],
    [uc2const.COLOR_SPOT, [[1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], 1.0,
     'Opaque White'],
    [uc2const.COLOR_SPOT, [[1.0, 0.0, 0.0], []], 1.0, 'Fluorescent Red'],
    [uc2const.COLOR_SPOT, [[1.0, 0.5922, 0.0118], []], 1.0,
     'Fluorescent Orange'],
    [uc2const.COLOR_SPOT, [[0.9216, 1.0, 0.0], []], 1.0, 'Fluorescent Yellow'],
    [uc2const.COLOR_SPOT, [[0.0, 1.0, 0.0157], []], 1.0, 'Fluorescent Green'],
    [uc2const.COLOR_SPOT, [[0.0549, 0.5647, 1.0], []], 1.0, 'Fluorescent Blue'],
    [uc2const.COLOR_SPOT, [[1.0, 0.0039, 0.498], []], 1.0,
     'Fluorescent Magenta'],
]

REG_COLOR = [1.0, 1.0, 1.0, 1.0]
REG_SPOT_COLOR = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]]


class HexField(wal.Entry):
    color = None
    callback = None
    hexcolor = ''

    def __init__(self, parent, onchange=None):
        wal.Entry.__init__(self, parent, width=8, onchange=self.on_change,
                           onenter=self.on_enter)
        if onchange:
            self.callback = onchange

    def set_color(self, color):
        self.color = color
        if color[2] == 1.0:
            self.set_value(cms.rgb_to_hexcolor(color[1]))
        else:
            self.set_value(cms.rgba_to_hexcolor(color[1] + [color[2], ]))
        self.hexcolor = self.get_value()

    def get_color(self):
        return self.hexcolor

    def _check_input(self):
        ret = ''
        val = self.get_value()
        if val:
            if val[0] == '#':
                seq = val[1:].lower()
            else:
                seq = val.lower()
            ret += '#'
            for item in seq:
                if item in '0123456789abcdef':
                    ret += item
        if not ret == val:
            self.set_value(ret)

    def on_enter(self):
        val = self.get_value()
        if len(val) in (4, 7, 9):
            if len(val) == 4:
                self.hexcolor = '#' + val[1] * 2 + val[2] * 2 + val[3] * 2
                self.set_value(self.hexcolor)
            if self.callback:
                self.callback()

    def on_change(self):
        self._check_input()
        val = self.get_value()
        if len(val) in (7, 9):
            self.hexcolor = val
            if self.callback:
                self.callback()


class SwatchCanvas(wal.SensitiveCanvas):
    fill = None
    color = None
    cms = None
    border = ''
    even_odd = False
    reg_icon = None
    pattern_size = 10

    def __init__(self, border='', even_odd=False):
        self.border = border
        self.even_odd = even_odd
        wal.SensitiveCanvas.__init__(self)
        self.set_double_buffered()

    def get_cairo_color(self, color):
        r, g, b = self.cms.get_display_color(color)
        return r, g, b, color[2]

    def paint(self):
        self.draw_background()
        if self.color is not None:
            self.draw_color()
        elif self.fill:
            if self.fill[1] == sk2const.FILL_GRADIENT:
                self.draw_gradient()
            elif self.fill[1] == sk2const.FILL_PATTERN:
                self.draw_pattern()
        self.draw_border()

    def draw_background(self):
        if self.color and self.color[2] == 1.0:
            return
        w, h = self.get_size()
        x1 = y1 = 0
        flag_y = self.even_odd
        self.set_stroke()
        while y1 < h:
            flag_x = flag_y
            while x1 < w:
                clr = wal.WHITE
                if not flag_x:
                    clr = wal.LIGHT_GRAY
                self.set_fill(clr)
                self.draw_rect(x1, y1, self.pattern_size, self.pattern_size)
                flag_x = not flag_x
                x1 += self.pattern_size
            flag_y = not flag_y
            y1 += self.pattern_size
            x1 = 0

    def draw_color(self):
        if self.color is None:
            return
        if not self.color:
            self.draw_empty_pattern()
            return
        w, h = self.get_size()
        if self.color[2] < 1.0:
            self.set_gc_stroke()
            self.set_gc_fill(self.cms.get_rgba_color255(self.color))
            self.gc_draw_rect(0, 0, w, h)
        else:
            self.set_stroke()
            self.set_fill(self.cms.get_rgb_color255(self.color))
            self.draw_rect(0, 0, w, h)
        if self.color[1] in (REG_COLOR, REG_SPOT_COLOR):
            if not self.reg_icon:
                self.reg_icon = get_icon(icons.REG_SIGN, size=wal.DEF_SIZE)
            x = (w - 19) / 2
            y = (h - 19) / 2
            self.draw_bitmap(self.reg_icon, x, y)

    def draw_cairo_background(self, ctx):
        w, h = self.get_size()
        x1 = y1 = 0
        flag_y = self.even_odd
        while y1 < h:
            flag_x = flag_y
            while x1 < w:
                clr = wal.WHITE
                if not flag_x:
                    clr = wal.LIGHT_GRAY
                ctx.set_source_rgb(*val_255_to_dec(clr.Get()))
                ctx.rectangle(x1, y1, self.pattern_size, self.pattern_size)
                ctx.fill()
                flag_x = not flag_x
                x1 += self.pattern_size
            flag_y = not flag_y
            y1 += self.pattern_size
            x1 = 0

    def draw_gradient(self):
        w, h = self.get_size()
        gradient = self.fill[2]
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
        ctx = cairo.Context(surface)
        self.draw_cairo_background(ctx)
        if gradient[0] == sk2const.GRADIENT_LINEAR:
            grd = cairo.LinearGradient(0.0, h / 2.0, w, h / 2.0)
        else:
            grd = cairo.RadialGradient(w / 2.0, h / 2.0, 0,
                                       w / 2.0, h / 2.0, w / 2.0)
        for stop in gradient[2]:
            grd.add_color_stop_rgba(stop[0], *self.get_cairo_color(stop[1]))
        ctx.set_source(grd)
        ctx.rectangle(0, 0, w, h)
        ctx.fill()
        self.gc_draw_bitmap(wal.copy_surface_to_bitmap(surface), 0, 0)

    def draw_pattern(self):
        w, h = self.get_size()
        pattern = self.fill[2]
        surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
        ctx = cairo.Context(surface)
        self.draw_cairo_background(ctx)
        bmpstr = b64decode(pattern[1])
        if self.cms.app.current_doc:
            cfg = self.cms.app.current_doc.model.config
        else:
            cfg = sk2_config.SK2_Config()
            config_file = os.path.join(self.cms.app.appdata.app_config_dir,
                                       'sk2_config.xml')
            cfg.load(config_file)
        image_obj = sk2_model.Pixmap(cfg)
        libimg.set_image_data(self.cms, image_obj, bmpstr)
        libimg.flip_left_to_right(image_obj)
        if pattern[0] == sk2const.PATTERN_IMG and len(pattern) > 2:
            image_obj.style[3] = deepcopy(pattern[2])
        libimg.update_image(self.cms, image_obj)
        sp = cairo.SurfacePattern(image_obj.cache_cdata)
        sp.set_extend(cairo.EXTEND_REPEAT)
        trafo = [-1.0, 0.0, 0.0, 1.0, 0.0, 0.0]
        if len(pattern) > 3:
            trafo = libgeom.multiply_trafo(pattern[3], trafo)
        pattern_matrix = cairo.Matrix(*trafo)
        pattern_matrix.invert()
        sp.set_matrix(pattern_matrix)
        ctx.set_source(sp)
        ctx.rectangle(0, 0, w, h)
        ctx.fill()
        image_obj.cache_cdata = None
        self.gc_draw_bitmap(wal.copy_surface_to_bitmap(surface), 0, 0)

    def draw_empty_pattern(self):
        w, h = self.get_size()
        self.set_stroke()
        self.set_fill(wal.WHITE)
        self.draw_rect(0, 0, w, h)
        self.set_gc_stroke(wal.RED)
        self.set_gc_fill()
        self.gc_draw_line(0, 0, w, h)
        self.gc_draw_line(w - 1, 0, 0, h - 1)
        self.set_stroke(wal.BLACK)
        self.set_fill()
        self.draw_rect(0, 0, w, h)

    def draw_border(self):
        if not self.border:
            return
        x1, y1 = self.get_size()
        y0 = -1 if 'n' not in self.border else 0
        x0 = -1 if 'w' not in self.border else 0
        x1 = x1 + 1 if 'e' not in self.border else x1
        y1 = y1 + 1 if 's' not in self.border else y1
        self.set_stroke(wal.BLACK)
        self.set_fill()
        self.draw_rect(x0, y0, x1 - x0, y1 - y0)


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

    def __init__(self, parent, cms_ref, color, size=(20, 20), onclick=None):
        self.color = color
        self.cms = cms_ref
        wal.VPanel.__init__(self, parent)
        SwatchCanvas.__init__(self)
        self.pack(size)
        if self.color and self.color[3]:
            self.set_tooltip(self.color[3])
        if onclick:
            self.callback = onclick

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


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

    def __init__(self, parent, cms_ref, color, size=(20, 20), border='wes',
                 even_odd=False, onclick=None):
        self.color = color
        self.cms = cms_ref
        wal.VPanel.__init__(self, parent)
        SwatchCanvas.__init__(self, border, even_odd)
        self.pack(size)
        if onclick:
            self.callback = onclick

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

    def set_color(self, color):
        self.color = color
        tooltip = ''
        if self.color and self.color[3]:
            tooltip = self.color[3]
        self.set_tooltip(tooltip)
        self.refresh()


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

    def __init__(self, parent, cms_ref, fill, size=(20, 20), border='new',
                 even_odd=True, onclick=None):
        self.cms = cms_ref
        wal.VPanel.__init__(self, parent)
        SwatchCanvas.__init__(self, border, even_odd)
        self.pack(size)
        self.set_swatch_fill(fill)
        if onclick:
            self.callback = onclick

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

    def set_swatch_fill(self, fill):
        if not fill:
            self.color = []
            self.fill = None
        elif fill[1] == sk2const.FILL_SOLID:
            self.color = fill[2]
            self.fill = None
        elif fill[1] in [sk2const.FILL_GRADIENT, sk2const.FILL_PATTERN]:
            self.color = None
            self.fill = fill
        tooltip = ''
        if self.color and self.color[3]:
            tooltip = self.color[3]
        self.set_tooltip(tooltip)
        self.refresh()


class SbStrokeSwatch(AlphaColorSwatch):
    pattern_size = 8

    def __init__(self, parent, app, label, color=None, size=(35, 16),
                 onclick=None):
        color = color or []
        self.app = app
        self.label = label
        AlphaColorSwatch.__init__(self, parent, self.app.default_cms,
                                  color, size, 'news', onclick=onclick)

    def update_from_obj(self, obj):
        text = _('Stroke:')
        self.cms = self.app.current_doc.cms
        if self.app.insp.is_obj_pixmap(obj):
            self.set_color(obj.style[3][1])
            text = _('Bg:')
        else:
            stroke = obj.style[1]
            if stroke:
                point_val = stroke[1]
                self.set_color(stroke[2])
                unit = self.app.current_doc.model.doc_units
                val = str(round(point_val * uc2const.point_dict[unit], 3))
                text += ' %s ' % val
                text += uc2const.unit_short_names[unit]
            else:
                self.set_color([])
                text += ' ' + _('None')
        self.label.set_text(text)


class SbFillSwatch(FillSwatch):
    pattern_size = 8

    def __init__(self, parent, app, label, fill=None, size=(35, 16),
                 onclick=None):
        fill = fill or []
        self.app = app
        self.label = label
        FillSwatch.__init__(self, parent, self.app.default_cms, fill, size,
                            'news', onclick=onclick)

    def update_from_obj(self, obj):
        text = _('Fill:')
        self.cms = self.app.current_doc.cms
        if self.app.insp.is_obj_pixmap(obj):
            text = _('Fg:')
            self.set_swatch_fill([sk2const.FILL_EVENODD, sk2const.FILL_SOLID,
                                  obj.style[3][0]])
        else:
            fill = obj.style[0]
            self.set_swatch_fill(fill)
            if fill:
                if fill[1] == sk2const.FILL_SOLID:
                    text += ' ' + cms.verbose_color(fill[2])
            else:
                text += ' ' + _('None')

        self.label.set_text(text)


class MiniPalette(wal.VPanel):
    callback = None

    def __init__(self, parent, cms_ref, palette=CMYK_PALETTE, 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)
        for item in palette:
            grid.pack(PaletteSwatch(grid, cms_ref, item,
                                    (40, 20), self.on_click))
        self.pack(grid, padding_all=1)
        if onclick:
            self.callback = onclick

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


class ColorColorRefPanel(wal.VPanel):
    def __init__(self, parent, cms_ref, orig_color, new_color, on_orig=None):
        wal.VPanel.__init__(self, parent)
        grid = wal.GridPanel(self, hgap=5)
        grid.pack(wal.Label(grid, _('Old color:')))

        self.before_swatch = AlphaColorSwatch(grid, cms_ref, orig_color,
                                              (70, 30), 'new', onclick=on_orig)
        grid.pack(self.before_swatch)

        grid.pack(wal.Label(grid, _('New color:')))

        self.after_swatch = AlphaColorSwatch(grid, cms_ref, new_color,
                                             (70, 30), even_odd=True)
        grid.pack(self.after_swatch)

        self.pack(grid, padding_all=2)

    def update(self, orig_color, new_color):
        self.before_swatch.set_color(orig_color)
        self.after_swatch.set_color(new_color)


class FillColorRefPanel(wal.VPanel):
    def __init__(self, parent, cms_ref, fill, new_color, on_orig=None):
        wal.VPanel.__init__(self, parent)
        grid = wal.GridPanel(self, hgap=5)
        grid.pack(wal.Label(grid, _('Old fill:')))

        self.before_swatch = FillSwatch(grid, cms_ref, fill, (70, 30),
                                        onclick=on_orig)
        grid.pack(self.before_swatch)

        grid.pack(wal.Label(grid, _('New fill:')))

        self.after_swatch = AlphaColorSwatch(grid, cms_ref, new_color, (70, 30))
        grid.pack(self.after_swatch)

        self.pack(grid, padding_all=2)

    def update(self, fill, new_color):
        self.before_swatch.set_swatch_fill(fill)
        self.after_swatch.set_color(new_color)


class FillFillRefPanel(wal.VPanel):
    def __init__(self, parent, cms_ref, fill, new_fill, on_orig=None):
        wal.VPanel.__init__(self, parent)
        grid = wal.GridPanel(self, hgap=5)
        grid.pack(wal.Label(grid, _('Old fill:')))

        self.before_swatch = FillSwatch(grid, cms_ref, fill, (70, 30),
                                        onclick=on_orig)
        grid.pack(self.before_swatch)

        grid.pack(wal.Label(grid, _('New fill:')))

        self.after_swatch = FillSwatch(grid, cms_ref, new_fill, (70, 30),
                                       border='swe', even_odd=False)
        grid.pack(self.after_swatch)

        self.pack(grid, padding_all=2)

    def update(self, fill, new_fill):
        self.before_swatch.set_swatch_fill(fill)
        self.after_swatch.set_swatch_fill(new_fill)


class StyleMonitor(wal.VPanel):
    def __init__(self, parent, app):
        self.app = app
        wal.VPanel.__init__(self, parent)
        self.pack((25, 25))
        self.stroke = AlphaColorSwatch(self, app.default_cms, [],
                                       border='news', onclick=self.stroke_click)
        self.stroke.set_position((5, 5))
        self.fill = FillSwatch(self, app.default_cms, [],
                               border='news', onclick=self.fill_click)
        self.fill.set_position((0, 0))
        events.connect(events.DOC_CHANGED, self.doc_changed)
        events.connect(events.DOC_MODIFIED, self.doc_changed)
        events.connect(events.NO_DOCS, self.no_docs)

    def doc_changed(self, doc):
        fill_style = doc.model.get_def_style()[0]
        stroke_style = doc.model.get_def_style()[1]
        self.stroke.cms = self.fill.cms = doc.cms
        self.fill.set_swatch_fill(fill_style)
        if stroke_style:
            self.stroke.set_color(stroke_style[2])
        else:
            self.stroke.set_color([])

    def no_docs(self):
        self.stroke.cms = self.fill.cms = self.app.default_cms
        self.stroke.set_color([])
        self.fill.set_swatch_fill([])

    def fill_click(self):
        self.app.proxy.fill_dialog(True)

    def stroke_click(self):
        self.app.proxy.stroke_dialog(True)


class ColoredSlider(wal.VPanel, wal.SensitiveCanvas):
    start_clr = wal.BLACK
    stop_clr = wal.WHITE
    value = 0.0
    knob = None
    check_flag = False
    callback = None

    def __init__(self, parent, size=20, onchange=None):
        wal.VPanel.__init__(self, parent)
        wal.SensitiveCanvas.__init__(self, check_move=True)
        self.set_double_buffered()
        self.pack((256 + 8, size + 10))
        self.knob = get_icon(icons.SLIDER_KNOB, size=wal.DEF_SIZE)
        if onchange:
            self.callback = onchange

    def paint(self):
        w, h = self.get_size()
        w -= 6
        h -= 10
        x, y = 3, 5
        self.draw_linear_gradient((x, y, w, h), self.start_clr, self.stop_clr)
        self.set_fill()
        self.set_stroke(wal.BLACK)
        self.draw_rect(x, y, w, h)
        pos = int(self.value * 255.0) + 1
        self.draw_bitmap(self.knob, pos, y + h)

    def _set_value(self, val):
        val = 4 if val < 4 else val
        val = 259 if val > 259 else val
        val = (val - 4) / 255.0
        self.value = val
        self.refresh()
        if self.callback:
            self.callback()

    def set_value(self, val, start_clr=wal.BLACK, stop_clr=wal.WHITE):
        self.value = val
        self.start_clr = start_clr
        self.stop_clr = stop_clr
        self.refresh()

    def get_value(self):
        return self.value

    def mouse_left_down(self, val):
        self.check_flag = True
        self._set_value(val[0])

    def mouse_move(self, val):
        if self.check_flag:
            self._set_value(val[0])

    def mouse_left_up(self, val):
        self.check_flag = False
        self._set_value(val[0])


class ColoredAlphaSlider(ColoredSlider):
    def __init__(self, parent, size=20, onchange=None):
        ColoredSlider.__init__(self, parent, size, onchange)

    def paint(self):
        w, h = self.get_size()
        w -= 6
        h -= 10
        x, y = 3, 5
        x1 = y1 = 0
        flag_y = True
        self.set_stroke()
        while y + y1 < h:
            flag_x = flag_y
            while x + x1 < w:
                clr = wal.WHITE
                if not flag_x:
                    clr = wal.LIGHT_GRAY
                self.set_fill(clr)
                rh = rw = 10
                if x + x1 + rw > w:
                    rw = w - x - x1
                self.draw_rect(x + x1, y + y1, rw, rh)
                flag_x = not flag_x
                x1 += 10
            flag_y = not flag_y
            y1 += 10
            x1 = 0

        w, h = self.get_size()
        w -= 6
        h -= 10
        x, y = 3, 5
        rect = (x, y, w, h)
        self.gc_draw_linear_gradient(rect, self.start_clr, self.stop_clr)
        self.set_fill()
        self.set_stroke(wal.BLACK)
        self.draw_rect(x, y, w, h)
        pos = int(self.value * 255.0) + 1
        self.draw_bitmap(self.knob, pos, y + h)


class CmykMixer(wal.GridPanel):
    color = None
    callback = None

    def __init__(self, parent, cms_ref, color=None, onchange=None):
        wal.GridPanel.__init__(self, parent, 5, 4, 3, 5)
        self.cms = cms_ref
        if color:
            self.color = color
        else:
            self.color = [uc2const.COLOR_CMYK, [0.0, 0.0, 0.0, 1.0], 1.0, '']
        if onchange:
            self.callback = onchange

        self.color_sliders = []
        self.color_spins = []

        labels = ['C:', 'M:', 'Y:', 'K:']
        for item in labels:
            self.pack(wal.Label(self, item))
            slider = ColoredSlider(self, onchange=self.on_slider_change)
            self.color_sliders.append(slider)
            self.pack(self.color_sliders[-1])
            spin = wal.FloatSpin(self, range_val=(0.0, 100.0),
                                 onchange=self.on_change,
                                 onenter=self.on_change)
            self.color_spins.append(spin)
            self.pack(self.color_spins[-1])
            self.pack(wal.Label(self, '%'))

        self.pack(wal.Label(self, 'A:'))
        self.alpha_slider = ColoredAlphaSlider(self,
                                               onchange=self.on_slider_change)
        self.pack(self.alpha_slider)
        self.alpha_spin = wal.FloatSpin(self,
                                        range_val=(0.0, 100.0),
                                        onchange=self.on_change,
                                        onenter=self.on_change)
        self.pack(self.alpha_spin)
        self.pack(wal.Label(self, '%'))

    def on_change(self):
        color_vals = []
        for item in self.color_spins:
            color_vals.append(item.get_value() / 100.0)
        self.color[1] = color_vals
        self.color[2] = self.alpha_spin.get_value() / 100.0
        if self.callback:
            self.callback()
        else:
            self.update()

    def on_slider_change(self):
        color_vals = []
        for item in self.color_sliders:
            color_vals.append(item.get_value())
        self.color[1] = color_vals
        self.color[2] = self.alpha_slider.get_value()
        if self.callback:
            self.callback()
        else:
            self.update()

    def get_color(self):
        return self.color

    def set_color(self, color):
        if color:
            self.color = color
            self.update()

    def update(self):
        for item in self.color_spins:
            index = self.color_spins.index(item)
            item.set_value(self.color[1][index] * 100.0)
        for item in self.color_sliders:
            index = self.color_sliders.index(item)
            start_clr = deepcopy(self.color)
            stop_clr = deepcopy(self.color)
            start_clr[1][index] = 0.0
            stop_clr[1][index] = 1.0
            start_clr = self.cms.get_rgb_color255(start_clr)
            stop_clr = self.cms.get_rgb_color255(stop_clr)
            item.set_value(self.color[1][index], start_clr, stop_clr)

        start_clr = deepcopy(self.color)
        start_clr[2] = 0.0
        stop_clr = deepcopy(self.color)
        stop_clr[2] = 1.0
        start_clr = self.cms.get_rgba_color255(start_clr)
        stop_clr = self.cms.get_rgba_color255(stop_clr)
        self.alpha_slider.set_value(self.color[2], start_clr, stop_clr)
        self.alpha_spin.set_value(self.color[2] * 100.0)


class RgbMixer(wal.VPanel):
    color = None
    callback = None

    def __init__(self, parent, cms_ref, color=None, onchange=None):
        wal.VPanel.__init__(self, parent)
        self.cms = cms_ref
        if color:
            self.color = color
        else:
            self.color = [uc2const.COLOR_RGB, [0.0, 0.0, 0.0], 1.0, '']
        if onchange:
            self.callback = onchange

        self.color_sliders = []
        self.color_spins = []
        grid = wal.GridPanel(self, 4, 3, 3, 5)

        labels = ['R:', 'G:', 'B:']
        for item in labels:
            grid.pack(wal.Label(grid, item))
            slider = ColoredSlider(grid, onchange=self.on_slider_change)
            self.color_sliders.append(slider)
            grid.pack(self.color_sliders[-1])
            spin = wal.IntSpin(grid,
                               range_val=(0, 255),
                               onchange=self.on_change,
                               onenter=self.on_change)
            self.color_spins.append(spin)
            grid.pack(self.color_spins[-1])

        grid.pack(wal.Label(grid, 'A:'))
        self.alpha_slider = ColoredAlphaSlider(grid,
                                               onchange=self.on_slider_change)
        grid.pack(self.alpha_slider)
        self.alpha_spin = wal.IntSpin(grid,
                                      range_val=(0, 255),
                                      onchange=self.on_change,
                                      onenter=self.on_change)
        grid.pack(self.alpha_spin)

        self.pack(grid)

        self.pack(wal.HPanel(self), fill=True, expand=True)

        html_panel = wal.HPanel(self)
        html_panel.pack(wal.Label(html_panel, _('HTML notation:')), padding=5)
        self.html = HexField(html_panel, onchange=self.on_hex_change)
        html_panel.pack(self.html)
        self.pack(html_panel, padding=5)

    def on_hex_change(self):
        hexcolor = self.html.get_color()
        if len(hexcolor) == 7:
            self.color[1] = cms.hexcolor_to_rgb(hexcolor)
            self.color[2] = 1.0
        elif len(hexcolor) == 9:
            r, g, b, a = cms.hexcolor_to_rgba(hexcolor)
            self.color[1] = [r, g, b]
            self.color[2] = a
        if self.callback:
            self.callback()
        else:
            self.update()

    def on_slider_change(self):
        color_vals = []
        for item in self.color_sliders:
            color_vals.append(item.get_value())
        self.color[1] = color_vals
        self.color[2] = self.alpha_slider.get_value()
        if self.callback:
            self.callback()
        else:
            self.update()

    def on_change(self):
        color_vals = []
        for item in self.color_spins:
            color_vals.append(item.get_value() / 255.0)
        self.color[1] = color_vals
        self.color[2] = self.alpha_spin.get_value() / 255.0
        if self.callback:
            self.callback()
        else:
            self.update()

    def get_color(self):
        return self.color

    def set_color(self, color):
        if color:
            self.color = color
            self.update()

    def update(self):
        for item in self.color_spins:
            index = self.color_spins.index(item)
            item.set_value(self.color[1][index] * 255.0)
        for item in self.color_sliders:
            index = self.color_sliders.index(item)
            start_clr = deepcopy(self.color)
            stop_clr = deepcopy(self.color)
            start_clr[1][index] = 0.0
            stop_clr[1][index] = 1.0
            start_clr = self.cms.get_rgb_color255(start_clr)
            stop_clr = self.cms.get_rgb_color255(stop_clr)
            item.set_value(self.color[1][index], start_clr, stop_clr)

        start_clr = deepcopy(self.color)
        start_clr[2] = 0.0
        stop_clr = deepcopy(self.color)
        stop_clr[2] = 1.0
        start_clr = self.cms.get_rgba_color255(start_clr)
        stop_clr = self.cms.get_rgba_color255(stop_clr)
        self.alpha_slider.set_value(self.color[2], start_clr, stop_clr)
        self.alpha_spin.set_value(self.color[2] * 255.0)
        self.html.set_color(self.color)


class GrayMixer(wal.VPanel):
    color = None
    callback = None

    def __init__(self, parent, cms_ref, color=None, onchange=None):
        wal.VPanel.__init__(self, parent)
        self.cms = cms_ref
        if color:
            self.color = color
        else:
            self.color = [uc2const.COLOR_GRAY, [0.0, ], 1.0, '']
        if onchange:
            self.callback = onchange

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

        grid.pack(wal.Label(grid, 'L:'))
        self.color_slider = ColoredSlider(grid, size=40,
                                          onchange=self.on_slider_change)
        grid.pack(self.color_slider)
        self.color_spin = wal.IntSpin(grid,
                                      range_val=(0, 255),
                                      onchange=self.on_change,
                                      onenter=self.on_change)
        grid.pack(self.color_spin)

        grid.pack(wal.Label(grid, 'A:'))
        self.alpha_slider = ColoredAlphaSlider(grid, size=40,
                                               onchange=self.on_slider_change)
        grid.pack(self.alpha_slider)
        self.alpha_spin = wal.IntSpin(grid,
                                      range_val=(0, 255),
                                      onchange=self.on_change,
                                      onenter=self.on_change)
        grid.pack(self.alpha_spin)

        self.pack(grid)

    def on_slider_change(self):
        self.color[1] = [self.color_slider.get_value(), ]
        self.color[2] = self.alpha_slider.get_value()
        if self.callback:
            self.callback()
        else:
            self.update()

    def on_change(self):
        self.color[1] = [self.color_spin.get_value() / 255.0, ]
        self.color[2] = self.alpha_spin.get_value() / 255.0
        if self.callback:
            self.callback()
        else:
            self.update()

    def get_color(self):
        return self.color

    def set_color(self, color):
        if color:
            self.color = color
            self.update()

    def update(self):
        self.color_spin.set_value(self.color[1][0] * 255.0)
        # L slider
        start_clr = deepcopy(self.color)
        stop_clr = deepcopy(self.color)
        start_clr[1][0] = 0.0
        stop_clr[1][0] = 1.0
        start_clr = self.cms.get_rgb_color255(start_clr)
        stop_clr = self.cms.get_rgb_color255(stop_clr)
        self.color_slider.set_value(self.color[1][0], start_clr, stop_clr)
        # Alpha slider
        start_clr = deepcopy(self.color)
        start_clr[2] = 0.0
        stop_clr = deepcopy(self.color)
        stop_clr[2] = 1.0
        start_clr = self.cms.get_rgba_color255(start_clr)
        stop_clr = self.cms.get_rgba_color255(stop_clr)
        self.alpha_slider.set_value(self.color[2], start_clr, stop_clr)
        self.alpha_spin.set_value(self.color[2] * 255.0)


class SpotMixer(wal.VPanel):
    color = None
    callback = None

    def __init__(self, parent, cms_ref, color=None, onchange=None):
        wal.VPanel.__init__(self, parent)
        self.cms = cms_ref
        if color:
            self.color = color
        else:
            self.color = get_registration_black()
        if onchange:
            self.callback = onchange

        name_panel = wal.HPanel(self)
        name_panel.pack(wal.Label(name_panel, 'Name:'), padding=5)
        self.name_field = wal.Entry(name_panel, width=30,
                                    onchange=self.on_change)
        name_panel.pack(self.name_field)
        self.pack(name_panel, padding_all=5)

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

        grid.pack((1, 1))
        self.rgb_txt = wal.Label(grid, '---')
        grid.pack(self.rgb_txt)
        grid.pack((1, 1))
        grid.pack((1, 1))

        grid.pack((1, 1))
        self.cmyk_txt = wal.Label(grid, '---')
        grid.pack(self.cmyk_txt)
        grid.pack((1, 1))
        grid.pack((1, 1))

        grid.pack(wal.Label(grid, 'A:'))
        self.alpha_slider = ColoredAlphaSlider(grid,
                                               onchange=self.on_slider_change)
        grid.pack(self.alpha_slider)
        self.alpha_spin = wal.FloatSpin(grid,
                                        range_val=(0.0, 100.0),
                                        onchange=self.on_change,
                                        onenter=self.on_change)
        grid.pack(self.alpha_spin)
        grid.pack(wal.Label(self, '%'))

        self.pack(grid)

    def on_slider_change(self):
        self.color[2] = self.alpha_slider.get_value()
        if self.callback:
            self.callback()
        else:
            self.update()

    def on_change(self):
        self.color[3] = self.name_field.get_value()
        self.color[2] = self.alpha_spin.get_value() / 100.0
        if self.callback:
            self.callback()
        else:
            self.update()

    def get_color(self):
        return self.color

    def set_color(self, color):
        if color:
            self.color = color
            self.update()

    def update(self):
        self.name_field.set_value(self.color[3])
        txt = 'RGB: '
        if self.color[1][0]:
            txt += verbose_color(self.cms.get_rgb_color(self.color))
        else:
            txt += '---'
        self.rgb_txt.set_text(txt)

        txt = 'CMYK: '
        if self.color[1][1]:
            txt += verbose_color(self.cms.get_cmyk_color(self.color))
        else:
            txt += '---'
        self.cmyk_txt.set_text(txt)

        # Alpha slider
        start_clr = deepcopy(self.color)
        start_clr[2] = 0.0
        stop_clr = deepcopy(self.color)
        stop_clr[2] = 1.0
        start_clr = self.cms.get_rgba_color255(start_clr)
        stop_clr = self.cms.get_rgba_color255(stop_clr)
        self.alpha_slider.set_value(self.color[2], start_clr, stop_clr)
        self.alpha_spin.set_value(self.color[2] * 255.0)


class ColorSticker(wal.VPanel):
    def __init__(self, parent, cms_ref, color=None):
        self.cms = cms_ref
        if not color:
            color = get_registration_black()
        wal.VPanel.__init__(self, parent, True)
        if wal.IS_MSW:
            self.set_bg(wal.GRAY)
        inner_panel = wal.VPanel(self)
        inner_panel.set_bg(wal.WHITE)

        nf = wal.VPanel(inner_panel)
        nf.set_bg(wal.BLACK)
        self.name_field = wal.Label(nf, '???', fontbold=True, fg=wal.WHITE)
        nf.pack(self.name_field, padding_all=3)

        vf = wal.VPanel(inner_panel)
        vf.set_bg(wal.WHITE)
        self.color_type = wal.Label(vf, '???', fontbold=True, fontsize=-1)
        self.line1 = wal.Label(vf, '???', fontsize=-1)
        self.line2 = wal.Label(vf, '???', fontsize=-1)
        vf.pack(self.color_type, padding_all=3)
        vf.pack(self.line1, padding_all=1)
        vf.pack(self.line2, padding_all=1)

        self.color_swatch = AlphaColorSwatch(inner_panel, self.cms,
                                             color, (180, 100), border='')

        inner_panel.pack(nf, fill=True)
        inner_panel.pack(vf, fill=True)
        inner_panel.pack(self.color_swatch, fill=True, expand=True)
        padding = 0
        if wal.IS_MSW:
            padding = 1
        self.pack(inner_panel, fill=True, expand=True, padding_all=padding)
        self.layout()

    def set_color(self, color):
        if not color:
            return
        txt = '???' if not color[3] else color[3]
        self.name_field.set_text(txt)
        self.color_swatch.set_color(color)
        self.color_type.set_text(color[0] + ' ' + _('color'))
        if not color[0] == uc2const.COLOR_SPOT:
            self.line1.set_text(cms.verbose_color(color))
            self.line2.set_text('')
        else:
            txt1 = txt2 = ''
            if color[1][0]:
                txt1 = cms.verbose_color(self.cms.get_rgb_color(color))
            if color[1][1]:
                txt2 = cms.verbose_color(self.cms.get_cmyk_color(color))
            self.line1.set_text(txt1)
            self.line2.set_text(txt2)
        self.parent.layout()


class PaletteMixer(wal.HPanel):
    callback = None
    color = None

    def __init__(self, parent, app, cms_ref, onchange=None):
        self.cms = cms_ref
        self.app = app
        self.callback = onchange
        wal.HPanel.__init__(self, parent)

        current_palette = self.get_current_palette()
        self.palviewer = PaletteViewer(self, self.cms, current_palette,
                                       self.on_change)
        self.pack(self.palviewer, fill=True)
        self.pack((5, 5))

        view_panel = wal.VPanel(self)
        view_panel.pack(wal.Label(view_panel, _('Palette:')),
                        align_center=False)

        pal_list = self.get_palette_list()
        self.pal = wal.Combolist(view_panel, items=pal_list,
                                 onchange=self.change_palette)
        current_palette_name = current_palette.model.name
        self.pal.set_active(pal_list.index(current_palette_name))
        view_panel.pack(self.pal, fill=True)

        self.sticker = ColorSticker(view_panel, self.cms)
        view_panel.pack(self.sticker, padding=5)
        self.pack(view_panel, fill=True, expand=True)
        self.palviewer.set_active_color()

    def change_palette(self):
        palette_name = self.get_palette_name_by_index(self.pal.get_active())
        current_palette = self.get_palette_by_name(palette_name)
        self.palviewer.draw_palette(current_palette)
        self.palviewer.set_active_color()
        self.on_change()

    def on_change(self):
        self.color = self.palviewer.get_color()
        self.sticker.set_color(self.color)
        if self.callback:
            self.callback(self.color)

    def get_color(self):
        return self.color

    # ===Stuff
    def get_current_palette(self):
        current_palette_name = config.palette
        if not current_palette_name:
            return self.app.palettes.palette_in_use
        return self.get_palette_by_name(current_palette_name)

    def get_palette_list(self):
        palettes = self.app.palettes.palettes
        pal_list = palettes.keys()
        pal_list.sort()
        return pal_list

    def get_palette_by_name(self, name):
        palettes = self.app.palettes.palettes
        pal_list = self.get_palette_list()
        if name not in pal_list:
            name = self.app.palettes.get_default_palette_name()
        return palettes[name]

    def get_palette_name_by_index(self, index):
        pal_list = self.get_palette_list()
        return pal_list[index]

    def get_index_by_palette_name(self, name=''):
        pal_list = self.get_palette_list()
        if name not in pal_list:
            name = self.app.palettes.get_default_palette_name()
        return pal_list.index(name)
