/*
 *  Copyright (c) 2021-2024 Giuseppe Torelli <colossus73@gmail.com>
 *   *
 *  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 Library 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.,59 Temple Place - Suite 330,Boston,MA 02111-1307,USA.
 *
 */
#include "gtk_timeline.h"

/**
 * ImgTimeline is a GTK+3 custom widget based on GtkLayout 
    which aims to build a nice and customizable timeline widget
 *
 */

typedef struct _ImgTimelinePrivate
{
  gchar *video_background_string;
  gchar *audio_background_string;
  gdouble video_background[4];
  gdouble audio_background[4];
  
  gint last_slide_posX;
  gint zoom;
  gint seconds;
  gint minutes;
  gint hours;
  gint total_time;
  gint time_marker_pos;
  gboolean button_pressed;

  cairo_surface_t *surface;
  GtkWidget *slide_selected;
} ImgTimelinePrivate;

typedef struct _TimelineHandle TimelineHandle;
typedef struct _TimelineHandleClass TimelineHandleClass;

struct _TimelineHandle
{
    GtkDrawingArea parent;
    gboolean is_left_handle;
};

struct _TimelineHandleClass
{
    GtkDrawingAreaClass parent_class;
};

G_DEFINE_TYPE(TimelineHandle, timeline_handle, GTK_TYPE_DRAWING_AREA)

typedef struct _media media;
struct _media
{
	gdouble x;
	gdouble drag_x;
	gdouble old_x;
	gdouble initial_width;
	gdouble width;
	enum {	RESIZE_NONE, RESIZE_LEFT, RESIZE_RIGHT } resizing;
	gboolean button_pressed;
	GtkWidget *button;
};

static guint timeline_signals[N_SIGNALS] = { 0 };

//Private functions.
static void gtk_timeline_class_init(ImgTimelineClass *);
static void gtk_timeline_set_property(GObject *, guint , const GValue *, GParamSpec *);
static void gtk_timeline_get_property(GObject *, guint , GValue *, GParamSpec *);
static void gtk_timeline_init(ImgTimeline *);
static void gtk_timeline_draw_time_ticks(GtkWidget *widget, cairo_t *, gint );
static void gtk_timeline_finalize(GObject *);
static void gtk_timeline_drag_data_get(GtkWidget *, GdkDragContext *, GtkSelectionData *, guint , guint, gpointer );
static gboolean gtk_timeline_draw(GtkWidget *, cairo_t *);
static gboolean gtk_timeline_slide_motion_notify(GtkWidget *, GdkEventMotion *, ImgTimeline *);
static gboolean gtk_timeline_slide_leave_event (GtkWidget *, GdkEventCrossing *, gpointer );

G_DEFINE_TYPE_WITH_PRIVATE(ImgTimeline, gtk_timeline, GTK_TYPE_LAYOUT)

static void gtk_timeline_class_init(ImgTimelineClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS(klass);
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);

	object_class->set_property = gtk_timeline_set_property;
	object_class->get_property = gtk_timeline_get_property;
	object_class->finalize = gtk_timeline_finalize;
	
	widget_class->draw = gtk_timeline_draw;
  
	g_object_class_install_property(object_class, VIDEO_BACKGROUND, g_param_spec_string("video_background", "video_background", "video_background", NULL, G_PARAM_READWRITE));
	g_object_class_install_property(object_class, AUDIO_BACKGROUND, g_param_spec_string("audio_background", "audio_background", "audio_background", NULL, G_PARAM_READWRITE));
	g_object_class_install_property(object_class, TOTAL_TIME,       g_param_spec_int("total_time", "total_time", "total_time", 0, G_MAXINT, 60, G_PARAM_READWRITE));
	g_object_class_install_property(object_class, TIME_MARKER_POS, g_param_spec_int("time_marker_pos", "Time Marker Position", "Position of the time marker", 0, G_MAXINT, 0, G_PARAM_READWRITE));

	// Install signals
    timeline_signals[SIGNAL_TIME_CHANGED] = g_signal_new("time-changed",
        G_TYPE_FROM_CLASS(klass),
        G_SIGNAL_RUN_LAST,
        0,
        NULL, NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1, G_TYPE_INT);

}

