/** A GtkWidget to manage specific trees where an object
 * is uniquely determined by 4 gint: father_typ ,father_id ,typ and id
 *
 *   Copyright (c) 2010 P. Vincent.    See GNU GPL ../LICENCE
 */
#include <gtk/gtksignal.h>

#include <gtk/gtkmain.h>
#include <gtk/gtkcellrenderer.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtktreeviewcolumn.h>
#include <gtk/gtktreeselection.h>

#include "ge_tree.h"

enum {
  GE_TREE_SIGNAL,
  GE_TREE_EVENT,
  GE_TREE_LAST_SIGNAL
};


static gint num_list = 0;

static void ge_tree_class_init          (Ge_treeClass *klass);
static void ge_tree_init                (Ge_tree      *ttt);

static guint ge_tree_signals[GE_TREE_LAST_SIGNAL] = { 0 };

GType ge_tree_get_type (void)
{
  static GType ttt_type = 0;

  if (!ttt_type)
    {
      static const GTypeInfo ttt_info =
	{
	  sizeof (Ge_treeClass),
	  NULL, /* base_init */
	  NULL, /* base_finalize */
	  (GClassInitFunc) ge_tree_class_init,
	  NULL, /* class_finalize */
	  NULL, /* class_data */
	  sizeof (Ge_tree),
	  0,
	  (GInstanceInitFunc) ge_tree_init,
	};

      ttt_type = g_type_register_static (GTK_TYPE_SCROLLED_WINDOW, "Ge_tree", &ttt_info, 0);
    }

  return ttt_type;
}

static void
ge_tree_class_init (Ge_treeClass *klass)
{
  ge_tree_signals[GE_TREE_SIGNAL] = g_signal_new ("ge_tree",
						  G_TYPE_FROM_CLASS (klass),
						  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
						  G_STRUCT_OFFSET (Ge_treeClass, ge_tree),
						  NULL, 
						  NULL,                
						  g_cclosure_marshal_VOID__VOID,
						  G_TYPE_NONE, 0);

  ge_tree_signals[GE_TREE_EVENT] = g_signal_new ("ge_tree_event",
						 G_TYPE_FROM_CLASS (klass),
						 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
						 G_STRUCT_OFFSET (Ge_treeClass, ge_tree),
						 NULL, 
						 NULL,                
						 g_cclosure_marshal_VOID__BOXED,
						 G_TYPE_NONE, 1,
						 GDK_TYPE_EVENT);


}

/* Appelée par ge_tree_new via g_object_new */
static void ge_tree_init (Ge_tree *ttt)
{

}

static void ge_tree_emit (GtkWidget *select ,Ge_tree *tree)
{
  GdkEvent *event;

  event = gtk_get_current_event ();
  g_signal_emit (G_OBJECT (tree) ,ge_tree_signals[GE_TREE_SIGNAL], 0);
  g_signal_emit (G_OBJECT (tree) ,ge_tree_signals[GE_TREE_EVENT], 0 ,event);

}

