using Gee;
using Gtk;

using Dino.Entities;
using Xmpp;

namespace Dino.Ui.ConversationSummary {

public class ReactionsController : Object {
    public signal void box_activated(Widget? widget);

    private Conversation conversation;
    private Account account;
    private ContentItem content_item;
    private StreamInteractor stream_interactor;

    private HashMap<string, Gee.List<Jid>> reactions = new HashMap<string, Gee.List<Jid>>();

    private WeakRef widget = WeakRef(null);

    public ReactionsController(Conversation conversation, ContentItem content_item, StreamInteractor stream_interactor) {
        this.conversation = conversation;
        this.account = conversation.account;
        this.content_item = content_item;
        this.stream_interactor = stream_interactor;
    }

    public void init() {
        Gee.List<ReactionUsers> reactions = stream_interactor.get_module(Reactions.IDENTITY).get_item_reactions(conversation, content_item);
        foreach (ReactionUsers reaction_users in reactions) {
            foreach (Jid jid in reaction_users.jids) {
                reaction_added(reaction_users.reaction, jid);
            }
        }

        stream_interactor.get_module(Reactions.IDENTITY).reaction_added.connect((account, content_item_id, jid, reaction) => {
            if (this.content_item.id == content_item_id) {
                reaction_added(reaction, jid);
            }
        });
        stream_interactor.get_module(Reactions.IDENTITY).reaction_removed.connect((account, content_item_id, jid, reaction) => {
            if (this.content_item.id == content_item_id) {
                reaction_removed(reaction, jid);
            }
        });
    }

    private ReactionsWidget initialize_widget() {
        var widget = new ReactionsWidget();
        this.widget.set(widget);
        widget.emoji_picked.connect((emoji) => {
            stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji);
        });
        widget.emoji_clicked.connect((emoji) => {
            if (account.bare_jid in reactions[emoji]) {
                stream_interactor.get_module(Reactions.IDENTITY).remove_reaction(conversation, content_item, emoji);
            } else {
                stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji);
            }
        });
        box_activated(widget);
        return widget;
    }

    public void reaction_added(string reaction, Jid jid) {
        var widget = this.widget.get() as ReactionsWidget;
        if (widget == null) {
            widget = initialize_widget();
        }

        if (!reactions.has_key(reaction)) {
            reactions[reaction] = new ArrayList<Jid>(Jid.equals_func);
        }
        if (jid.equals_bare(account.bare_jid) && reactions[reaction].contains(jid)) {
            return;
        }
        reactions[reaction].add(jid);

        if (reactions[reaction].size == 0) return;

        widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction));
    }

    public void reaction_removed(string reaction, Jid jid) {
        var widget = this.widget.get() as ReactionsWidget;
        if (!reactions.has_key(reaction)) return;
        reactions[reaction].remove(jid);

        if (reactions[reaction].size > 0) {
            widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction));
        } else {
            widget.remove_reaction(reaction);
            reactions.unset(reaction);
        }

        if (reactions.size == 0) {
            box_activated(null);
            this.widget.set(null);
        }
    }

    private Gee.List<string> update_tooltip(string reaction) {
        var name_list = new ArrayList<string>();
        if (reactions[reaction].size > 0) {
            if (account.bare_jid in reactions[reaction]) {
                name_list.add(_("You"));
            }
            foreach (Jid jid in reactions[reaction]) {
                if (jid.equals(account.bare_jid)) continue;

                name_list.add(Util.get_participant_display_name(stream_interactor, conversation, jid));
            }
        }
        return name_list;
    }
}

private const string REACTIONS_BUTTON_REACTION = "dino_reactions_button_reaction";
public class ReactionsWidget : Grid {

    public signal void emoji_picked(string emoji);
    public signal void emoji_clicked(string emoji);

    private HashMap<string, weak Label> reaction_counts = new HashMap<string, weak Label>();
    private HashMap<string, weak Button> reaction_buttons = new HashMap<string, weak Button>();
    private unowned MenuButton add_button;

    construct {
        check_widget_leak(this);
    }

    public ReactionsWidget() {
        this.row_spacing = this.column_spacing = 5;
        this.margin_top = 2;
        this.add_css_class("reaction-grid");
    }

    private MenuButton create_add_button() {
        MenuButton add_button = new MenuButton() { tooltip_text= _("Add reaction") };
        check_widget_leak(add_button);
        add_button.add_css_class("pill");
        Util.menu_button_set_icon_with_size(add_button, "dino-emoticon-add-symbolic", 14);

        EmojiChooser chooser = new EmojiChooser();
        chooser.emoji_picked.connect((emoji) => {
            emoji_picked(emoji);
        });
        add_button.set_popover(chooser);
        return add_button;
    }

    private static void on_reactions_button_clicked(Button button) {
        ReactionsWidget widget = button.parent as ReactionsWidget;
        if (widget != null) {
            widget.emoji_clicked(button.get_data(REACTIONS_BUTTON_REACTION));
        } else {
            warning("Reaction button was not in ReactionsWidget");
        }
    }

    public void update_reaction(string reaction, int count, bool own, Gee.List<string> names) {
        if (!reaction_buttons.has_key(reaction)) {
            Label reaction_label = new Label("<span size='small'>" + reaction + "</span>") { use_markup=true };
            Label count_label = new Label("") { use_markup=true };
            Button button = new Button();
            check_widget_leak(button);
            button.add_css_class("pill");
            Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { halign=Align.CENTER };
            reaction_box.append(reaction_label);
            reaction_box.append(count_label);
            button.set_child(reaction_box);

            reaction_counts[reaction] = count_label;
            reaction_buttons[reaction] = button;

            this.attach(button, (reaction_buttons.size - 1) % 10, (reaction_buttons.size - 1) / 10, 1, 1);
            MenuButton add_button = this.add_button;
            if (add_button == null) {
                add_button = create_add_button();
                this.add_button = add_button;
            } else {
                this.remove(add_button);
            }
            this.attach(add_button, reaction_buttons.size % 10, reaction_buttons.size / 10, 1, 1);
            button.set_data(REACTIONS_BUTTON_REACTION, reaction);
            button.clicked.connect(on_reactions_button_clicked);
        }

        reaction_counts[reaction].label = "<span font_family='monospace' size='small'>" + count.to_string() + "</span>";
        if (own) {
            reaction_buttons[reaction].add_css_class("own-reaction");
        } else {
            reaction_buttons[reaction].remove_css_class("own-reaction");
        }

        // Build tooltip
        StringBuilder tooltip_builder = new StringBuilder ();
        for (int i = 0; i < names.size - 1; i++) {
            tooltip_builder.append(names[i]);
            if (i < names.size - 2) tooltip_builder.append(", ");
        }
        if (names.size > 1) {
            tooltip_builder.append(" and ");
        }
        tooltip_builder.append(names[names.size - 1]);
        tooltip_builder.append(" reacted with " + reaction);
        reaction_buttons[reaction].set_tooltip_text(tooltip_builder.str);
    }

    public void remove_reaction(string reaction) {
        reaction_buttons[reaction].unparent();
    }
}

}