/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxbin.cpp,v 1.3.4.7 2004/10/28 22:28:39 rggammon Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include <memory.h>
#include <stdio.h>

#include <gtk/gtkdnd.h>
#include <gdk/gdkcolor.h>

#include "hxbin.h"
#include "hxplayer.h"

static void hx_bin_class_init   (HXBinClass*     klass);
static void hx_bin_init         (HXBin*          hxbin);
static void hx_bin_size_allocate(GtkWidget*      widget,
                                 GtkAllocation*  allocation);
static void hx_bin_drag_data_received
                                (GtkWidget*      widget,
                                 GdkDragContext* context,
                                 gint            x,
                                 gint            y,
                                 GtkSelectionData* selection_data,
                                 guint           info,
                                 guint           time);
static void hx_bin_realize      (GtkWidget*      widget);
static void hx_bin_add          (GtkContainer*   container,
                                 GtkWidget*      widget);
static void hx_bin_remove       (GtkContainer*   container,
                                 GtkWidget*      widget);

static GtkBinClass *g_parent_class = NULL;

GType
hx_bin_get_type (void)
{
    static GType hxbin_type = 0;

    if (!hxbin_type)
    {
        static const GTypeInfo hxbin_info =
            {
                sizeof (HXBinClass),
                NULL,           /* base_init */
                NULL,           /* base_finalize */
                (GClassInitFunc) hx_bin_class_init,
                NULL,           /* class_finalize */
                NULL,           /* class_data */
                sizeof (HXBin),
                0,              /* n_preallocs */
                (GInstanceInitFunc) hx_bin_init,
                NULL,           /* value_table */
            };

        hxbin_type = g_type_register_static (GTK_TYPE_BIN, "HXBin",
                                             &hxbin_info, (GTypeFlags)0);
    }

    return hxbin_type;
}

static void
hx_bin_class_init(HXBinClass* klass)
{
    GObjectClass *gobject_class;
    GtkWidgetClass *widget_class;
    GtkContainerClass *container_class;
        
    gobject_class = (GObjectClass*) klass;
    widget_class = GTK_WIDGET_CLASS (klass);
    container_class = GTK_CONTAINER_CLASS (klass);

    g_parent_class = (GtkBinClass*) g_type_class_peek_parent (klass);
    
    widget_class->realize = hx_bin_realize;
    widget_class->size_allocate = hx_bin_size_allocate;
    widget_class->drag_data_received = hx_bin_drag_data_received;

    container_class->add = hx_bin_add;
    container_class->remove = hx_bin_remove;
}

static void
hx_bin_init (HXBin* hxbin)
{
    GTK_WIDGET_UNSET_FLAGS (hxbin, GTK_NO_WINDOW);

    hxbin->maintain_aspect_ratio = TRUE;
    hxbin->stretch_to_fit = FALSE;
    hxbin->player_ideal_size_changed_handler = -1;
    memset(&hxbin->color, 0, sizeof(hxbin->color));
}

GtkWidget*
hx_bin_new (void)
{
    GtkWidget* widget;

    widget = (GtkWidget*) g_object_new (HX_TYPE_BIN, NULL);
    
    /* set up drag 'n drop */
    /* XXXRGG: Move this to an add function for this container */
    static const GtkTargetEntry targets[] =
    {
        { "text/uri-list", 0, 0 },            
    };
    
    gtk_drag_dest_set (GTK_WIDGET(widget),
                       GTK_DEST_DEFAULT_ALL,
                       targets,
                       sizeof(targets) / sizeof(*targets),
                       GDK_ACTION_COPY);
    return widget;
}

