# window.py
#
# Copyright 2024 Lucas Fryzek
#
# 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/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
from gi.repository import Adw, Gtk, Gio, GObject, GLib

from .message import WeegtkMessage
from .color import Color
from weegtk import config
from weegtk import netfile

import json

@Gtk.Template(resource_path='/com/fryzekconcepts/weegtk/gtk/chat.ui')
class WeegtkChat(Adw.Bin):
    __gtype_name__ = 'WeegtkChat'

    __gsignals__ = {
        "buffer_input" : (
            GObject.SignalFlags.RUN_FIRST,
            None,
            (str, str)
        )
    }

    window = Gtk.Template.Child()
    messages = Gtk.Template.Child()
    text_entry = Gtk.Template.Child()
    scroll_button_revealer = Gtk.Template.Child()

    def __init__(self, data=None, **kwargs):
        super().__init__(**kwargs)

        self.data = data or {}

        self.model = Gtk.StringList()
        self.select = Gtk.NoSelection(model=self.model)

        self.factory = Gtk.SignalListItemFactory()
        self.factory.connect("setup", self.setup_list_item)
        self.factory.connect("bind", self.bind_list_item)

        self.messages.set_model(self.select)
        self.messages.set_factory(self.factory)

        self.color = Color(config.color_options(), False)

        self.auto_scroll = True
        self.set_sticky(True)
        adj = self.window.get_vadjustment()
        adj.connect("value-changed", self.scroll_changes)
        adj.connect("notify::upper", self.upper_notify)

        # TODO figure out why style is not being taken from ui file
        self.add_css_class("view")

    def is_at_bottom(self):
        adj = self.window.get_vadjustment()
        return (adj.get_value() + adj.get_page_size()) == adj.get_upper()

    def set_sticky(self, is_sticky):
        if not is_sticky:
            self.scroll_button_revealer.set_visible(True)
        self.scroll_button_revealer.set_reveal_child(not is_sticky)
        self.sticky = is_sticky

    def scroll_changes(self, *args):
        is_at_bottom = self.is_at_bottom()
        if self.auto_scroll:
            if is_at_bottom:
                self.auto_scroll = False
                self.set_sticky(True)
            else:
                self.scroll_bottom()
        else:
            self.set_sticky(is_at_bottom)

    def upper_notify(self, *args):
        if self.sticky:
            self.scroll_bottom()

    @Gtk.Template.Callback()
    def scroll_bottom(self, *args):
        n_items = self.model.get_n_items()
        self.auto_scroll = True

        if n_items > 0:
            self.messages.scroll_to(n_items - 1, Gtk.ListScrollFlags.FOCUS)
            self.window.emit("scroll_child", Gtk.ScrollType.END, False)

    def setup_list_item(self, factory, list_item, *user_data):
        message = WeegtkMessage()
        list_item.set_child(message)
        pass

    def bind_list_item(self, factory, list_item, *user_data):
        text = list_item.get_item().get_string()
        data = json.loads(text)
        message = list_item.get_child()
        message.set_contents(data)

    def is_chat(self):
        buf_type = self.data['local_variables']['type'] if 'type' in self.data['local_variables'] else None
        return buf_type is not None and (buf_type == "private" or buf_type == "channel")

    def pointer(self):
        """Return pointer on buffer."""
        return self.data.get('__path', [''])[0]

    def parse_out_colors(self, text):
        clean_text = self.color.convert(text)
        return clean_text

    def display(self, time, prefix, text, forcecolor=None):
        # TODO using JSON here seems a bit ineffcient
        # See if its possible to make a model that uses
        # our own custom struct
        user = self.parse_out_colors(prefix)
        msg = self.parse_out_colors(text)
        count = self.model.get_n_items()
        msg_type = "message"

        # TODO figure out if there is a way to check if a message is from the
        # the system instead of from a user
        if len(user) == 0 or user[0] == "=" or user[0] == "-" or user[0] == "[":
            user = f"{self.data['short_name']} {user}"
            msg_type = "system"

        if count != 0:
            last = self.model.get_string(count - 1)
            last_data = json.loads(last)
            if last_data["username"] == user:
                last_data["text"].append(msg)
                self.model.splice(count - 1, 1, [json.dumps(last_data)])
                return

        data = {
            "username": user,
            "text": [msg],
            "type": msg_type
        }
        self.model.append(json.dumps(data))

    @Gtk.Template.Callback()
    def entry_activate(self, *args):
        entry_buffer = self.text_entry.get_buffer()
        text = entry_buffer.get_text()
        entry_buffer.set_text("", 0)
        self.emit("buffer_input", self.data['full_name'], text)
        
    def open_file_dialog(self, dialog, result, caller):
        try:
            file = dialog.open_finish(result)
        except GLib.GError:
            # gtk-dialog-error-quark: Dismissed by user
            pass
        else:
            conf = config.read()
            file_url = netfile.upload(file.get_path(), conf["upload"]["url"])
            entry_buffer = self.text_entry.get_buffer()
            entry_buffer.insert_text(self.text_entry.get_position(), file_url, -1)

    @Gtk.Template.Callback()
    def attach_file(self, *args):
        dialog = Gtk.FileDialog()
        dialog.set_title("Select file to upload")
        dialog.open(self.get_root(), None, self.open_file_dialog, self)