from giara.constants import RESOURCE_PREFIX
from gettext import gettext as _
from gi.repository import Gtk, Gdk, GtkSource, GObject, GLib
from os.path import isfile
from giara.path_utils import is_image, is_video
from giara.image_utils import make_video_thumb
from giara.subreddits_list_view import SubredditsListbox
from giara.choice_picker import ChoicePickerButton
from giara.flair_label import FlairLabel
from giara.confManager import ConfManager
from praw.models import Submission
from threading import Thread


class CommonNewEntityWindow(Gtk.Window):
    __gsignals__ = {
        'file-chosen': (
            GObject.SignalFlags.RUN_LAST,
            None,
            (str,)
        )
    }

    def __init__(self, widgets_to_hide, widgets_to_show, **kwargs):
        super().__init__(**kwargs)
        self.set_modal(True)
        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        # self.set_size_request(340, 500)

        self.confman = ConfManager()

        # Why this -52?
        # because every time a new value is saved, for some reason
        # it's the actual value +52 out of nowhere
        # this makes the window ACTUALLY preserve its old size
        self.resize(
            self.confman.conf['newentity_windowsize']['width']-52,
            self.confman.conf['newentity_windowsize']['height']-52
        )
        print(self.confman.conf['newentity_windowsize'])
        self.size_allocation = self.get_allocation()

        def update_size_allocation(*args):
            self.size_allocation = self.get_allocation()

        self.connect('size-allocate', update_size_allocation)

        self.builder = Gtk.Builder.new_from_resource(
            f'{RESOURCE_PREFIX}/ui/new_post_window.ui'
        )

        self.title_entry = self.builder.get_object('title_entry')
        self.link_entry = self.builder.get_object('link_entry')
        self.text_source_view_container = self.builder.get_object(
            'text_source_view_container'
        )
        self.choice_picker_container = self.builder.get_object(
            'choice_picker_container'
        )

        self.headerbar = self.builder.get_object('headerbar')
        self.set_titlebar(self.headerbar)
        self.add(self.builder.get_object('inner_box'))

        self.widgets_to_hide = widgets_to_hide
        self.widgets_to_show = widgets_to_show

        for w in self.widgets_to_hide:
            w = self.builder.get_object(w)
            w.set_visible(False)
            w.set_no_show_all(True)
        for w in self.widgets_to_show:
            w = self.builder.get_object(w)
            w.set_visible(True)
            w.set_no_show_all(False)

        self.source_buffer = None
        if 'text_source_view_container' in self.widgets_to_show:
            source_style_scheme_manager = \
                GtkSource.StyleSchemeManager.get_default()
            source_lang_manager = GtkSource.LanguageManager.get_default()
            source_lang_md = source_lang_manager.get_language('markdown')
            self.source_buffer = GtkSource.Buffer()
            color_scheme = 'oblivion'
            self.source_buffer.set_style_scheme(
                source_style_scheme_manager.get_scheme(color_scheme)
            )
            self.source_buffer.set_language(source_lang_md)
            source_view = self.builder.get_object('source_view')
            source_view.set_buffer(self.source_buffer)
            self.source_buffer.connect('changed', self.on_input)

        self.file_chooser_btn = self.builder.get_object('file_chooser_btn')
        self.file_chooser_label = self.builder.get_object('file_chooser_label')
        self.file_chooser_dialog = None
        if 'file_chooser_btn' in self.widgets_to_show:

            def on_file_chooser_deployed(*args):
                self.file_chooser_dialog = Gtk.FileChooserNative.new(
                    _('Choose an image or video to upload'),
                    self,
                    Gtk.FileChooserAction.OPEN,
                    None, None
                )
                self.file_chooser_dialog.set_filter(self.builder.get_object(
                    'filefilter1'
                ))
                self.file_chooser_dialog.run()
                filename = self.file_chooser_dialog.get_filename()
                if filename:
                    self.file_chooser_label.set_text(
                        filename.split('/')[-1]
                    )
                    self.emit('file-chosen', '')

            self.file_chooser_btn.connect('clicked', on_file_chooser_deployed)

        self.cancel_btn = self.builder.get_object('cancel_btn')
        self.cancel_btn.connect('clicked', lambda *args: self.destroy())
        self.send_btn = self.builder.get_object('send_btn')
        self.send_btn.set_sensitive(False)
        self.send_btn.connect('clicked', self.on_send)

        for entry in (
                self.title_entry,
                self.link_entry
        ):
            entry.connect('changed', self.on_input)
        self.connect(
            'file-chosen',
            self.on_input
        )

        self.accel_group = Gtk.AccelGroup()
        self.accel_group.connect(
            *Gtk.accelerator_parse('Escape'), Gtk.AccelFlags.VISIBLE,
            lambda *args: self.close()
        )
        self.add_accel_group(self.accel_group)

    def destroy(self, *args, **kwargs):
        self.on_destroy()
        super().destroy(*args, **kwargs)

    def on_destroy(self, *args):
        self.confman.conf['newentity_windowsize'] = {
            'width': self.size_allocation.width,
            'height': self.size_allocation.height
        }
        self.hide()
        self.confman.save_conf()

    def on_send(self, *args):
        raise NotImplementedError('on_send not implemented')

    def on_input(self, *args):
        raise NotImplementedError('on_input not implemented')

    def get_sourcebuffer_text(self):
        return self.source_buffer.get_text(
            self.source_buffer.get_start_iter(),
            self.source_buffer.get_end_iter(),
            True
        ).strip() if self.source_buffer is not None else ''