void
hx_bin_realize (GtkWidget* widget)
{
    GdkWindowAttr attributes;
    gint attributes_mask;

    g_return_if_fail(HX_IS_BIN(widget));

    HXBin* bin = HX_BIN(widget);
    
    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

    attributes.x = widget->allocation.x;
    attributes.y = widget->allocation.y;
    attributes.width = widget->allocation.width;
    attributes.height = widget->allocation.height;
    attributes.window_type = GDK_WINDOW_CHILD;
    attributes.wclass = GDK_INPUT_OUTPUT;
    attributes.visual = gtk_widget_get_visual (widget);
    attributes.colormap = gtk_widget_get_colormap (widget);
    attributes.event_mask = gtk_widget_get_events (widget) |
        GDK_EXPOSURE_MASK |
        GDK_BUTTON_PRESS_MASK | 
        GDK_BUTTON_RELEASE_MASK |
        GDK_POINTER_MOTION_MASK |
        GDK_POINTER_MOTION_HINT_MASK;    

    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
    gdk_window_set_user_data (widget->window, widget);

    widget->style = gtk_style_attach (widget->style, widget->window);

    gdk_colormap_alloc_color (gtk_widget_get_colormap (widget),
                              &bin->color, TRUE, TRUE);

    gdk_window_set_background (widget->window, &bin->color);
}

static void
hx_bin_size_allocate(GtkWidget*     widget,
                     GtkAllocation* alloc_bin)
{    
    GtkWidget *player = gtk_bin_get_child(GTK_BIN(widget));
    HXBin* hxbin = HX_BIN(widget);

    g_return_if_fail(hxbin != NULL);
    
    widget->allocation = *alloc_bin;

    if (GTK_WIDGET_REALIZED (widget))
    {
        gdk_window_move_resize (widget->window,
                                widget->allocation.x,
                                widget->allocation.y,
                                widget->allocation.width,
                                widget->allocation.height);
    }

    
    if(player)
    {
        GtkAllocation alloc_player;
        gint player_width, player_height;
        gint ideal_width, ideal_height;

        /* Calculate the player's aspect ratio from its ideal size */
        hx_player_get_ideal_size(HX_PLAYER(player),
                                 &ideal_width, &ideal_height);        

        if(ideal_width > 0 && ideal_height > 0)
        {
            gdouble player_aspect_ratio, bin_aspect_ratio;

            player_aspect_ratio = (gdouble) ideal_width / (gdouble) ideal_height;
            bin_aspect_ratio = (gdouble) alloc_bin->width / (gdouble) alloc_bin->height;
            
            if(!hxbin->stretch_to_fit &&
               ideal_width <= alloc_bin->width &&
               ideal_height <= alloc_bin->height)
            {
                /* There's space for the video */
                player_height = ideal_height;
                player_width = ideal_width;                
            }
            else
            {
                /* Stretch/shrink it to fit */
                if(hxbin->maintain_aspect_ratio)
                {        
                    /* If we're showing video, stretch the widget to fit.
                       What do we do if the ideal size of the video is smaller
                       than that of the fixed?
                       Right now, we stretch it. */
                    
                    if(bin_aspect_ratio > player_aspect_ratio)
                    {
                        /* fit to height */
                        player_height = alloc_bin->height;
                        player_width = (gint)(alloc_bin->height * player_aspect_ratio);
                    }
                    else
                    {
                        /* fit to width */
                        player_width = alloc_bin->width;
                        player_height = (gint)(alloc_bin->width / player_aspect_ratio);
                    }
                }
                else
                {
                    /* Don't maintain aspect ratio */
                    player_height = alloc_bin->height;
                    player_width = alloc_bin->width;
                }
            }
        }
        else
        {
            gint logo_width, logo_height;

            hx_player_get_logo_size(HX_PLAYER(player),
                                    &logo_width, &logo_height);        

            /* If we're showing the logo, use the ideal size. */
            player_width = logo_width; 
            player_height = logo_height;
        }

        alloc_player.width = player_width;
        alloc_player.height = player_height;
    
        alloc_player.x = (alloc_bin->width - alloc_player.width) / 2;
        alloc_player.y = (alloc_bin->height - alloc_player.height) / 2;
    
        if(memcmp(&alloc_player,
                  &GTK_WIDGET(player)->allocation,
                  sizeof(alloc_player)) != 0)
        {
            /* Allocation has changed -- update it */
            gtk_widget_size_allocate(GTK_WIDGET(player), &alloc_player);
        }
    }
}

static void
hx_bin_player_ideal_size_changed(GtkWidget* widget,
                                 gint /* width */,
                                 gint /* height */,
                                 GtkWidget* hxbin)
{
    g_return_if_fail(hxbin != NULL);
    
    if(GTK_WIDGET_REALIZED(hxbin))
    {
        /* Reallocate when switching from logo to vido and visa versa */
        hx_bin_size_allocate(hxbin, &hxbin->allocation);
    }
}