static void gtk_timeline_init(ImgTimeline *timeline)
{
    ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(timeline);

    priv->last_slide_posX = 0;
    priv->zoom = 1;
    priv->seconds = priv->minutes = priv->hours = 0;
    priv->total_time = 60;
    priv->time_marker_pos = 0;

    for (int i = 0; i < 4; i++)
        priv->video_background[i] = priv->audio_background[i] = (i < 3) ? 0.0 : 1.0;

    priv->video_background_string = g_strdup("rgba(0, 0, 0, 1.0)");
    priv->audio_background_string = g_strdup("rgba(0, 0, 0, 1.0)");

    // Set up CSS styling
    GtkCssProvider *css_provider = gtk_css_provider_new();
    gtk_css_provider_load_from_data(css_provider,
        ".timeline-button { border-width: 0; outline-width: 0; background-color: transparent; padding: 0; margin: 0; }"
        ".timeline-button:focus { outline-width: 0; }"
        ".timeline-button .timeline-image-container { border-top: 2px solid transparent; border-bottom: 2px solid transparent; }"
        ".timeline-button:checked .timeline-image-container { border-top-color: #FFD700; border-bottom-color: #FFD700; }"
        ".timeline-button:hover { opacity: 0.8; }"
        ".timeline-image-container { padding: 0; margin: 0; }", -1, NULL);
    
    gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
        GTK_STYLE_PROVIDER(css_provider),
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    
    g_object_unref(css_provider);
}

//Needed for g_object_set().
static void gtk_timeline_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
	ImgTimeline *da = GTK_TIMELINE(object);

	switch(prop_id)
	{
		case VIDEO_BACKGROUND:
			gtk_timeline_set_video_background(da, g_value_get_string(value));
		break;
		case AUDIO_BACKGROUND:
			gtk_timeline_set_audio_background(da, g_value_get_string(value));
		break;
		case TOTAL_TIME:
			gtk_timeline_set_total_time(da, g_value_get_int(value));
		break; 
		case TIME_MARKER_POS:
			gtk_timeline_set_time_marker(da, g_value_get_int(value));
		break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
  }
}

void gtk_timeline_set_video_background(ImgTimeline *da, const gchar *background_string)
{
	  ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(da);
	  GdkRGBA rgba;
	  
	  gdk_rgba_parse(&rgba, background_string);
	  priv->video_background[0] = rgba.red;
	  priv->video_background[1] = rgba.green;
	  priv->video_background[2] = rgba.blue;
	  priv->video_background[3] = rgba.alpha;
	  
	  if(priv->video_background_string)
		g_free(priv->video_background_string);
	  
	  priv->video_background_string = g_strdup(background_string); 
}

void gtk_timeline_set_audio_background(ImgTimeline *da, const gchar *background_string)
{
	  ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(da);
	  GdkRGBA rgba;
	  
	  gdk_rgba_parse(&rgba, background_string);
	  priv->audio_background[0] = rgba.red;
	  priv->audio_background[1] = rgba.green;
	  priv->audio_background[2] = rgba.blue;
	  priv->audio_background[3] = rgba.alpha;

	  if(priv->audio_background_string)
		g_free(priv->audio_background_string);
	  
	  priv->audio_background_string = g_strdup(background_string); 
}

void gtk_timeline_set_total_time(ImgTimeline *da, gint total_time)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(da);

	priv->total_time = total_time;
}

void gtk_timeline_set_time_marker(ImgTimeline *timeline, gint posx)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(timeline);

	priv->time_marker_pos = posx;
	gtk_widget_queue_draw(GTK_WIDGET(timeline));
    
	g_signal_emit(timeline, timeline_signals[SIGNAL_TIME_CHANGED], 0, posx);
}

static void gtk_timeline_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
	ImgTimeline *da = GTK_TIMELINE(object);
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(da);
	  
	switch(prop_id)
	{
		case VIDEO_BACKGROUND:
			g_value_set_string(value, priv->video_background_string);
		 break;
		case AUDIO_BACKGROUND:
			g_value_set_string(value, priv->audio_background_string);
		break;
		case TOTAL_TIME:
			g_value_set_int(value, priv->total_time);
		break;

		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
	}
}

