/*
 * Copyright (C) 2009 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) version 3.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "mux-frame.h"
#include <math.h>

static GdkColor mux_frame_default_border_color = { 0, 0xdddd, 0xe2e2, 0xe5e5 };
static GdkColor mux_frame_default_bullet_color = { 0, 0xaaaa, 0xaaaa, 0xaaaa };
static gfloat mux_frame_bullet_size_factor = 1.3;
#define MUX_FRAME_BULLET_PADDING 10

static void mux_frame_buildable_init                (GtkBuildableIface *iface);
static void mux_frame_buildable_add_child           (GtkBuildable *buildable,
                                                     GtkBuilder   *builder,
                                                     GObject      *child,
                                                     const gchar  *type);

G_DEFINE_TYPE_WITH_CODE (MuxFrame, mux_frame, GTK_TYPE_FRAME,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, mux_frame_buildable_init))

static void
mux_frame_dispose (GObject *object)
{
    G_OBJECT_CLASS (mux_frame_parent_class)->dispose (object);
}

static void
mux_frame_finalize (GObject *object)
{
      G_OBJECT_CLASS (mux_frame_parent_class)->finalize (object);
}

static void
label_changed_cb (MuxFrame *frame)
{
    char *font = NULL;
    GtkWidget *label = gtk_frame_get_label_widget (GTK_FRAME (frame));

    if (!label)
        return;

    /* ensure font is correct */
    gtk_widget_style_get (GTK_WIDGET (frame),
                          "title-font", &font,
                          NULL);
    if (font) {
        PangoFontDescription *desc;
        desc = pango_font_description_from_string (font);
        gtk_widget_modify_font (label, desc);
        pango_font_description_free (desc);
        g_free (font);
    }

    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
}

static void
mux_frame_update_style (MuxFrame *frame)
{
    GdkColor *border_color, *bullet_color;

    gtk_widget_style_get (GTK_WIDGET (frame),
                          "border-color", &border_color, 
                          "bullet-color", &bullet_color,
                          NULL);

    if (border_color) {
        frame->border_color = *border_color;
        gdk_color_free (border_color);
    } else {
        frame->border_color = mux_frame_default_border_color;
    }
    if (bullet_color) {
        frame->bullet_color = *bullet_color;
        gdk_color_free (bullet_color);
    } else {
        frame->bullet_color = mux_frame_default_bullet_color;
    }

    label_changed_cb (frame);
}

static void
rounded_rectangle (cairo_t * cr,
                   double x, double y, double w, double h,
                   guint radius)
{
    if (radius > w / 2)
        radius = w / 2;
    if (radius > h / 2)
        radius = h / 2;

    cairo_move_to (cr, x + radius, y);
    cairo_arc (cr, x + w - radius, y + radius, radius, M_PI * 1.5, M_PI * 2);
    cairo_arc (cr, x + w - radius, y + h - radius, radius, 0, M_PI * 0.5);
    cairo_arc (cr, x + radius, y + h - radius, radius, M_PI * 0.5, M_PI);
    cairo_arc (cr, x + radius, y + radius, radius, M_PI, M_PI * 1.5);
}

static void
mux_frame_paint (GtkWidget *widget, cairo_t *cairo)
{
    MuxFrame *frame = MUX_FRAME (widget);
    GtkStyle *style;
    guint width;
    GtkAllocation allocation;

    g_return_if_fail (widget != NULL);
    g_return_if_fail (MUX_IS_FRAME (widget));

    style = gtk_widget_get_style (widget);
    width = gtk_container_get_border_width (GTK_CONTAINER (widget));
    gtk_widget_get_allocation (widget, &allocation);

    /* draw border */
    if (width != 0) {
        gdk_cairo_set_source_color (cairo, &frame->border_color);
        
        rounded_rectangle (cairo,
                           0, 0,
                           allocation.width, allocation.height,
                           width);
        cairo_clip (cairo);

        cairo_paint (cairo);
    }

    /* draw background */
    gdk_cairo_set_source_color (cairo, &style->bg[gtk_widget_get_state (widget)]);
    rounded_rectangle (cairo,
                       width, width,
                       allocation.width - 2 * width,
                       allocation.height - 2 * width,
                       width);
    cairo_clip (cairo);

    cairo_paint (cairo);

    /* draw bullet before title */
    if (gtk_frame_get_label_widget (GTK_FRAME (frame))) {
        gdk_cairo_set_source_color (cairo, &frame->bullet_color);

        rounded_rectangle (cairo,
                           0, 0,
                           frame->bullet_allocation.width,
                           frame->bullet_allocation.height,
                           4);
        cairo_clip (cairo);

        cairo_paint (cairo);
        
    }
}

