from math import pi, cos, sin

import cairo
from gi.repository import GLib, Gtk, Gdk, Pango, PangoCairo, GObject

from pychess.System import conf
from pychess.Utils import formatTime
from pychess.Utils.const import (
    BLACK,
    WHITE,
    LOCAL,
    UNFINISHED_STATES,
    DRAW,
    WHITEWON,
    BLACKWON,
    UNKNOWN_STATE,
)
from . import preferencesDialog


class ChessClock(Gtk.DrawingArea):
    def __init__(self):
        GObject.GObject.__init__(self)
        self.connect("draw", self.expose)
        self.names = [_("White"), _("Black")]

        self.model = None
        self.short_on_time = [False, False]
        self.alarm_spin = conf.get("alarm_spin")

        conf.notify_add("alarm_spin", self.on_alarm_spin)

    def on_alarm_spin(self, *args):
        self.alarm_spin = conf.get("alarm_spin")

    def expose(self, widget, ctx):
        context = widget.get_window().cairo_create()
        clip_ext = context.clip_extents()

        if clip_ext[0] > 0 or clip_ext[2] < self.get_allocated_width():
            self.redraw_canvas()
            return False

        rec = Gdk.Rectangle()
        rec.x, rec.y, rec.width, rec.height = (
            clip_ext[0],
            clip_ext[1],
            clip_ext[2] - clip_ext[0],
            clip_ext[3] - clip_ext[1],
        )
        context.rectangle(rec.x, rec.y, rec.width, rec.height)
        context.clip()
        self.draw(context)
        return False

    def draw(self, context):
        style_ctxt = self.get_style_context()
        self.light = style_ctxt.lookup_color("p_light_color")[1]
        self.dark = style_ctxt.lookup_color("p_dark_color")[1]
        if not self.model:
            return

        # Draw graphical Clock. Should this be moved to preferences?
        drawClock = True

        rect = Gdk.Rectangle()
        clip_ext = context.clip_extents()
        rect.x, rect.y, rect.width, rect.height = (
            clip_ext[0],
            clip_ext[1],
            clip_ext[2] - clip_ext[0],
            clip_ext[3] - clip_ext[1],
        )
        context.rectangle(
            rect.width / 2.0 * self.model.movingColor, 0, rect.width / 2.0, rect.height
        )

        context.set_source_rgba(
            self.dark.red, self.dark.green, self.dark.blue, self.dark.alpha
        )
        context.fill_preserve()
        context.new_path()

        time0 = self.names[0], self.formatedCache[WHITE]
        layout0 = self.create_pango_layout(" %s: %s " % (time0))
        layout0.set_font_description(Pango.FontDescription("Sans Serif 17"))

        time1 = self.names[1], self.formatedCache[BLACK]
        layout1 = self.create_pango_layout(" %s: %s " % (time1))
        layout1.set_font_description(Pango.FontDescription("Sans Serif 17"))

        dbl_max = max(layout1.get_pixel_size()[0], layout0.get_pixel_size()[0]) * 2
        self.set_size_request(dbl_max + rect.height + 7, -1)

        pangoScale = float(Pango.SCALE)

        # Analog clock code.
        def paintClock(player):
            clock_y = rect.height / 2.0
            clock_x = clock_y + rect.width / 2.0 * player + 1
            rec = rect.height / 2.0 - 3.5

            context.arc(clock_x, clock_y, rec - 1, 0, 2 * pi)
            linear = cairo.LinearGradient(
                clock_x - rec * 2,
                clock_y - rec * 2,
                clock_x + rec * 2,
                clock_y + rec * 2,
            )
            linear.add_color_stop_rgba(0, 1, 1, 1, 0.3)
            linear.add_color_stop_rgba(1, 0, 0, 0, 0.3)
            # context.set_source_rgba( 0, 0, 0, .3)
            context.set_source(linear)
            context.fill()

            linear = cairo.LinearGradient(
                clock_x - rec, clock_y - rec, clock_x + rec, clock_y + rec
            )
            linear.add_color_stop_rgba(0, 0, 0, 0, 0.5)
            linear.add_color_stop_rgba(1, 1, 1, 1, 0.5)
            context.arc(clock_x, clock_y, rec, 0, 2 * pi)
            context.set_source(linear)
            context.set_line_width(2.5)
            context.stroke()

            starttime = float(self.model.getInitialTime()) or 1
            used = self.model.getPlayerTime(player) / starttime
            if used > 0:
                if used > 0:
                    context.arc(
                        clock_x, clock_y, rec - 0.8, -(used + 0.25) * 2 * pi, -0.5 * pi
                    )
                    context.line_to(clock_x, clock_y)
                    context.close_path()
                elif used == 0:
                    context.arc(clock_x, clock_y, rec - 0.8, -0.5 * pi, 1.5 * pi)
                    context.line_to(clock_x, clock_y)

                radial = cairo.RadialGradient(
                    clock_x, clock_y, 3, clock_x, clock_y, rec
                )
                if player == 0:
                    # radial.add_color_stop_rgb(0, .73, .74, .71)
                    radial.add_color_stop_rgb(0, 0.93, 0.93, 0.92)
                    radial.add_color_stop_rgb(1, 1, 1, 1)
                else:
                    # radial.add_color_stop_rgb(0, .53, .54, .52)
                    radial.add_color_stop_rgb(0, 0.18, 0.20, 0.21)
                    radial.add_color_stop_rgb(1, 0, 0, 0)
                context.set_source(radial)
                context.fill()

                x_loc = clock_x - cos((used - 0.25) * 2 * pi) * (rec - 1)
                y_loc = clock_y + sin((used - 0.25) * 2 * pi) * (rec - 1)
                context.move_to(clock_x, clock_y - rec + 1)
                context.line_to(clock_x, clock_y)
                context.line_to(x_loc, y_loc)
                context.set_line_width(0.2)
                if player == 0:
                    context.set_source_rgb(0, 0, 0)
                else:
                    context.set_source_rgb(1, 1, 1)
                context.stroke()

        if drawClock:
            paintClock(WHITE)
        if (self.model.movingColor or WHITE) == WHITE:
            context.set_source_rgba(
                self.light.red, self.light.green, self.light.blue, self.light.alpha
            )
        else:
            context.set_source_rgba(
                self.dark.red, self.dark.green, self.dark.blue, self.dark.alpha
            )
        y_loc = (
            rect.height / 2.0
            - layout0.get_extents()[0].height / pangoScale / 2
            - layout0.get_extents()[0].y / pangoScale
        )
        context.move_to(rect.height - 7, y_loc)
        PangoCairo.show_layout(context, layout0)

        if drawClock:
            paintClock(BLACK)
        if self.model.movingColor == BLACK:
            context.set_source_rgba(
                self.light.red, self.light.green, self.light.blue, self.light.alpha
            )
        else:
            context.set_source_rgba(
                self.dark.red, self.dark.green, self.dark.blue, self.dark.alpha
            )
        y_loc = (
            rect.height / 2.0
            - layout0.get_extents()[0].height / pangoScale / 2
            - layout0.get_extents()[0].y / pangoScale
        )
        context.move_to(rect.width / 2.0 + rect.height - 7, y_loc)
        PangoCairo.show_layout(context, layout1)

    def redraw_canvas(self):
        def do_redraw_canvas():
            if self.get_window():
                allocation = self.get_allocation()
                rect = Gdk.Rectangle()
                rect.x, rect.y, rect.width, rect.height = (
                    0,
                    0,
                    allocation.width,
                    allocation.height,
                )
                self.get_window().invalidate_rect(rect, True)
                self.get_window().process_updates(True)

        GLib.idle_add(do_redraw_canvas)

    def setModel(self, model):
        self.model = model
        self.model.connect("time_changed", self.time_changed)
        self.model.connect("player_changed", self.player_changed)
        self.formatedCache = [
            formatTime(self.model.getPlayerTime(self.model.movingColor or WHITE))
        ] * 2
        if model.secs != 0 or model.gain != 0:
            GLib.timeout_add(100, self.update)

    def time_changed(self, model):
        self.update()

    def player_changed(self, model):
        self.redraw_canvas()

    def update(self, wmovecount=-1, bmovecount=-1):
        if self.model.ended:
            return False
        if len(self.model.gamemodel.players) < 2:
            return not self.model.ended
        alarm_time = int(self.alarm_spin)
        if (
            self.model.getPlayerTime(self.model.movingColor) <= alarm_time
            and self.model.gamemodel.players[self.model.movingColor].__type__ == LOCAL
            and self.model.gamemodel.status in UNFINISHED_STATES
            and self.model.secs != 0
            and self.model.gamemodel.endstatus
            not in (DRAW, WHITEWON, BLACKWON, UNKNOWN_STATE)
            and not self.short_on_time[self.model.movingColor]
        ):
            self.short_on_time[self.model.movingColor] = True
            preferencesDialog.SoundTab.playAction("shortOnTime")

        if self.model.paused and wmovecount == -1 and bmovecount == -1:
            return not self.model.ended
        white_time = formatTime(self.model.getPlayerTime(WHITE, wmovecount))
        black_time = formatTime(self.model.getPlayerTime(BLACK, bmovecount))
        if self.formatedCache != [white_time, black_time]:
            self.formatedCache = [white_time, black_time]
            self.redraw_canvas()
        return not self.model.ended