GtkWidget* gtk_timeline_new()
{
	return GTK_WIDGET(g_object_new(gtk_timeline_get_type(), NULL));
}

static void timeline_handle_init(TimelineHandle *handle)
{
	gtk_widget_set_size_request(GTK_WIDGET(handle), 20, -1);
}

static gboolean timeline_handle_draw(GtkWidget *widget, cairo_t *cr)
{
    TimelineHandle *handle = TIMELINE_HANDLE(widget);
    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);

    double radius = 8.0;  // Radius for rounded corners
    double degrees = G_PI / 180.0;

    // Draw handle background with rounded corners on one side
    cairo_new_sub_path(cr);
    
    if (handle->is_left_handle)
    {
        cairo_arc(cr, radius, radius, radius, 180 * degrees, 270 * degrees);
        cairo_line_to(cr, allocation.width, 0);
        cairo_line_to(cr, allocation.width, allocation.height);
        cairo_line_to(cr, radius, allocation.height);
        cairo_arc(cr, radius, allocation.height - radius, radius, 90 * degrees, 180 * degrees);
    }
    else
    {
        cairo_move_to(cr, 0, 0);
        cairo_line_to(cr, allocation.width - radius, 0);
        cairo_arc(cr, allocation.width - radius, radius, radius, -90 * degrees, 0 * degrees);
        cairo_arc(cr, allocation.width - radius, allocation.height - radius, radius, 0 * degrees, 90 * degrees);
        cairo_line_to(cr, 0, allocation.height);
    }
    
    cairo_close_path(cr);

    cairo_set_source_rgb(cr, 1, 1, 0);
    cairo_fill_preserve(cr);
    
    // Draw a subtle border
    cairo_set_source_rgba(cr, 0.7, 0.7, 0.7, 1);  // Slightly darker gray for border
    cairo_set_line_width(cr, 1);
    cairo_stroke(cr);

    // Draw circles
    cairo_set_source_rgb(cr, 0, 0, 0);  // Black circles
    for (int i = 0; i < 3; i++) {
        cairo_arc(cr, allocation.width / 2, 5 + allocation.height * (i + 1) / 5, 2, 0, 2 * G_PI);
        cairo_fill(cr);
    }

    return TRUE;
}

static void timeline_handle_class_init(TimelineHandleClass *class)
{
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
    widget_class->draw = timeline_handle_draw;
}

static GtkWidget *timeline_handle_new(gboolean is_left_handle)
{
    TimelineHandle *handle = g_object_new(TIMELINE_TYPE_HANDLE, NULL);
    handle->is_left_handle = is_left_handle;
    gtk_widget_set_size_request(GTK_WIDGET(handle), 15, -1);
    return GTK_WIDGET(handle);
}

static gboolean gtk_timeline_draw(GtkWidget *da, cairo_t *cr)
{
	  ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);

	  gint width = gtk_widget_get_allocated_width(da);

	  cairo_save(cr);
	  cairo_translate (cr, 0 , 12);
	  gtk_timeline_draw_time_ticks(da, cr, width);

	  //Video timeline
	  cairo_translate (cr, 0 , 11);
	  cairo_set_source_rgba(cr, priv->video_background[0], priv->video_background[1], priv->video_background[2], priv->video_background[3]);
	  cairo_set_line_width(cr, 1);
	  cairo_rectangle(cr, 0,11, width - 2, 51);
	  cairo_fill(cr);
	  
	  //Audio timeline
	  cairo_set_source_rgba(cr, priv->audio_background[0], priv->audio_background[1], priv->audio_background[2], priv->audio_background[3]);
	  cairo_rectangle(cr, 0,70, width - 2, 51);
	  cairo_fill(cr);
	  cairo_restore(cr);

	  //This is necessary to draw the slides represented by the GtkButtons
	  GTK_WIDGET_CLASS (gtk_timeline_parent_class)->draw (da, cr);

	  //Draw the red time marker 
	  gtk_timeline_draw_time_marker(da, cr, priv->time_marker_pos);

	  return TRUE;
}