static gboolean
mux_frame_draw(GtkWidget *widget,
               cairo_t   *cr)
{
    GtkWidgetClass *grand_parent;
    if (gtk_widget_is_drawable (widget)) {
        mux_frame_paint (widget, cr);

        grand_parent = GTK_WIDGET_CLASS (g_type_class_peek_parent (mux_frame_parent_class));
        grand_parent->draw (widget, cr);
    }
    return FALSE;
}

static void
mux_frame_size_request (GtkWidget *widget,
                        GtkRequisition *requisition)
{
    GtkWidget *label = gtk_frame_get_label_widget (GTK_FRAME (widget));
    GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
    GtkRequisition child_req;
    GtkRequisition title_req;

    child_req.width = child_req.height = 0;
    if (child)
        gtk_widget_size_request (child, &child_req);

    title_req.width = title_req.height = 0;
    if (label) {
        gtk_widget_size_request (label, &title_req);
        /* add room for bullet */
        title_req.height = title_req.height * mux_frame_bullet_size_factor +  
                           2 * MUX_FRAME_BULLET_PADDING;
        title_req.width += title_req.height * mux_frame_bullet_size_factor + 
                           2 * MUX_FRAME_BULLET_PADDING;
    }

    requisition->width = MAX (child_req.width, title_req.width) +
                         2 * (gtk_container_get_border_width (GTK_CONTAINER (widget)) +
                              gtk_widget_get_style (widget)->xthickness);
    requisition->height = title_req.height + child_req.height +
                          2 * (gtk_container_get_border_width (GTK_CONTAINER (widget)) +
                               gtk_widget_get_style (widget)->ythickness);
}

static void
mux_frame_get_preferred_width (GtkWidget *widget,
                               gint      *minimal_width,
                               gint      *natural_width)
{
  GtkRequisition requisition;

  mux_frame_size_request (widget, &requisition);
  *minimal_width = *natural_width = requisition.width;
}

static void
mux_frame_get_preferred_height (GtkWidget *widget,
                                gint      *minimal_height,
                                gint      *natural_height)
{
  GtkRequisition requisition;

  mux_frame_size_request (widget, &requisition);
  *minimal_height = *natural_height = requisition.height;
}


