/*
 * * Copyright (C) 2009-2011 Ali <aliov@xfce.org>
 * * Copyright (C) 2012-2017 Simon Steinbeiß <ochosi@xfce.org>
 * * Copyright (C) 2012-2020 Sean Davis <bluesabre@xfce.org>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "src/dbus/parole-dbus.h"

#include "src/gst/parole-gst.h"

#include "src/misc/parole-provider-player.h"

#include "src/parole-medialist.h"
#include "src/parole-plugin-player.h"
#include "src/parole-plugins-manager.h"


static void parole_plugin_player_iface_init(ParoleProviderPlayerIface *iface);

static void parole_plugin_player_finalize(GObject *object);

struct ParolePluginPlayerPrivate {
    GtkWidget *gst;
    GtkWidget *box;

    gulong window_state_changed;
    gulong state_changed;
    gulong tag_message;
    gulong seeked;
    gboolean packed;

    gboolean fullscreen;
};

G_DEFINE_TYPE_WITH_CODE(ParolePluginPlayer, parole_plugin_player, G_TYPE_OBJECT,
                        G_ADD_PRIVATE(ParolePluginPlayer)
                        G_IMPLEMENT_INTERFACE(PAROLE_TYPE_PROVIDER_PLAYER,
                                              parole_plugin_player_iface_init))

static void
parole_plugin_player_send_message(const gchar *message) {
    DBusGProxy *proxy;

    proxy = parole_get_proxy(PAROLE_DBUS_PATH, PAROLE_DBUS_INTERFACE);

    dbus_g_proxy_call_no_reply(proxy, message,
                                G_TYPE_INVALID,
                                G_TYPE_INVALID);

    g_object_unref(proxy);
}

static GtkWidget *
parole_plugin_player_get_main_window(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);
    if (GTK_IS_WIDGET (player->priv->gst)) {
        return gtk_widget_get_toplevel (player->priv->gst);
    }
    return NULL;
}

static void
parole_plugin_player_pack_widget(ParoleProviderPlayer *provider, GtkWidget *widget,
                                 const gchar *title, ParolePluginContainer container_type) {
    ParolePluginPlayer *player;
    ParolePluginsManager *manager;

    manager = parole_plugins_manager_get();
    player = PAROLE_PLUGIN_PLAYER(provider);

    g_return_if_fail(player->priv->packed == FALSE);

    player->priv->packed = TRUE;
    player->priv->box = widget;

    parole_plugins_manager_pack(manager, widget, title, container_type);
}

static ParoleState
parole_plugin_player_get_state(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    return parole_gst_get_state (PAROLE_GST (player->priv->gst));
}

static const ParoleStream *
parole_plugin_player_get_stream(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    return parole_gst_get_stream (PAROLE_GST (player->priv->gst));
}

static gboolean
parole_plugin_player_play_uri(ParoleProviderPlayer *provider, const gchar *uri) {
    ParoleMediaList *list;

    list = PAROLE_MEDIA_LIST(parole_media_list_get());
    parole_media_list_open_uri(list, uri);

    g_object_unref(list);

    return TRUE;
}

static gboolean
parole_plugin_player_pause(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    parole_gst_pause(PAROLE_GST(player->priv->gst));

    return TRUE;
}

static gboolean
parole_plugin_player_resume(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    parole_gst_resume(PAROLE_GST(player->priv->gst));

    return TRUE;
}

static gboolean
parole_plugin_player_stop(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    parole_gst_stop(PAROLE_GST(player->priv->gst));

    return TRUE;
}

static gboolean
parole_plugin_player_play_previous(ParoleProviderPlayer *provider) {
    parole_plugin_player_send_message("PrevTrack");

    return TRUE;
}

static gboolean
parole_plugin_player_play_next(ParoleProviderPlayer *provider) {
    parole_plugin_player_send_message("NextTrack");

    return TRUE;
}

static gboolean
parole_plugin_player_seek(ParoleProviderPlayer *provider, gdouble pos) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    parole_gst_seek(PAROLE_GST(player->priv->gst), pos);

    return TRUE;
}

static gdouble
parole_plugin_player_get_stream_position(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;
    gdouble position = 0;

    player = PAROLE_PLUGIN_PLAYER(provider);

    position = parole_gst_get_stream_position(PAROLE_GST(player->priv->gst));

    /* Return as microseconds */
    return position*1000000.0;
}

static void parole_plugin_player_open_media_chooser(ParoleProviderPlayer *provider) {
    ParoleMediaList *list;

    list = PAROLE_MEDIA_LIST(parole_media_list_get());
    parole_media_list_open(list);

    g_object_unref(list);
}

static GSimpleAction* parole_plugin_player_get_action(ParoleProviderPlayer *provider, ParolePlayerAction action) {
    return parole_player_get_action (action);
}

static gboolean parole_plugin_player_get_fullscreen(ParoleProviderPlayer *provider) {
    ParolePluginPlayer *player;

    player = PAROLE_PLUGIN_PLAYER(provider);

    return player->priv->fullscreen;
}