class SubredditChoiceListbox(SubredditsListbox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, load_now=True, sort=True, **kwargs)
        self.set_selection_mode(Gtk.SelectionMode.SINGLE)
        self.set_filter_func(self.subs_only_filter_func, None, False)

    def subs_only_filter_func(self, row, data, notify_destroy):
        return 'u/' not in row.subreddit.display_name_prefixed


class FlairChoiceListboxRow(Gtk.ListBoxRow):
    def __init__(self, text, bg, fg, flair_id, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.flair_id = flair_id
        fl = FlairLabel(text, bg, fg)
        fl.set_halign(Gtk.Align.CENTER)
        fl.set_margin_top(12)
        fl.set_margin_bottom(12)
        fl.set_margin_start(12)
        fl.set_margin_end(12)
        self.add(fl)
        self.title = text

    def get_key(self):
        return self.title.lower()


class FlairChoiceListbox(Gtk.ListBox):
    def __init__(self, subreddit, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_selection_mode(Gtk.SelectionMode.SINGLE)
        self.set_header_func(self.separator_header_func)
        self.set_sort_func(self.flair_sort_func, None, False)
        self.subreddit = subreddit
        self.populate()

    def flair_sort_func(self, row1, row2, data, notify_destroy):
        if row1.flair_id is None:
            return False
        if row2.flair_id is None:
            return True
        return row1.title.lower() > row2.title.lower()

    def populate(self):
        nonerow = Gtk.ListBoxRow()
        nonerow.get_key = lambda *args: _('None').lower()
        nonelbl = Gtk.Label(_('None'))
        nonelbl.set_margin_top(12)
        nonelbl.set_margin_bottom(12)
        nonelbl.set_margin_start(12)
        nonelbl.set_margin_end(12)
        nonerow.add(nonelbl)
        nonerow.title = _('None')
        nonerow.flair_id = None
        self.add(nonerow)
        for lt in self.subreddit.flair.link_templates:
            self.add(FlairChoiceListboxRow(
                lt['text'], lt['background_color'], lt['text_color'], lt['id']
            ))

    def separator_header_func(self, row, prev_row):
        if (
            prev_row is not None and
            row.get_header() is None
        ):
            row.set_header(
                Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
            )


class NewPostWindow(CommonNewEntityWindow):
    def __init__(self, reddit, post_type, callback=None, **kwargs):
        hide = []
        show = []
        self.post_type = post_type
        if self.post_type == 'text':
            hide = ['link_entry', 'file_chooser_btn']
            show = ['text_source_view_container']
        elif self.post_type == 'link':
            hide = ['file_chooser_btn', 'text_source_view_container']
            show = ['link_entry']
        else:  # media
            hide = ['text_source_view_container', 'link_entry']
            show = ['file_chooser_btn']
        show.append('choice_picker_container')
        super().__init__(hide, show, **kwargs)
        self.callback = callback
        assert (
            self.post_type in ('text', 'link', 'media')
        ), 'Post type can only be text, link or media'

        self.subreddits_picker_listbox = SubredditChoiceListbox(
            reddit.user.subreddits
        )
        self.picker = ChoicePickerButton(
            self.subreddits_picker_listbox, _('Select a subreddit…')
        )
        self.selected_subreddit = None
        self.flair_picker = None
        self.picker.connect('changed', self.on_picker_changed)
        self.choice_picker_container.add(self.picker)
        self.show_all()

    def on_send(self, *args):
        self.set_sensitive(False)
        subreddit = self.get_selected_subreddit()
        title = self.title_entry.get_text().strip()

        def af():
            if self.post_type in ('text', 'link'):
                subreddit.submit(
                    title=title,
                    selftext=(
                        self.get_sourcebuffer_text() if self.post_type == 'text'
                        else None
                    ),
                    url=(
                        self.link_entry.get_text().strip()
                        if self.post_type == 'link'
                        else None
                    ),
                    flair_id=self.get_selected_flair_id()
                )
            else:
                media = self.get_selected_media()
                if is_image(media):
                    subreddit.submit_image(
                        title=title,
                        image_path=media
                    )
                elif is_video(media):
                    subreddit.submit_video(
                        title=title,
                        video_path=media,
                        thumbnail_path=make_video_thumb(media)
                    )
            GLib.idle_add(cb)

        def cb():
            # TODO: verify that post has been sent before closing, else show error
            if self.callback is not None:
                self.callback()
            self.destroy()

        Thread(target=af).start()

    def on_picker_changed(self, *args):
        n_sub = self.get_selected_subreddit()
        if n_sub != self.selected_subreddit and n_sub is not None:
            if self.flair_picker is not None:
                self.choice_picker_container.remove(self.flair_picker)
            self.selected_subreddit = n_sub
            try:
                flair_picker_listbox = FlairChoiceListbox(
                    self.selected_subreddit
                )
                self.flair_picker = ChoicePickerButton(
                    flair_picker_listbox, default_title=_('Select a flair…')
                )
                self.choice_picker_container.add(self.flair_picker)
                self.flair_picker.show_all()
            except Exception:
                print(
                    'Error getting flairs for subreddit',
                    f'`{self.selected_subreddit}`'
                )
        self.on_input()

    def on_input(self, *args):
        self.send_btn.set_sensitive(
            self.get_selected_subreddit() is not None and
            len(self.title_entry.get_text().strip()) > 0 and
            (
                self.post_type != 'text' or
                len(self.get_sourcebuffer_text()) > 0
            ) and
            (
                self.post_type != 'link' or
                len(self.link_entry.get_text().strip()) > 0
            ) and
            (
                self.post_type != 'media' or
                (self.validate_selected_media())
            )
        )

    def get_selected_media(self):
        return (
            self.file_chooser_dialog.get_filename()
            if self.file_chooser_dialog is not None else None
        )

    def validate_selected_media(self):
        media = self.get_selected_media()
        return media is not None and isfile(media) and (
            is_image(media) or is_video(media)
        )

    def get_selected_subreddit(self):
        choice = self.picker.get_choice()
        return choice.subreddit if choice is not None else None

    def get_selected_flair_id(self):
        choice = self.flair_picker.get_choice()
        return (
            choice.flair_id
            if self.flair_picker is not None else None
        ) if choice is not None else None


class NewCommentWindow(CommonNewEntityWindow):
    def __init__(self, parent, callback=None, **kwargs):
        super().__init__(
            [
                'link_entry', 'file_chooser_btn',
                'choice_picker_container', 'title_entry'
            ],
            ['text_source_view_container'],
            **kwargs
        )
        self.parent = parent
        self.callback = callback
        self.headerbar.set_title(_('New comment'))

    def on_send(self, *args):
        self.set_sensitive(False)

        def af():
            self.parent.reply(body=self.get_sourcebuffer_text())
            GLib.idle_add(cb)

        def cb():
            if self.callback is not None:
                self.callback()
            self.destroy()

        Thread(target=af).start()

    def on_input(self, *args):
        self.send_btn.set_sensitive(
            len(self.get_sourcebuffer_text()) > 0
        )


class EditWindow(NewCommentWindow):
    def __init__(self, entity, callback=None, **kwargs):
        super().__init__(
            entity,
            callback,
            **kwargs
        )
        self.headerbar.set_title(_('Editing'))
        self.send_btn.set_label(_('Edit'))
        self.source_buffer.set_text(
            entity.selftext if isinstance(entity, Submission)
            else entity.body
        )

    def on_send(self, *args):
        # NOTE: parent == entity
        self.set_sensitive(False)

        def af():
            self.parent.edit(body=self.get_sourcebuffer_text())
            GLib.idle_add(cb)

        def cb():
            if self.callback is not None:
                self.callback()
            self.destroy()

        Thread(target=af).start()