static void gtk_timeline_finalize(GObject *object)
{ 
  ImgTimeline *da = GTK_TIMELINE(object);
  ImgTimelinePrivate *priv =gtk_timeline_get_instance_private(da);
	  
  g_free(priv->video_background_string);
  g_free(priv->audio_background_string);
	  
  if(priv->surface != NULL)
	cairo_surface_destroy(priv->surface);

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

void gtk_timeline_draw_time_ticks(GtkWidget *da, cairo_t *cr, gint width)
{
	  ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);
	  cairo_text_extents_t extents;
	  gchar *time;
	  gint i, factor;
	  gdouble distanceBetweenTicks, cairo_factor;

	  distanceBetweenTicks = 48.5 - priv->zoom;
	  factor = 2;

	  if (distanceBetweenTicks <= 12)
		distanceBetweenTicks = 12;    
	  
	  gtk_widget_set_size_request(da, (priv->total_time * distanceBetweenTicks), -1);
	  cairo_set_source_rgb(cr, 0,0,0);

	  for (i = 0; i < priv->total_time; i++)
	  {
		if (i % 2 == 0)
		  cairo_factor = 0;
		else
		  cairo_factor = 0.5;
		
		//Draw the line markers
		cairo_move_to( cr, i * distanceBetweenTicks + cairo_factor, 4);
		cairo_line_to( cr, i * distanceBetweenTicks + cairo_factor, 14);
		//Draw the sub line markers
		// if (distanceBetweenTicks > 12)
		// {
		//   for (j = 9; j < distanceBetweenTicks; j +=10)
		//   {
		//     cairo_move_to( cr, (i * distanceBetweenTicks + cairo_factor) + j , 12);
		//     cairo_line_to( cr, (i * distanceBetweenTicks + cairo_factor) + j , 24);
		//   }
		// }

		if (priv->zoom >= 8)
			factor = 4;
		else
			factor = 1;

		if (i % factor == 0)
		{  
		  time = g_strdup_printf("%02d:%02d:%02d", priv->hours, priv->minutes, priv->seconds);
		  cairo_select_font_face(cr, "Ubuntu", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
		  cairo_set_font_size(cr, 10);
		  cairo_text_extents(cr, time, &extents);
		  cairo_move_to(cr, (-extents.width/2) + i*distanceBetweenTicks, 0);  
		  cairo_show_text(cr, time);
		  g_free(time);
		}
		
		if (priv->seconds >= 59)
		{
			priv->seconds = -1;
			priv->minutes++;
		}   
		if (priv->minutes >= 59)
		{
			priv->minutes = 0;
			priv->hours++;
		}
		priv->seconds += priv->zoom;
		if (priv->seconds > 59)
		  priv->seconds = 59;

		cairo_stroke(cr);
	}
	priv->seconds = priv->minutes = priv->hours = 0;
}

void gtk_timeline_adjust_zoom(GtkWidget *da, gint zoom, gint direction)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);

	priv->zoom = zoom;
	if (direction > 0)
		priv->total_time--;
	else
		priv->total_time++;
	//TODO change 300 to the total time of the slideshow
	 if (priv->total_time < 300)
		priv->total_time = 300;
	
	g_print("adjust zoom: %d\n",priv->total_time);
	gtk_widget_queue_draw(GTK_WIDGET(da));
}

