/* editor.c
 * Copyright (C) 2002-2004 Pascal Eberhard <pascal.ebo@netcourrier.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
 * 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 "common.h"
#include <string.h>
#include <gdk/gdkkeysyms.h>

static void     editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
                                 char *iconame, gint x, gint y,
                                 gboolean is_valid, gboolean is_cursor);
static void     editor_build_interface(editor_t *e);

static gboolean editor_button_press_event(GtkWidget *widget,
                                          GdkEventButton *event,
                                          editor_t *e);
static gboolean editor_motion_notify_event(GtkWidget *widget,
                                           GdkEventMotion *event,
                                           editor_t *e);
static gboolean editor_is_dblclick(editor_t *e, int x, int y);
static gboolean editor_button_release_event(GtkWidget *widget,
                                            GdkEventButton *event,
                                            editor_t *e);
static void     editor_arrow_up_clicked(GtkWidget *widget, editor_t *e);
static void     editor_arrow_down_clicked(GtkWidget *widget, editor_t *e);
static void     editor_arrow_left_clicked(GtkWidget *widget, editor_t *e);
static void     editor_arrow_right_clicked(GtkWidget *widget, editor_t *e);

// ----------------------------------------------------------------------------
editor_t* editor_new(struct apwalapp_t *apwal)
{
  editor_t *editor;

  g_assert(apwal != NULL);
  g_assert(apwal->editor_frame != NULL);

  editor = (editor_t *)malloc(sizeof(editor_t));
  g_assert(editor != NULL);

  editor->apwal = apwal;
  editor->dblclick_timer = NULL;

  editor->drag_in_progress = FALSE;

  editor->xwidth = gdk_screen_width();
  editor->xheight = gdk_screen_height();

  editor->x = -2;
  editor->y = -2;
  editor->width = 10;
  if (editor->xheight <= 480)
    editor->height = 3;
  else
    editor->height = 6;

  editor_build_interface(editor);
  editor_refresh(editor);

  return editor;
}
// ----------------------------------------------------------------------------
void editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
                      char *iconame, gint x, gint y, gboolean is_valid,
                      gboolean is_cursor)
{
  GdkPixbuf *pixbuf_icon;
  gint        width;
  gint        height;
  g_assert(e != NULL && pixbuf_view != NULL &&  iconame != NULL);
  // first check if this icon has to be showed
  if ((x < e->x) || (x >= (e->x + e->width)))
    return;
  if ((y < e->y) || (y >= (e->y + e->height)))
    return;
    
  // icon pixbuf
  pixbuf_icon = gdk_pixbuf_new_from_apwal(iconame, NULL, NULL);
    
  // if the app has something wrong then change is color to N&B.
  if (!is_valid && !is_cursor)
    gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
                                     0.1/*saturation*/,
                                     TRUE/*pixelate*/);

  if ((x == e->drag_start_x) && (y == e->drag_start_y))
  {
    if ((e->drag_in_progress == TRUE) && 
        ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) )
    {
      if (!is_cursor)
        gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
                                         0.0/*saturation*/,
                                         TRUE/*pixelate*/);
      if ((e->drag_start_x != e->drag_current_x) ||
          (e->drag_start_y != e->drag_current_y))
      {
        if ((!is_cursor) &&
            (app_list_at_xy(e->apwal->apps, e->drag_current_x,
                            e->drag_current_y) == NULL))
          editor_draw_icon(e, pixbuf_view, iconame,  e->drag_current_x,
                           e->drag_current_y, TRUE/*valid?*/, FALSE/*cursor?*/);
      }
    }
  }

  // accept icons with size lower that 48x48
  width = gdk_pixbuf_get_width(pixbuf_icon);
  height = gdk_pixbuf_get_height(pixbuf_icon);
  width = width < ICON_WIDTH  ? width : ICON_WIDTH;
  height= height< ICON_HEIGHT ? height: ICON_HEIGHT;
  /*
  TRACE("x:%d y:%d w:%d h:%d, "
          "icon x:%d y:%d w:%d h:%d coord:(%d,%d)",
          e->x, e->y, e->width, e->height,
          x, y, width, height,
          (x - e->x) * ICON_WIDTH, (y - e->y) * ICON_HEIGHT);
  */
  // put the icon in the image which has to be displayed
  gdk_pixbuf_composite(pixbuf_icon/*src*/, pixbuf_view/*dest*/,
                       (x - e->x) * ICON_WIDTH/*dest_x*/,
                       (y - e->y) * ICON_HEIGHT/*dest_y*/,
                       gdk_pixbuf_get_width(pixbuf_icon)/*dest_width*/,
                       gdk_pixbuf_get_height(pixbuf_icon)/*dest_height*/,
                       (x - e->x) * ICON_WIDTH/*offset_x*/,
                       (y - e->y) * ICON_WIDTH/*offset_y*/,
                       1/*scale_x*/,
                       1/*scale_y*/,
                       GDK_INTERP_NEAREST/*interp_type*/,
                       255/*overall_alpha*/);
  g_object_unref(pixbuf_icon);

  return;
}
// ----------------------------------------------------------------------------
void editor_refresh(editor_t *e)
{
  GdkPixbuf *pixbuf_view;
  app_t     *app;
  gboolean   is_valid;

  g_assert(e != NULL);
  g_assert(e->apwal != NULL);
  g_assert(e->apwal->apps != NULL);

  pixbuf_view = gdk_pixbuf_new(GDK_COLORSPACE_RGB/*colorspace*/,
                               TRUE/*has_alpha*/,
                               8/*bits_per_sample*/,
                               e->width * ICON_WIDTH,
                               e->height * ICON_HEIGHT);
  gdk_pixbuf_fill(pixbuf_view, 0x00000000);

  app = app_list_first(e->apwal->apps);
  while (app)
  {
    is_valid = app_is_executable(app);
    editor_draw_icon(e, pixbuf_view, app->icon, app->x, app->y, is_valid,
                     FALSE/*cursor?*/);
    app = app_list_next(e->apwal->apps);
  } // end while app
  
  // draw cursor
  editor_draw_icon(e, pixbuf_view, "cursor2", 0, 0, TRUE, TRUE/*cursor?*/);
  // draw selection around the current selected icon
  if (e->apwal->selected_app != NULL)
    editor_draw_icon(e, pixbuf_view, "selected", e->apwal->selected_app->x,
                     e->apwal->selected_app->y, TRUE, TRUE/*cursor?*/);
    //editor_draw_icon(e, pixbuf_view, "selected", e->drag_current_x,
    //                 e->drag_current_y, TRUE, TRUE /*cursor?*/);

  gtk_image_set_from_pixbuf(GTK_IMAGE(e->image), pixbuf_view);
  g_object_unref(pixbuf_view);

}
// ----------------------------------------------------------------------------
static void editor_build_interface(editor_t *e)
{
//GtkWidget     *notebook;
//GtkWidget       *editor_vbox;
//GtkWidget         *editor_frame;
  GtkWidget           *table;
  GtkWidget             *eventbox;
//GtkWidget               *image;
  GtkWidget             *button; // left, right, top, button & ?
  GtkWidget             *image;  // ? img
  GtkWidget             *eventbox_for_image;  // ? img
  char *tooltips_str;

  g_assert(e != NULL);
  g_assert(e->apwal != NULL);
  g_assert(e->apwal->editor_frame != NULL);

  // table for the editor view
  table = gtk_table_new(3/*rows*/,3/*cols*/, FALSE/*homogeous*/);
  gtk_container_add(GTK_CONTAINER(e->apwal->editor_frame), table);
  gtk_widget_show(table);

  // arrows ^ v < >  and '?' for the tips
  button = gtk_arrow_button_new(GTK_ARROW_UP);
  gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 0, 1);
  g_signal_connect(G_OBJECT(button), "clicked",
                   G_CALLBACK(editor_arrow_up_clicked), e);
  gtk_widget_show(button);
  button = gtk_arrow_button_new(GTK_ARROW_DOWN);
  gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 2, 3);
  g_signal_connect(G_OBJECT(button), "clicked",
                   G_CALLBACK(editor_arrow_down_clicked), e);
  gtk_widget_show(button);
  button = gtk_arrow_button_new(GTK_ARROW_LEFT);
  gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, 1, 2);
  g_signal_connect(G_OBJECT(button), "clicked",
                   G_CALLBACK(editor_arrow_left_clicked), e);
  gtk_widget_show(button);
  button = gtk_arrow_button_new(GTK_ARROW_RIGHT);
  gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 1, 2);
  g_signal_connect(G_OBJECT(button), "clicked",
                   G_CALLBACK(editor_arrow_right_clicked), e);
  gtk_widget_show(button);

  eventbox_for_image = gtk_event_box_new();
  gtk_table_attach_defaults(GTK_TABLE(table), eventbox_for_image, 0, 1, 0, 1);
  gtk_widget_show(eventbox_for_image);
  tooltips_str = "Editor behaviours\n"
    ". Double click on an icon to select a new one\n"
    ". Drag and drop an icon to move it on the grid\n"
    ". Press the remove button to delete an application\n"
    ". Use the arrow buttons to see all the grid\n"
    ". The red cross is the positon of the cursor";
  gtk_tooltips_set_tip(e->apwal->tips, eventbox_for_image, tooltips_str, NULL);
  image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
                                   GTK_ICON_SIZE_MENU);