GtkWidget* ge_tree_new (char *title
			,char *title_col1
			,char *title_col2
			,void  title_col2_edited (GtkCellRendererText *cell,
						 gchar               *path_string,
						 gchar               *new_text,
						 gpointer             user_data))
{
  Ge_tree *ttt;

  GtkCellRenderer   *renderer;
  GtkTreeViewColumn *col;
  GtkTreeSelection  *select;


  ttt = g_object_new (GE_TREE_TYPE, NULL);

  /* Create the graph tree */
  ttt->store = gtk_tree_store_new ( GE_TREE_NB_COLS
				    ,G_TYPE_INT       /* GE_TREE_TYP    */
				    ,G_TYPE_INT       /* GE_TREE_ID     */
				    ,G_TYPE_INT       /* GE_TREE_PARENT_TYP */
				    ,G_TYPE_INT       /* GE_TREE_PARENT_ID */
				    ,G_TYPE_STRING    /* GE_TREE_ID_STR */
				    ,G_TYPE_STRING    /* GE_TREE_LABEL  */
				    ,G_TYPE_BOOLEAN   /* GE_TREE_HIDDEN */
				    ,G_TYPE_INT       /* GE_TREE_STYLE_COLUMN  */
				    ,G_TYPE_INT       /* GE_TREE_WEIGHT_COLUMN */
				    );
  ttt->model = GTK_TREE_MODEL(ttt->store);
  ttt->view = gtk_tree_view_new_with_model (ttt->model);
  
  /* TreeView definitions */
  
  /* First column:  GE_TREE_ID_STR   */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer,"family" ,"Monospace" ,NULL);
  col = gtk_tree_view_column_new_with_attributes (title_col1
  						  ,renderer
  						  ,"text"   ,GE_TREE_ID_STR
						  ,"style"  ,GE_TREE_STYLE_COLUMN
						  ,"weight" ,GE_TREE_WEIGHT_COLUMN
  						  ,NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (ttt->view), col);
  
  /* Second column: GE_TREE_LABEL  */
  renderer = gtk_cell_renderer_text_new ();
  g_object_set (renderer ,"family"   ,"Monospace" ,NULL);
  g_object_set (renderer ,"editable" ,TRUE        ,NULL);
  col = gtk_tree_view_column_new_with_attributes (title_col2
  						  ,renderer
  						  ,"text" ,GE_TREE_LABEL
  						  ,NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (ttt->view), col);
  
  gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW(ttt)
				   ,GTK_POLICY_AUTOMATIC
				   ,GTK_POLICY_AUTOMATIC);

  /* Set selection mode */
  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
  // gtk_tree_selection_set_mode (select ,GTK_SELECTION_SINGLE);
  gtk_tree_selection_set_mode (select ,GTK_SELECTION_MULTIPLE);

  g_signal_connect (select ,"changed" ,G_CALLBACK (ge_tree_emit) ,ttt);

  gtk_container_add(GTK_CONTAINER(ttt) ,ttt->view);

  g_signal_connect (renderer ,"edited" ,(GCallback) title_col2_edited ,NULL);

  num_list++;
  return GTK_WIDGET (ttt);
}

/**
 * Append a new object after iter "parent"
 */
void ge_tree_append (Ge_tree *ttt ,GtkTreeIter *parent ,GtkTreeIter **child
		     ,gint typ ,gint id
		     ,gint father_typ ,gint father_id
		     ,gchar *sid ,gchar *label
		     ,gboolean hidden)
{
  gtk_tree_store_append (ttt->store ,*child  ,parent);
  if (hidden) {
    gtk_tree_store_set    (ttt->store ,*child
			   ,GE_TREE_TYP        ,typ
			   ,GE_TREE_ID         ,id
			   ,GE_TREE_PARENT_TYP ,father_typ
			   ,GE_TREE_PARENT_ID  ,father_id
			   ,GE_TREE_ID_STR     ,sid
			   ,GE_TREE_LABEL      ,label
			   ,GE_TREE_HIDDEN     ,hidden
			   ,GE_TREE_STYLE_COLUMN  ,PANGO_STYLE_ITALIC
			   ,GE_TREE_WEIGHT_COLUMN ,PANGO_WEIGHT_ULTRALIGHT
			 ,-1);
  } else {
    gtk_tree_store_set    (ttt->store ,*child
			   ,GE_TREE_TYP        ,typ
			   ,GE_TREE_ID         ,id
			   ,GE_TREE_PARENT_TYP ,father_typ
			   ,GE_TREE_PARENT_ID  ,father_id
			   ,GE_TREE_ID_STR     ,sid
			   ,GE_TREE_LABEL      ,label
			   ,GE_TREE_HIDDEN     ,hidden
			   ,GE_TREE_STYLE_COLUMN  ,PANGO_STYLE_NORMAL
			   ,GE_TREE_WEIGHT_COLUMN ,PANGO_WEIGHT_BOLD
			 ,-1);
  }
}