void gtk_timeline_add_media(GtkWidget *da, gchar *filename, gint x)
{
    GdkPixbuf *pix;
    GtkWidget *img, *box, *image_container;
    struct _media *item;
    gint pos;

    ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);

    item = g_new0(media, 1);

    // Create a box to hold the handles and the image
    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    item->button = gtk_toggle_button_new();
    gtk_style_context_add_class(gtk_widget_get_style_context(item->button), "timeline-button");
    gtk_container_add(GTK_CONTAINER(item->button), box);

    // Add left handle
    GtkWidget *left_handle = timeline_handle_new(TRUE);
    gtk_box_pack_start(GTK_BOX(box), left_handle, FALSE, FALSE, 0);

	// Create image container
    image_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_style_context_add_class(gtk_widget_get_style_context(image_container), "timeline-image-container");
    gtk_box_pack_start(GTK_BOX(box), image_container, TRUE, TRUE, 0);
    
    // Add the image
    pix = gdk_pixbuf_new_from_file_at_scale(filename, -1, 45, TRUE, NULL);
    if (pix == NULL)
        img = gtk_image_new_from_icon_name("image-missing", GTK_ICON_SIZE_DIALOG);
    else
        img = gtk_image_new_from_pixbuf(pix);
    
     gtk_widget_set_valign(img, GTK_ALIGN_CENTER);
    gtk_container_add(GTK_CONTAINER(image_container), img);

    // Add right handle
    GtkWidget *right_handle = timeline_handle_new(FALSE);
    gtk_box_pack_end(GTK_BOX(box), right_handle, FALSE, FALSE, 0);

    g_object_set_data(G_OBJECT(item->button), "mem_address", item);

    gtk_widget_add_events(item->button, GDK_POINTER_MOTION_MASK
                                      | GDK_LEAVE_NOTIFY_MASK
                                      | GDK_BUTTON1_MASK 
                                      | GDK_BUTTON1_MOTION_MASK
                                      | GDK_BUTTON_PRESS_MASK
                                      | GDK_BUTTON_RELEASE_MASK);

    if (pix != NULL)
    {
        g_signal_connect(G_OBJECT(item->button), "drag-data-get", G_CALLBACK(gtk_timeline_drag_data_get), NULL);
        g_signal_connect(G_OBJECT(item->button), "motion-notify-event", G_CALLBACK(gtk_timeline_slide_motion_notify), da);
        g_signal_connect(G_OBJECT(item->button), "leave-notify-event", G_CALLBACK(gtk_timeline_slide_leave_event), da);
        g_signal_connect(G_OBJECT(item->button), "button-press-event", G_CALLBACK(gtk_timeline_slide_button_press_event), da);
        g_signal_connect(G_OBJECT(item->button), "button-release-event", G_CALLBACK(gtk_timeline_slide_button_release_event), da);
    }

    gtk_container_add(GTK_CONTAINER(da), item->button);
    gtk_widget_show_all(item->button);

    if (x > 0)
        pos = x - 47.5;
    else
        pos = priv->last_slide_posX;
    
    gtk_layout_move(GTK_LAYOUT(da), item->button, priv->last_slide_posX, 35);
    item->old_x = pos;

    priv->last_slide_posX += 95;
    gtk_widget_set_size_request(item->button, 95, (pix ? -1 : 35));

    if (pix)
        g_object_unref(pix);
}

void gtk_timeline_draw_time_marker(GtkWidget *widget, cairo_t *cr, gint posx)
{
	cairo_save(cr);
	cairo_set_source_rgb(cr, 1,0,0);
	cairo_translate(cr, posx,17);

	gint i;
	gint points[3][2] =
	{ 
           { 0, 0 }, 
           { 5, 15 }, 
           { 10, 0 }           
     };

	for (i = 0; i < 3; i++)
		cairo_line_to(cr, points[i][0], points[i][1]);

	cairo_close_path(cr);
	cairo_stroke_preserve(cr);
	cairo_fill(cr);

	cairo_move_to(cr, 5,15);
	cairo_line_to(cr, 5,126);
	cairo_stroke(cr);
	cairo_restore(cr);
}

void gtk_timeline_drag_data_get(GtkWidget *widget, GdkDragContext *drag_context, GtkSelectionData *data, guint info, guint time,
gpointer user_data)
{
	gtk_selection_data_set (data, gdk_atom_intern_static_string ("GTK_TIMELINE_SLIDE"), 32, (const guchar *)&widget, sizeof (gpointer));
}

void gtk_timeline_adjust_marker_posx(GtkWidget *da, gint posx)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);

	priv->time_marker_pos = posx;
	gtk_widget_queue_draw((GtkWidget*)da);
}

gboolean gtk_timeline_mouse_button_press (GtkWidget *timeline, GdkEvent *event, ImgTimeline *da)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);
	
	if (event->button.y < 16.0 || event->button.y > 32.0)
		return FALSE;

	priv->button_pressed = TRUE;
	gtk_timeline_adjust_marker_posx(timeline, event->button.x - 5);
	return TRUE;
}

gboolean gtk_timeline_motion_notify(GtkWidget *timeline, GdkEventMotion *event, ImgTimeline *da)
{
	ImgTimelinePrivate *priv = gtk_timeline_get_instance_private((ImgTimeline*)da);

	if (priv->button_pressed)
		if ( (event->state & GDK_BUTTON1_MOTION_MASK) && (event->y > 16.0 && event->y < 32.0) )
			gtk_timeline_adjust_marker_posx(timeline, event->x - 5);

   return FALSE;
}