static gboolean parole_plugin_player_set_fullscreen(ParoleProviderPlayer *provider, gboolean fullscreen) {
    GtkWidget *window;

    window = parole_plugin_player_get_main_window(provider);

    if (fullscreen)
        gtk_window_fullscreen(GTK_WINDOW(window));
    else
        gtk_window_unfullscreen(GTK_WINDOW(window));

    return TRUE;
}

static void parole_plugin_player_iface_init(ParoleProviderPlayerIface *iface) {
    iface->get_main_window = parole_plugin_player_get_main_window;
    iface->pack = parole_plugin_player_pack_widget;
    iface->get_state = parole_plugin_player_get_state;
    iface->get_stream = parole_plugin_player_get_stream;
    iface->play_uri = parole_plugin_player_play_uri;
    iface->pause = parole_plugin_player_pause;
    iface->resume = parole_plugin_player_resume;
    iface->stop = parole_plugin_player_stop;
    iface->play_previous = parole_plugin_player_play_previous;
    iface->play_next = parole_plugin_player_play_next;
    iface->seek = parole_plugin_player_seek;
    iface->get_stream_position = parole_plugin_player_get_stream_position;
    iface->open_media_chooser = parole_plugin_player_open_media_chooser;
    iface->get_action = parole_plugin_player_get_action;
    iface->get_fullscreen = parole_plugin_player_get_fullscreen;
    iface->set_fullscreen = parole_plugin_player_set_fullscreen;
}

static void
parole_plugin_player_media_state_changed_cb(ParoleGst *gst, const ParoleStream *stream,
                                            ParoleState state, ParolePluginPlayer *player) {
    g_signal_emit_by_name(G_OBJECT(player), "state-changed", stream, state);
}

static void
parole_plugin_player_media_tag_cb(ParoleGst *gst, const ParoleStream *stream, ParolePluginPlayer *player) {
    g_signal_emit_by_name(G_OBJECT(player), "tag-message", stream);
}

static void
parole_plugin_player_media_seeked_cb(ParoleGst *gst, ParolePluginPlayer *player) {
    g_signal_emit_by_name(G_OBJECT(player), "seeked");
}

static gboolean
parole_plugin_player_window_state_event(GtkWidget *widget,
                                        GdkEventWindowState *event,
                                        ParolePluginPlayer *player) {
    if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
        player->priv->fullscreen = TRUE;
    else
        player->priv->fullscreen = FALSE;
    return FALSE;
}

static void
parole_plugin_player_class_init(ParolePluginPlayerClass *klass) {
    GObjectClass *object_class = G_OBJECT_CLASS(klass);

    object_class->finalize = parole_plugin_player_finalize;
}

static void
parole_plugin_player_init(ParolePluginPlayer *player) {
    GtkWidget *window;
    player->priv = parole_plugin_player_get_instance_private(player);

    player->priv->gst = parole_gst_get();

    player->priv->state_changed = g_signal_connect(G_OBJECT(player->priv->gst), "media-state",
                        G_CALLBACK(parole_plugin_player_media_state_changed_cb), player);

    player->priv->tag_message = g_signal_connect(G_OBJECT(player->priv->gst), "media-tag",
                        G_CALLBACK(parole_plugin_player_media_tag_cb), player);

    player->priv->seeked = g_signal_connect(G_OBJECT(player->priv->gst), "media-seeked",
                        G_CALLBACK(parole_plugin_player_media_seeked_cb), player);

    player->priv->fullscreen = FALSE;
    window = GTK_WIDGET(gtk_widget_get_toplevel(player->priv->gst));
    player->priv->window_state_changed = g_signal_connect(G_OBJECT(window),
                                                          "window-state-event",
                                                          G_CALLBACK(parole_plugin_player_window_state_event),
                                                          player);

    player->priv->packed = FALSE;
    player->priv->box = NULL;
}

static void
parole_plugin_player_finalize(GObject *object) {
    ParolePluginPlayer *player;
    GtkWidget          *window;

    player = PAROLE_PLUGIN_PLAYER(object);

    if (G_IS_OBJECT(player->priv->gst)) {
        if (g_signal_handler_is_connected (player->priv->gst, player->priv->state_changed))
            g_signal_handler_disconnect(player->priv->gst, player->priv->state_changed);

        if (g_signal_handler_is_connected (player->priv->gst, player->priv->tag_message))
            g_signal_handler_disconnect(player->priv->gst, player->priv->tag_message);

        if (g_signal_handler_is_connected (player->priv->gst, player->priv->seeked))
            g_signal_handler_disconnect(player->priv->gst, player->priv->seeked);

        if (GTK_IS_WIDGET (player->priv->gst)) {
            window = GTK_WIDGET(gtk_widget_get_toplevel(player->priv->gst));
            if (g_signal_handler_is_connected(window, player->priv->window_state_changed))
                g_signal_handler_disconnect(window, player->priv->window_state_changed);
        }
    }

    if ( player->priv->packed && GTK_IS_WIDGET (player->priv->box))
        gtk_widget_destroy(player->priv->box);

    G_OBJECT_CLASS(parole_plugin_player_parent_class)->finalize(object);
}

ParolePluginPlayer *
parole_plugin_player_new(void) {
    ParolePluginPlayer *player = NULL;

    player = g_object_new(PAROLE_TYPE_PLUGIN_PLAYER, NULL);

    return player;
}