/**
 * Clear all the tree
 */
void ge_tree_clear (Ge_tree *ttt)
{
  gtk_tree_store_clear (ttt->store);
}

/**
 * Remove the object targeted by (father_typ ,father_id ,typ ,id)
 */
gboolean ge_tree_remove (Ge_tree *ttt ,gint father_typ ,gint father_id ,gint typ ,gint id)
{
  GtkTreeIter iter;
  if (ge_tree_get_iter (ttt ,father_typ ,father_id ,typ ,id ,&iter)) {
    gtk_tree_store_remove (ttt->store ,&iter);
    return TRUE;
  }
  return FALSE;
}


/**
 * Expand the tree at the object targeted by (father_typ ,father_id ,typ ,id)
 */
gboolean ge_tree_expand (Ge_tree *ttt ,gint father_typ ,gint father_id ,gint typ ,gint id)
{
  GtkTreeIter iter;
  GtkTreePath *path;

  if (ge_tree_get_iter (ttt ,father_typ ,father_id ,typ ,id ,&iter)) {
    path = gtk_tree_model_get_path (ttt->model ,&iter);
    gtk_tree_view_expand_to_path (GTK_TREE_VIEW(ttt->view) ,path);
    return TRUE;
  }
  return FALSE;
}

/**
 *
 */
void ge_tree_unselect_all (Ge_tree *ttt)
{
  GtkTreeSelection *select;

  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
  gtk_tree_selection_unselect_all (select);
}

/**
 * Returns TRUE if ONE row and only one is selected
 * and returns also (father_typ ,father_id ,typ ,id ,sid ,label ,hidden) int the args 
 */
gboolean ge_tree_get_selected (Ge_tree *ttt
			   ,gint *typ        ,gint *id
			   ,gint *father_typ ,gint *father_id
			   ,gchar **sid ,gchar **label
			   ,gboolean *hidden)
{
  GtkTreeIter iter;
  GtkTreeSelection *select;
  gint n;
  GList *lpath;

  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
  n = gtk_tree_selection_count_selected_rows (select);
  if (n != 1) return FALSE;

  lpath = gtk_tree_selection_get_selected_rows (select ,&(ttt->model));
  if (gtk_tree_model_get_iter (ttt->model ,&iter ,lpath->data)) {
    gtk_tree_model_get (ttt->model ,&iter 
  			,GE_TREE_TYP        ,typ
  			,GE_TREE_ID         ,id
  			,GE_TREE_PARENT_TYP ,father_typ
  			,GE_TREE_PARENT_ID  ,father_id
  			,GE_TREE_ID_STR     ,sid
  			,GE_TREE_LABEL      ,label
  			,GE_TREE_HIDDEN     ,hidden
  			,-1);
    return TRUE;
  }
  return FALSE;
}

/**
 * Returns n > 0  if all the n selected rows have the same GE_TREE_TYP
 * If not or if no row selected, returns 0
 */
gint ge_tree_sel_is_homogeneous (Ge_tree *ttt ,gint *typ)
{
  GtkTreeIter iter;
  GtkTreeSelection *select;
  GList *lpath;
  gint old_typ = -1 ,n;

  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
  lpath  = gtk_tree_selection_get_selected_rows (select ,&(ttt->model));
  n = 0;
  while (lpath != NULL) {
    if (gtk_tree_model_get_iter (ttt->model ,&iter ,lpath->data)) {
      gtk_tree_model_get (ttt->model ,&iter ,GE_TREE_TYP ,typ ,-1);
      if (n == 0) old_typ = *typ;
      if (*typ != old_typ) return 0;
      n++;
      lpath = g_list_next (lpath);
    }
  }
  return n;
}

/**
 * Returns n > 0  if all the n selected rows have a GE_TREE_TYP
 * between  start and stop and a boolean array with_type 
 * giving the types present in the selection.
 * If if no row selected, or one typ out of range, returns 0
 */