static void gtk_timeline_update_button_image(GtkWidget *button)
{
    GtkWidget *box, *image_container, *image;
    GdkPixbuf *original_pixbuf, *tiled_pixbuf;
    gint button_width, button_height, image_width, image_height;

    // Get the box that contains the image container
    box = gtk_bin_get_child(GTK_BIN(button));
    if (!GTK_IS_BOX(box))
        return;

    // Find the image container within the box
    GList *children = gtk_container_get_children(GTK_CONTAINER(box));
    image_container = NULL;
    for (GList *l = children; l != NULL; l = l->next) {
        if (GTK_IS_BOX(l->data) && 
            gtk_style_context_has_class(gtk_widget_get_style_context(l->data), "timeline-image-container")) {
            image_container = GTK_WIDGET(l->data);
            break;
        }
    }
    g_list_free(children);

    if (!image_container)
        return;

    // Find the image widget within the image container
    children = gtk_container_get_children(GTK_CONTAINER(image_container));
    image = NULL;
    for (GList *l = children; l != NULL; l = l->next) {
        if (GTK_IS_IMAGE(l->data)) {
            image = GTK_WIDGET(l->data);
            break;
        }
    }
    g_list_free(children);

    if (!image)
        return;

    original_pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
    if (!original_pixbuf)
        return;

    gtk_widget_get_size_request(button, &button_width, &button_height);
    image_width = gdk_pixbuf_get_width(original_pixbuf);
    image_height = gdk_pixbuf_get_height(original_pixbuf);

    // Ensure button dimensions are positive
    button_width = MAX(button_width, 50);
    button_height = MAX(button_height, 1);

    // Create a new pixbuf to hold the tiled image
    // Subtract width of handles (assumed to be 20px each)
    tiled_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
                                  gdk_pixbuf_get_has_alpha(original_pixbuf), 
                                  8, 
                                  button_width - 20, 
                                  button_height);

    if (!tiled_pixbuf)
        return;

    // Tile the original image across the button
    for (int y = 0; y < button_height; y += image_height)
    {
        for (int x = 0; x < button_width - 20; x += image_width)
        {
            gdk_pixbuf_copy_area(original_pixbuf, 
                                 0, 0, 
                                 MIN(image_width, button_width - 20 - x), 
                                 MIN(image_height, button_height - y), 
                                 tiled_pixbuf, 
                                 x, y);
        }
    }

    gtk_image_set_from_pixbuf(GTK_IMAGE(image), tiled_pixbuf);
    g_object_unref(tiled_pixbuf);
}

gboolean gtk_timeline_slide_button_press_event(GtkWidget *button, GdkEventButton *event, ImgTimeline *timeline)
{
	//ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(timeline);
	struct _media *item;
	  
	  item = g_object_get_data(G_OBJECT(button), "mem_address");
	  item->button_pressed = TRUE;
	  
	  // Store the initial mouse position and button width
	  item->drag_x = event->x;
	  item->initial_width = gtk_widget_get_allocated_width(button);
	  
	  // Check if we're on the resize areas (left or right 10 pixels of the button)
	  if (event->x <= 10) {
		item->resizing = RESIZE_LEFT;
	  } else if (event->x >= item->initial_width - 10) {
		item->resizing = RESIZE_RIGHT;
	  } else {
		item->resizing = RESIZE_NONE;
	  }
	  
	  return FALSE;
	}

gboolean gtk_timeline_slide_button_release_event(GtkWidget *button, GdkEventButton *event, ImgTimeline *timeline)
{
	 struct _media *item;
	 item = g_object_get_data(G_OBJECT(button), "mem_address");
	 item->button_pressed = FALSE;
	 item->resizing = RESIZE_NONE;

	return FALSE;
}