static void
hx_bin_add (GtkContainer*   container,
            GtkWidget*      widget)
{
    HXBin* hxbin;
    
    g_return_if_fail(HX_IS_BIN(container));
    g_return_if_fail(HX_IS_PLAYER(widget));

    hxbin = HX_BIN (container);
        
    g_return_if_fail(hxbin->player_ideal_size_changed_handler < 0);

    hxbin->player_ideal_size_changed_handler =
        g_signal_connect(G_OBJECT(widget), "ideal_size_changed",
                         G_CALLBACK(hx_bin_player_ideal_size_changed), hxbin);

    GTK_CONTAINER_CLASS(g_parent_class)->add(container, widget);
}

static void
hx_bin_remove (GtkContainer*   container,
               GtkWidget*      widget)
{
    HXBin* hxbin;
    GtkWidget* player;
    
    g_return_if_fail(HX_IS_BIN(container));

    hxbin = HX_BIN (container);
    g_return_if_fail(hxbin->player_ideal_size_changed_handler >= 0);

    player = gtk_bin_get_child(GTK_BIN(container));

    if(widget == player)
    {
        g_signal_handler_disconnect(G_OBJECT(player),
                                    hxbin->player_ideal_size_changed_handler);
    }
    
    hxbin->player_ideal_size_changed_handler = -1;

    GTK_CONTAINER_CLASS(g_parent_class)->remove(container, widget);
}

/**
 * hx_bin_drag_data_received:
 * @widget: the player's #GtkWidget
 * @context: the #GdkDragContext
 * @x: the x coordinate of the drop
 * @y: the y coordinate of the drop
 * @info:
 * @time:
 * @data:
 *
 * Handle a drag_data_received signal
 *
 **/
static void
hx_bin_drag_data_received (GtkWidget*        widget,
                           GdkDragContext*   /* context */,
                           gint              /* x */,
                           gint              /* y */,
                           GtkSelectionData* selection_data,
                           guint             /* info */,
                           guint             /* time */)
{
    GtkWidget* player = gtk_bin_get_child(GTK_BIN(widget));

    g_return_if_fail(selection_data != NULL);
    g_return_if_fail(selection_data->length >= 0);
    g_return_if_fail(widget != NULL);

    /* XXXRGG: we need to parse this into a playlist if
       multiple files are being dropped */
    gchar** pURLs = g_strsplit((gchar*)selection_data->data, "\r\n", 0xffff);
    if (pURLs)
    {
	// XXXNH: for now just use the first URL, later we'll turn them into
	// a playlist
	hx_player_open_url(HX_PLAYER(player), pURLs[0]);
	hx_player_play(HX_PLAYER(player));

	// cleanup
	g_strfreev(pURLs);
    }
}

void
hx_bin_maintain_aspect_ratio(HXBin*   bin,
                             gboolean enable)
{
    g_return_if_fail(HX_IS_BIN(bin));

    bin->maintain_aspect_ratio = enable;

    hx_bin_size_allocate(GTK_WIDGET(bin), &GTK_WIDGET(bin)->allocation);
}

void
hx_bin_stretch_to_fit(HXBin*   bin,
                      gboolean enable)
{
    g_return_if_fail(HX_IS_BIN(bin));

    bin->stretch_to_fit = enable;

    hx_bin_size_allocate(GTK_WIDGET(bin), &GTK_WIDGET(bin)->allocation);
}

void
hx_bin_set_bg_color(HXBin*    bin,
                    GdkColor* color)
{
    g_return_if_fail(HX_IS_BIN(bin));

    memcpy(&bin->color, color, sizeof(bin->color));

    if(GTK_WIDGET_REALIZED(bin))
    {
        GtkWidget* widget = GTK_WIDGET(bin);
        gdk_colormap_alloc_color (gtk_widget_get_colormap (widget),
                                  color, TRUE, TRUE);
        gdk_window_set_background (GTK_WIDGET(widget)->window, color);
    }
}