gint ge_tree_sel_is_in_range (Ge_tree *ttt ,gint start ,gint stop ,gboolean *with_type)
{
  GtkTreeIter iter;
  GtkTreeSelection *select;
  GList *lpath;
  gint typ ,n;
  for (typ = start; typ < stop+1; typ++) with_type[typ] = FALSE;
  
  select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
  lpath  = gtk_tree_selection_get_selected_rows (select ,&(ttt->model));
  n = 0;
  while (lpath != NULL) {
    if (gtk_tree_model_get_iter (ttt->model ,&iter ,lpath->data)) {
      gtk_tree_model_get (ttt->model ,&iter ,GE_TREE_TYP ,&typ ,-1);
      if (typ < start || typ > stop) return 0;
      with_type[typ] = TRUE;
      n++;
      lpath = g_list_next (lpath);
    }
  }
  return n;
}


/**
 * Select the object targeted by  (father_typ ,father_id ,typ ,id)
 */
gboolean ge_tree_select (Ge_tree *ttt ,gint father_typ ,gint father_id ,gint typ ,gint id)
{
  GtkTreeIter iter;
  //  GtkTreePath *path;
  GtkTreeSelection *select;

  if (ge_tree_get_iter (ttt ,father_typ ,father_id ,typ ,id ,&iter)) {
    //    path = gtk_tree_model_get_path (ttt->model ,&iter);
    //    gtk_tree_view_expand_to_path (GTK_TREE_VIEW(ttt->view) ,path);
    select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
    gtk_tree_selection_select_iter (select ,&iter);
    return TRUE;
  }
  return FALSE;
}

/**
 * As ge_tree_select doesn't work for root of the tree...
 */
gboolean ge_tree_select_root (Ge_tree *ttt)
{
  GtkTreeIter iter;
  GtkTreeSelection *select;
  if (gtk_tree_model_get_iter_first (ttt->model ,&iter)) {
    select = gtk_tree_view_get_selection (GTK_TREE_VIEW (ttt->view));
    gtk_tree_selection_select_iter (select ,&iter);
    return TRUE;
  }
  return FALSE;
    

}

static gint p_typ ,p_id ,c_typ ,c_id;
static gint stamp;
static gpointer user_data ,user_data2 ,user_data3;
static gboolean found = FALSE;

static gboolean ge_tree_walk (GtkTreeModel *model ,GtkTreePath *path ,GtkTreeIter *iter ,gpointer data)
{
  gint typ ,id;
  GtkTreeIter parent;

  gtk_tree_model_get (model ,iter ,GE_TREE_TYP ,&typ ,GE_TREE_ID ,&id ,-1);
  if (c_typ == typ && c_id == id) {
    if (gtk_tree_model_iter_parent (model ,&parent ,iter)) {
      gtk_tree_model_get (model ,&parent ,GE_TREE_TYP ,&typ ,GE_TREE_ID ,&id ,-1);
      if (p_typ == typ && p_id == id) {
	stamp      = iter->stamp;
	user_data  = iter->user_data;
	user_data2 = iter->user_data2;
	user_data3 = iter->user_data3;
	found = TRUE;
	return TRUE;
      }
    }
  }
  return FALSE;
}

/**
 * Get the iter of the object targeted by (father_typ ,father_id ,typ ,id)
 */
gboolean ge_tree_get_iter (Ge_tree *ttt ,gint father_typ ,gint father_id ,gint typ ,gint id ,GtkTreeIter *iter)
{
  GtkTreeIter parent;

  if (gtk_tree_model_get_iter_first (ttt->model ,&parent)) {
    p_typ = father_typ;
    p_id  = father_id;
    c_typ = typ;
    c_id  = id;
    found = FALSE;
    gtk_tree_model_foreach (ttt->model ,ge_tree_walk ,iter);
    if (found) {
      iter->stamp      = stamp;
      iter->user_data  = user_data;
      iter->user_data2 = user_data2;
      iter->user_data3 = user_data3;
    }
  }
  return found;
}  