//                                 GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_container_add(GTK_CONTAINER(eventbox_for_image), image);
  gtk_widget_show(image);

  // eventbox
  eventbox = gtk_event_box_new();
  g_signal_connect(G_OBJECT(eventbox), "button_press_event",
                   G_CALLBACK(editor_button_press_event), e);
  g_signal_connect(G_OBJECT(eventbox), "button_release_event",
                   G_CALLBACK(editor_button_release_event), e);
  g_signal_connect(G_OBJECT(eventbox), "motion_notify_event",
                   G_CALLBACK(editor_motion_notify_event), e);
  gtk_table_attach_defaults(GTK_TABLE(table), eventbox, 1, 2, 1, 2);
  gtk_widget_show(eventbox);

  // image
  e->image = gtk_image_new();
  gtk_container_add(GTK_CONTAINER(eventbox), e->image);
  gtk_widget_show(e->image);

  //gtk_widget_set_extension_events(eventbox, GDK_EXTENSION_EVENTS_ALL);
  //gtk_widget_add_events(eventbox, GDK_ALL_EVENTS_MASK | GDK_KEY_RELEASE_MASK);
  //GTK_WIDGET_SET_FLAGS (eventbox, GTK_CAN_FOCUS);
  //g_signal_connect(G_OBJECT(eventbox), "key_release_event",
  //                 G_CALLBACK(editor_key_release_event), e);

  //gtk_notebook_append_page(GTK_NOTEBOOK(e->apwal->notebook),
  //                         editor_vbox, editor_label);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static gboolean editor_button_press_event(GtkWidget *widget,
                                          GdkEventButton *event,
                                          editor_t *e)
{
  gint x, y;
  app_t *app;
  g_assert(widget != NULL && e != NULL && event != NULL);
  g_assert(e->apwal != NULL);

  // have the keyboard focus on the eventbox to be able to catch key bindings
  gtk_widget_grab_focus(widget);
  
  x = (((int)event->x)/ICON_WIDTH) + e->x;
  y = (((int)event->y)/ICON_WIDTH) + e->y;
  TRACE("x:%d, y:%d", x, y);
  app = app_list_at_xy(e->apwal->apps, x, y);
  if (app != NULL)
    e->drag_in_progress = TRUE;
  else
    e->drag_in_progress = FALSE;

  e->drag_start_x = x;
  e->drag_start_y = y;
  e->drag_current_x = x;
  e->drag_current_y = y;

  if (app != NULL)
  {
    e->apwal->selected_app = app;
    apwalapp_selected_app_modified(e->apwal);
  }
  editor_refresh(e);
  return TRUE;
}
// ----------------------------------------------------------------------------
static gboolean editor_motion_notify_event(GtkWidget *widget,
                                           GdkEventMotion *event,
                                          editor_t *e)
{
  gint x, y;
  g_assert(widget != NULL && e != NULL && event != NULL);

  x = (((int)event->x)/ICON_WIDTH) + e->x;
  y = (((int)event->y)/ICON_WIDTH) + e->y;
  if ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) //||
   //       ((x == e->drag_start_x) && (y == e->drag_start_y)) ) )
  { 
    e->drag_current_x = (((int)event->x)/ICON_WIDTH) + e->x;
    e->drag_current_y = (((int)event->y)/ICON_WIDTH) + e->y;
    TRACE("x:%d, y:%d", e->drag_current_x, e->drag_current_y);
    editor_refresh(e);
  }
  return TRUE;
}
// ----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
static gboolean editor_is_dblclick(editor_t *e, int x, int y)
{
  gint elapse;
  g_assert(e != NULL);
  if (e->dblclick_timer == NULL)
  {
    e->dblclick_timer = g_timer_new();
    e->dblclick_last_x = x;
    e->dblclick_last_y = y;
    return FALSE;
  }
  elapse = (int)(g_timer_elapsed(e->dblclick_timer, NULL) * 1000);
  g_timer_start(e->dblclick_timer);
  if ((e->dblclick_last_x != x) || (e->dblclick_last_y != y))
  {
    e->dblclick_last_x = x;
    e->dblclick_last_y = y;
    return FALSE;
  }
  if (elapse > DBLCLICK)
    return FALSE;

  return TRUE;
}
// -----------------------------------------------------------------------------
static gboolean editor_button_release_event(GtkWidget *widget,
                                            GdkEventButton *event,
                                            editor_t *e)
{
  int pos_x;
  int pos_y;
  app_t      *app, *newapp;
  gboolean drag_in_progress;

  g_assert(e != NULL);
  g_assert(e->apwal != NULL);
  g_assert(e->apwal->apps != NULL);

  TRACE("type:%x, x:%f, y:%f, button:%d",
          event->type, event->x, event->y, event->button);

  pos_x = (((int)event->x)/ICON_WIDTH) + e->x;
  pos_y = (((int)event->y)/ICON_WIDTH) + e->y;
  drag_in_progress = e->drag_in_progress;
  e->drag_in_progress = FALSE;
  // on double click on a app, go to icon selection tab to select the icon
  // if no app on the position of the double click, do nothing
  if (editor_is_dblclick(e, pos_x, pos_y) == TRUE)
  {
    if (e->apwal->selected_app != NULL)
      apwalapp_goto_iconsel(e->apwal);
    return TRUE;
  }

  // if drag in progress and the cursor had moved then do the drop
  // on the new location of the cursor
  if ((drag_in_progress == TRUE) &&
      ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
  {
    // if there are no app on the drop posistion, move the app
    // dragged to the new position, else nothing to do
    app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
    if (app == NULL)
    {
      newapp = app_list_at_xy(e->apwal->apps, e->drag_start_x, e->drag_start_y);
      if (newapp == NULL)
        ERR("app_list_at_xy, e->drag_start_x:%d, e->drag_start_y:%d",
            e->drag_start_x, e->drag_start_y);
      newapp->x = pos_x;
      newapp->y = pos_y;
      e->drag_start_x = pos_x;  // so we see the app 'selected' in the editor
      e->drag_start_y = pos_y;
      e->drag_current_x = pos_x;  // so we see the app 'selected' in the editor
      e->drag_current_y = pos_y;
    }
    editor_refresh(e);
    return TRUE;
  }
  // if the cursor has moved like and drag and drop but no drag in progress,
  // then do nothing (case of a drag from an empty position)
  if ((drag_in_progress == FALSE) &&
      ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
    return TRUE;

  
  // case of the simple clic
  editor_goto(e, pos_x, pos_y);
  return TRUE;
}

// -----------------------------------------------------------------------------
// selected app and show it in property.
void editor_goto(editor_t *e, int pos_x, int pos_y)
{
  int    cc;
  app_t *app, *newapp;
  // if no app on the current position then move the noname app if not
  // created. if no noname app found create one.
  // if an app is found check if a noname app exist and remove it.
  // then select the found app and refresh the properties.
  app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
  if (app == NULL)
  {
    newapp = app_list_find_noname(e->apwal->apps);
    if (newapp != NULL)
    {
      newapp->x = pos_x;
      newapp->y = pos_y;
    }
    else
    {
      newapp = app_new_noname(pos_x, pos_y);
      app_list_add(e->apwal->apps, newapp);
    }
    app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
    if (app != newapp)
      ERR("app:%p != newapp:%p", app, newapp);
  }
  else
  {
    newapp = app_list_find_noname(e->apwal->apps);
    if (newapp != NULL && newapp != app)
    {
      cc = app_list_remove(e->apwal->apps, newapp);
      if (cc)
        ERR("%s", "impossible to remove the noname app found just before.");
    }
  }
  e->apwal->selected_app = app;
  editor_refresh(e);
  apwalapp_selected_app_modified(e->apwal);
}
// ----------------------------------------------------------------------------
static void editor_arrow_up_clicked(GtkWidget *widget, editor_t *e)
{
  g_assert(e != NULL);
  //TRACE("%s", "");
  e->y--;
  editor_refresh(e);
}
// ----------------------------------------------------------------------------
static void editor_arrow_down_clicked(GtkWidget *widget, editor_t *e)
{
  g_assert(e != NULL);
  //TRACE("%s", "");
  e->y++;
  editor_refresh(e);
}
// ----------------------------------------------------------------------------
static void editor_arrow_left_clicked(GtkWidget *widget, editor_t *e)
{
  g_assert(e != NULL);
  //TRACE("%s", "");
  e->x--;
  editor_refresh(e);
}
// ----------------------------------------------------------------------------
static void editor_arrow_right_clicked(GtkWidget *widget, editor_t *e)
{
  g_assert(e != NULL);
  //TRACE("%s", "");
  e->x++;
  editor_refresh(e);
}
// ----------------------------------------------------------------------------