static void
mux_frame_size_allocate (GtkWidget *widget,
                         GtkAllocation *allocation)
{
    GtkBin *bin = GTK_BIN (widget);
    MuxFrame *mux_frame = MUX_FRAME (widget);
    GtkFrame *frame = GTK_FRAME (widget);
    GtkAllocation child_allocation;
    GtkWidget *child;
    int xmargin, ymargin, title_height;

    gtk_widget_set_allocation (widget, allocation);
    xmargin = gtk_container_get_border_width (GTK_CONTAINER (widget)) +
              gtk_widget_get_style (widget)->xthickness;
    ymargin = gtk_container_get_border_width (GTK_CONTAINER (widget)) +
              gtk_widget_get_style (widget)->ythickness;

    title_height = 0;
    if (gtk_frame_get_label_widget (frame)) {
        GtkAllocation title_allocation;
        GtkRequisition title_req;
        gtk_widget_get_child_requisition (gtk_frame_get_label_widget (frame), &title_req);

        /* the bullet is bigger than the text */
        title_height = title_req.height * mux_frame_bullet_size_factor + 
                       2 * MUX_FRAME_BULLET_PADDING;

        /* x allocation starts after bullet */
        title_allocation.x = allocation->x + xmargin + title_height;
        title_allocation.y = allocation->y + ymargin + MUX_FRAME_BULLET_PADDING;
        title_allocation.width = MIN (title_req.width,
                                      allocation->width - 2 * xmargin - title_height);
        title_allocation.height = title_height - 2 * MUX_FRAME_BULLET_PADDING;
        gtk_widget_size_allocate (gtk_frame_get_label_widget (frame), &title_allocation);

        mux_frame->bullet_allocation.x = allocation->x + xmargin + MUX_FRAME_BULLET_PADDING;
        mux_frame->bullet_allocation.y = allocation->y + ymargin + MUX_FRAME_BULLET_PADDING;
        mux_frame->bullet_allocation.width = title_allocation.height;
        mux_frame->bullet_allocation.height = title_allocation.height;
    }

    child_allocation.x = allocation->x + xmargin;
    child_allocation.y = allocation->y + ymargin + title_height;
    child_allocation.width = allocation->width - 2 * xmargin;
    child_allocation.height = allocation->height - 2 * ymargin - title_height;

    if (gtk_widget_get_mapped (widget)) {
        gdk_window_invalidate_rect (gtk_widget_get_window (widget),
                                    allocation, FALSE);
    }
    child = gtk_bin_get_child (bin);
    if (child && gtk_widget_get_visible (child)) {
        gtk_widget_size_allocate (child, &child_allocation);
    }
}

static void mux_frame_style_set (GtkWidget *widget,
                                 GtkStyle *previous)
{
    MuxFrame *frame = MUX_FRAME (widget);

    mux_frame_update_style (frame);

    GTK_WIDGET_CLASS (mux_frame_parent_class)->style_set (widget, previous);
}

static void
mux_frame_class_init (MuxFrameClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
    GParamSpec *pspec;

    object_class->dispose = mux_frame_dispose;
    object_class->finalize = mux_frame_finalize;

    widget_class->draw = mux_frame_draw;
    widget_class->get_preferred_width = mux_frame_get_preferred_width;
    widget_class->get_preferred_height = mux_frame_get_preferred_height;
    widget_class->size_allocate = mux_frame_size_allocate;
    widget_class->style_set = mux_frame_style_set;

    pspec = g_param_spec_boxed ("border-color",
                                "Border color",
                                "Color of the outside border",
                                GDK_TYPE_COLOR,
                                G_PARAM_READABLE);
    gtk_widget_class_install_style_property(widget_class, pspec);
    pspec = g_param_spec_boxed ("bullet-color",
                                "Bullet color",
                                "Color of the rounded rectangle before a title",
                                GDK_TYPE_COLOR,
                                G_PARAM_READABLE);
    gtk_widget_class_install_style_property(widget_class, pspec);
    pspec = g_param_spec_string ("title-font",
                                 "Title font",
                                 "Pango font description string for title text",
                                 "12",
                                 G_PARAM_READWRITE);
    gtk_widget_class_install_style_property(widget_class, pspec);
}

static void
mux_frame_buildable_add_child (GtkBuildable *buildable,
                               GtkBuilder *builder,
                               GObject *child,
                               const gchar *type)
{
  if (!type)
    gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
  else
    GTK_BUILDER_WARN_INVALID_CHILD_TYPE (MUX_FRAME (buildable), type);
}

static void
mux_frame_buildable_init (GtkBuildableIface *iface)
{
    iface->add_child = mux_frame_buildable_add_child;
}

static void
mux_frame_init (MuxFrame *self)
{
    g_signal_connect (self, "notify::label-widget",
                      G_CALLBACK (label_changed_cb), NULL);
}

GtkWidget*
mux_frame_new (void)
{
    return g_object_new (MUX_TYPE_FRAME, 
                         "border-width", 4,
                         NULL);
}

