#    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 <https://www.gnu.org/licenses/>.
"""Overlay window for voice"""
import math
import cairo
from .overlay import OverlayWindow
from .image_getter import get_surface, draw_img_to_rect


class VoiceOverlayWindow(OverlayWindow):
    """Overlay window for voice"""

    def __init__(self, discover):
        OverlayWindow.__init__(self)

        self.discover = discover
        self.avatars = {}

        self.avatar_size = 48
        self.text_pad = 6
        self.text_font = None
        self.text_size = 13
        self.icon_spacing = 8
        self.vert_edge_padding = 0
        self.horz_edge_padding = 0
        self.only_speaking = None
        self.highlight_self = None
        self.order = None
        self.def_avatar = None

        self.round_avatar = True
        self.icon_only = True
        self.talk_col = [0.0, 0.6, 0.0, 0.1]
        self.text_col = [1.0, 1.0, 1.0, 1.0]
        self.norm_col = [0.0, 0.0, 0.0, 0.5]
        self.wind_col = [0.0, 0.0, 0.0, 0.0]
        self.mute_col = [0.7, 0.0, 0.0, 1.0]
        self.userlist = []
        self.users_to_draw = []
        self.connected = False
        self.force_location()
        get_surface(self.recv_avatar,
                    "https://cdn.discordapp.com/embed/avatars/3.png",
                    'def', self.avatar_size)
        self.set_title("Discover Voice")

    def set_bg(self, bg):
        self.norm_col = bg
        self.redraw()

    def set_fg(self, fg):
        self.text_col = fg
        self.redraw()

    def set_tk(self, tk):
        self.talk_col = tk
        self.redraw()

    def set_mt(self, mt):
        self.mute_col = mt
        self.redraw()

    def set_avatar_size(self, size):
        self.avatar_size = size
        self.redraw()

    def set_icon_spacing(self, i):
        self.icon_spacing = i
        self.redraw()

    def set_text_padding(self, i):
        self.text_pad = i
        self.redraw()

    def set_vert_edge_padding(self, i):
        self.vert_edge_padding = i
        self.redraw()

    def set_horz_edge_padding(self, i):
        self.horz_edge_padding = i
        self.redraw()

    def set_square_avatar(self, i):
        self.round_avatar = not i
        self.redraw()

    def set_only_speaking(self, only_speaking):
        self.only_speaking = only_speaking

    def set_highlight_self(self, highlight_self):
        self.highlight_self = highlight_self

    def set_order(self, i):
        self.order = i

    def set_icon_only(self, i):
        self.icon_only = i
        self.redraw()

    def set_wind_col(self):
        self.col(self.wind_col)

    def set_text_col(self):
        self.col(self.text_col)

    def set_norm_col(self):
        self.col(self.norm_col)

    def set_talk_col(self, a=1.0):
        self.col(self.talk_col, a)

    def set_mute_col(self, a=1.0):
        self.col(self.mute_col, a)

    def set_user_list(self, userlist, alt):
        self.userlist = userlist
        for user in userlist:
            if "nick" in user:
                user["friendlyname"] = user["nick"]
            else:
                user["friendlyname"] = user["username"]
        if self.order == 1:  # ID Sort
            self.userlist.sort(key=lambda x: x["id"])
        elif self.order == 2:  # Spoken sort
            self.userlist.sort(key=lambda x: x["lastspoken"], reverse=True)
            self.userlist.sort(key=lambda x: x["speaking"], reverse=True)
        else:  # Name sort
            self.userlist.sort(key=lambda x: x["friendlyname"])
        screen = self.get_screen()
        c = screen.is_composited()
        if not self.compositing == c:
            alt = True
            self.compositing = c
        if alt:
            self.redraw()

    def set_connection(self, connection):
        is_connected = connection == "VOICE_CONNECTED"
        if self.connected != is_connected:
            self.connected = is_connected
            self.redraw()

    def overlay_draw(self, _w, context, _data=None):
        self.context = context
        context.set_antialias(cairo.ANTIALIAS_GOOD)
        # Get size of window
        (w, h) = self.get_size()
        # Make background transparent
        self.set_wind_col()
        # Don't layer drawing over each other, always replace
        context.set_operator(cairo.OPERATOR_SOURCE)
        context.paint()
        context.save()
        if self.is_wayland:
            # Special case!
            # The window is full-screen regardless of what the user has selected. Because Wayland
            # We need to set a clip and a transform to imitate original behaviour

            w = self.w
            h = self.h
            context.translate(self.x, self.y)
            context.rectangle(0, 0, w, h)
            context.clip()

        context.set_operator(cairo.OPERATOR_OVER)
        if not self.connected:
            return

        connection = self.discover.connection
        self_user = connection.user

        # Gather which users to draw
        self.users_to_draw = self.userlist[:]
        for user in self.userlist:
            # Bad object equality here, so we need to reassign
            if user["id"] == self_user["id"]:
                self_user = user

            # Update friendly name with nick if possible
            if "nick" in user:
                user["friendlyname"] = user["nick"]
            else:
                user["friendlyname"] = user["username"]

            # Remove users that arent speaking
            if self.only_speaking:
                speaking = "speaking" in user and user["speaking"]
                if not speaking:
                    self.users_to_draw.remove(user)

        if self.highlight_self:
            try:
                self.users_to_draw.remove(self_user)
            except ValueError:
                pass  # Not in list
            self.users_to_draw.insert(0, self_user)

        # Calculate height needed to show overlay
        height = (len(self.users_to_draw) * self.avatar_size) + \
            (len(self.users_to_draw) + 1) * self.icon_spacing

        # Choose where to start drawing
        rh = 0 + self.vert_edge_padding
        if self.align_vert == 1:
            # Ignore padding?
            rh = (h / 2) - (height / 2)
        elif self.align_vert == 2:
            rh = h - height - self.vert_edge_padding

        for user in self.users_to_draw:
            self.draw_avatar(context, user, rh)
            # Shift the relative position down to next location
            rh += self.avatar_size + self.icon_spacing

        # Don't hold a ref
        if self.is_wayland:
            context.restore()
        self.context = None

    def recv_avatar(self, identifier, pix):
        if identifier == 'def':
            self.def_avatar = pix
        else:
            self.avatars[identifier] = pix
        self.redraw()

    def delete_avatar(self, identifier):
        if id in self.avatars:
            del self.avatars[identifier]

    def draw_avatar(self, context, user, y):
        # Ensure pixbuf for avatar
        if user["id"] not in self.avatars and user["avatar"]:
            url = "https://cdn.discordapp.com/avatars/%s/%s.jpg" % (
                user['id'], user['avatar'])
            get_surface(self.recv_avatar, url, user["id"],
                        self.avatar_size)

            # Set the key with no value to avoid spamming requests
            self.avatars[user["id"]] = None

        c = None
        mute = False
        deaf = False

        if "mute" in user and user["mute"]:
            mute = True
        if "deaf" in user and user["deaf"]:
            deaf = True
        if "speaking" in user and user["speaking"] and not deaf and not mute:
            c = self.talk_col
        pix = None
        if user["id"] in self.avatars:
            pix = self.avatars[user["id"]]
        if self.align_right:
            if not self.icon_only:
                self.draw_text(
                    context, user["friendlyname"], self.w - self.avatar_size - self.horz_edge_padding, y)
            self.draw_avatar_pix(
                context, pix, self.w - self.avatar_size - self.horz_edge_padding, y, c)
            if deaf:
                self.draw_deaf(context, self.w - self.avatar_size -
                               self.horz_edge_padding, y)
            elif mute:
                self.draw_mute(context, self.w - self.avatar_size -
                               self.horz_edge_padding, y)
        else:
            if not self.icon_only:
                self.draw_text(
                    context, user["friendlyname"], self.avatar_size + self.horz_edge_padding, y)
            self.draw_avatar_pix(
                context, pix, self.horz_edge_padding, y, c)
            if deaf:
                self.draw_deaf(context, self.horz_edge_padding, y)
            elif mute:
                self.draw_mute(context, self.horz_edge_padding, y)

    def draw_text(self, context, string, x, y):
        if self.text_font:
            context.set_font_face(cairo.ToyFontFace(
                self.text_font, cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL))
        context.set_font_size(self.text_size)
        _xb, _yb, w, h, _dx, _dy = context.text_extents(string)
        ho = (self.avatar_size / 2) - (h / 2)
        if self.align_right:
            context.move_to(0, 0)
            self.set_norm_col()
            context.rectangle(x - w - (self.text_pad * 2), y + ho - self.text_pad,
                              w + (self.text_pad * 4), h + (self.text_pad * 2))
            context.fill()

            self.set_text_col()
            context.move_to(x - w - self.text_pad, y + ho + h)
            context.show_text(string)
        else:
            context.move_to(0, 0)
            self.set_norm_col()
            context.rectangle(x - (self.text_pad * 2), y + ho - self.text_pad,
                              w + (self.text_pad * 4), h + (self.text_pad * 2))
            context.fill()

            self.set_text_col()
            context.move_to(x + self.text_pad, y + ho + h)
            context.show_text(string)

    def draw_avatar_pix(self, context, pixbuf, x, y, c):
        if not pixbuf:
            pixbuf = self.def_avatar
            if not pixbuf:
                return
        context.move_to(x, y)
        context.save()
        if self.round_avatar:
            context.arc(x + (self.avatar_size / 2), y +
                        (self.avatar_size / 2), self.avatar_size / 2, 0, 2 * math.pi)
            context.clip()

        self.set_norm_col()
        context.set_operator(cairo.OPERATOR_SOURCE)
        context.rectangle(x, y, self.avatar_size, self.avatar_size)
        context.fill()
        draw_img_to_rect(pixbuf, context, x, y,
                         self.avatar_size, self.avatar_size)
        context.restore()
        if c:
            if self.round_avatar:
                context.arc(x + (self.avatar_size / 2), y +
                            (self.avatar_size / 2), self.avatar_size / 2, 0, 2 * math.pi)
                self.col(c)
                context.stroke()
            else:
                context.rectangle(x, y, self.avatar_size, self.avatar_size)
                self.col(c)
                context.stroke()

    def draw_mute(self, context, x, y):
        context.save()
        context.translate(x, y)
        context.scale(self.avatar_size, self.avatar_size)
        self.set_mute_col()
        context.save()

        # Clip Strike-through
        context.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
        context.set_line_width(0.1)
        context.move_to(0.0, 0.0)
        context.line_to(1.0, 0.0)
        context.line_to(1.0, 1.0)
        context.line_to(0.0, 1.0)
        context.line_to(0.0, 0.0)
        context.close_path()
        context.new_sub_path()
        context.arc(0.9, 0.1, 0.05, 1.25 * math.pi, 2.25 * math.pi)
        context.arc(0.1, 0.9, 0.05, .25 * math.pi, 1.25 * math.pi)
        context.close_path()
        context.clip()

        # Center
        context.set_line_width(0.07)
        context.arc(0.5, 0.3, 0.1, math.pi, 2 * math.pi)
        context.arc(0.5, 0.5, 0.1, 0, math.pi)
        context.close_path()
        context.fill()

        context.set_line_width(0.05)

        # Stand rounded
        context.arc(0.5, 0.5, 0.15, 0, 1.0 * math.pi)
        context.stroke()

        # Stand vertical
        context.move_to(0.5, 0.65)
        context.line_to(0.5, 0.75)
        context.stroke()

        # Stand horizontal
        context.move_to(0.35, 0.75)
        context.line_to(0.65, 0.75)
        context.stroke()

        context.restore()
        # Strike through
        context.arc(0.7, 0.3, 0.035, 1.25 * math.pi, 2.25 * math.pi)
        context.arc(0.3, 0.7, 0.035, .25 * math.pi, 1.25 * math.pi)
        context.close_path()
        context.fill()

        context.restore()

    def draw_deaf(self, context, x, y):
        context.save()
        context.translate(x, y)
        context.scale(self.avatar_size, self.avatar_size)
        self.set_mute_col()
        context.save()

        # Clip Strike-through
        context.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
        context.set_line_width(0.1)
        context.move_to(0.0, 0.0)
        context.line_to(1.0, 0.0)
        context.line_to(1.0, 1.0)
        context.line_to(0.0, 1.0)
        context.line_to(0.0, 0.0)
        context.close_path()
        context.new_sub_path()
        context.arc(0.9, 0.1, 0.05, 1.25 * math.pi, 2.25 * math.pi)
        context.arc(0.1, 0.9, 0.05, .25 * math.pi, 1.25 * math.pi)
        context.close_path()
        context.clip()

        # Top band
        context.arc(0.5, 0.5, 0.2, 1.0 * math.pi, 0)
        context.stroke()

        # Left band
        context.arc(0.28, 0.65, 0.075, 1.5 * math.pi, 0.5 * math.pi)
        context.move_to(0.3, 0.5)
        context.line_to(0.3, 0.75)
        context.stroke()

        # Right band
        context.arc(0.72, 0.65, 0.075, 0.5 * math.pi, 1.5 * math.pi)
        context.move_to(0.7, 0.5)
        context.line_to(0.7, 0.75)
        context.stroke()

        context.restore()
        # Strike through
        context.arc(0.7, 0.3, 0.035, 1.25 * math.pi, 2.25 * math.pi)
        context.arc(0.3, 0.7, 0.035, .25 * math.pi, 1.25 * math.pi)
        context.close_path()
        context.fill()

        context.restore()