static gboolean gtk_timeline_slide_motion_notify(GtkWidget *button, GdkEventMotion *event, ImgTimeline *timeline)
{
	gint x, new_width, button_width;
	GdkCursor *cursor;
	GdkWindow *window;
	struct _media *item;
	  
	//ImgTimelinePrivate *priv = gtk_timeline_get_instance_private(timeline);
	item = g_object_get_data(G_OBJECT(button), "mem_address");
	  
	window = gtk_widget_get_window(button);
	button_width = gtk_widget_get_allocated_width(button);

	 if (item->button_pressed)
	{
		switch (item->resizing)
		{
		  case RESIZE_LEFT:
			new_width = item->initial_width + (item->drag_x - event->x);
			new_width = MAX(new_width, 50);
			x = item->old_x + (item->initial_width - new_width);
			gtk_widget_set_size_request(button, new_width, 45);
			gtk_layout_move(GTK_LAYOUT(timeline), button, x, 35);
			item->old_x = x;
			item->initial_width = new_width;
			gtk_timeline_update_button_image(button);
			break;
		  
		  case RESIZE_RIGHT:
			new_width = MAX(event->x, 50);  // Ensure a minimum width
			gtk_widget_set_size_request(button, new_width, 45);
			gtk_timeline_update_button_image(button);
			break;
		  
		  case RESIZE_NONE:
			x = item->old_x + (event->x - item->drag_x);
			gint timeline_width = gtk_widget_get_allocated_width(GTK_WIDGET(timeline));
			x = CLAMP(x, 0, timeline_width - button_width);
		   gtk_layout_move(GTK_LAYOUT(timeline), button, x, 35);
			
			// Update old_x for the next movement
			item->old_x = x;
			break;
		}
	}

	// Update cursor based on mouse position
	if (event->x <= 15 || (event->x >= button_width - 15 && event->x <= button_width))
		cursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_SB_H_DOUBLE_ARROW);
	else
		cursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_ARROW);
	
	gdk_window_set_cursor(window, cursor);
	g_object_unref(cursor);

	return FALSE;
}

static gboolean gtk_timeline_slide_leave_event(GtkWidget *button, GdkEventCrossing *event, gpointer timeline)
{
	 GdkCursor *cursor;
	 GdkWindow *window;

	window = gtk_widget_get_window(button);
	cursor = gdk_cursor_new_for_display(gdk_display_get_default(), GDK_ARROW);
	gdk_window_set_cursor(window, cursor);
	return FALSE;
}

gboolean gtk_timeline_scroll(GtkWidget *timeline, GdkEventScroll *event, GtkWidget *scrolledwindow)
{
  GdkModifierType accel_mask = gtk_accelerator_get_default_mod_mask();
  GtkAdjustment *scrollX     = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(scrolledwindow));
  gdouble deltax, deltay;
  GdkScrollDirection direction;

  static gint zoom = 1;
  
  if (gdk_event_get_scroll_direction((GdkEvent*)event, &direction))
  {
    if ( direction == GDK_SCROLL_DOWN )
      gtk_adjustment_set_value(scrollX, gtk_adjustment_get_value(scrollX) + 40);

    if ( direction == GDK_SCROLL_UP )
      gtk_adjustment_set_value(scrollX, gtk_adjustment_get_value(scrollX) - 40);    
  }
  else
  {
    gdk_event_get_scroll_deltas((GdkEvent *)event, &deltax, &deltay);
    if (deltay > 0)
      gtk_adjustment_set_value(scrollX, gtk_adjustment_get_value(scrollX) + 40);
  else
      gtk_adjustment_set_value(scrollX, gtk_adjustment_get_value(scrollX) - 40);
  }
  if ( (event->state & accel_mask) == GDK_CONTROL_MASK)
  {
    if (deltay > 0)
      zoom --;
    else if (deltay < 0)
      zoom ++;

    if (zoom < 1)
        zoom = 1;
    gtk_timeline_adjust_zoom(timeline, zoom, deltay);
  }
  return TRUE;
}

void gtk_timeline_drag_data_received (GtkWidget *timeline, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer pointer)
{
  gchar **images = NULL;
  gchar *filename;
  gint i = 0;

  images = gtk_selection_data_get_uris(selection_data);
  if (images)
  {
    while(images[i])
    {
      filename = g_filename_from_uri (images[i], NULL, NULL);
      gtk_timeline_add_media(timeline, filename, x);
      g_free(filename);
      i++;
    }
  }
  g_strfreev (images);

  gtk_drag_finish (context, TRUE, FALSE, time);
}
