/*
This source file is part of Scol
For the latest info, see http://www.scolring.org

Copyright (c) 2010 Stephane Bisaro, aka Iri <iri@irizone.net>

This program 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 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser 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, or go to
http://www.gnu.org/copyleft/lesser.txt

For others informations, please contact us from http://www.scolring.org/
*/






/*
$Iri

Récupération des callbacks Scol depuis un fichier .glade
========================================================

GtkBuilder permet de définir, directement depuis le fichier .glade, un certain
nombre de callbacks sur les widgets.
Comment les récupérer pour appeler la "bonne" callback Scol ?

- 1ere chose à faire, récupérer ces callbacks depuis GtkBuilder

Pour cela, pas de difficultés majeures, les développeurs de Gtk+ ont conçu la
fonction ' gtk_builder_connect_signals_full ' : elle demande une callback
qui traitera, pour chaque signal défini dans le .glade, le comportement à adopter.

Ainsi, pour chaque signal, j'ai (entre autres) :
* le GObject associé
* le nom complet du signal
* le nom de la callback (typiquement C mais ici c'est le nom de la callback Scol)
* un paramètreutilisateur arbitraire qui me permet de passer la machine Scol courante
* et d'autres arguments qui ne me sont a priori moins utile pour notre discussion.

- 2e chose à faire, créer les objets Scol correspondants à chaque widget

Là aussi, pas de difficultés majeures, j'ai les éléments nécessaires pour les créer.

- 3e chose à faire, ajouter la callback à la machine Scol (OBJaddrefex)

Là, les choses commencent à se compliquer. Je dois avoir dans la pile :
* à l'étage 2 : l'objet Scol corresponddant au widget
* à l'étage 1 : la callback Scol
* à l'étage 0 : le paramètre utilisateur Scol

L'objet Scol est ok.
La callback Scol, j'ai son nom sous forme de chaîne de caractères (const gchar *).
Faut que j'approfondisse ce sujet
Quant au paramètre utilisateur, l'ennui vient qu'au mieux, dans le fichier .glade,
onne peut y mettre qu'une chaîne. Ça limite donc fortement l'intérêt vis à vis
de Scol

Enfin, il me faut aussi un drapeau pour chaque callback (entre OBJaddreflex et
OBJbeginreflex).
Je peux faire cela avec un compteur qui s'incrémente à chaque signal traité. Sa
valeur fait alors de flag et est poussée dans la pile (MMpush) puis dépilée (MMpull)
avant d'appeler OBJbeginreflex.
Problème, si je push bien la valeur et qu'un MMget (m, 0) me redonne bien la valeur,
cettedernière se perd lors de l'appel de GCallback (exécutée lorsqu'un évènement
se produit pour le signal donné). En 0, j'ai l'objet Scol (qui était en logiquement
en 1 après mon push ... Ce doit être un truc très con mais je ne vois pas pour
l'instant.

En attendant, vu les problèmes liés aux paramètresutilisateurs Scol (uniquement
des chaînes), je laisse provisoirement de côté ladéfinition automatisée des
reflexesScol depuis un .glade.
Il faudra faire de façon classique, par la définition des callbacks.

Bien sur, cela n'impacte pas la création d'interfaces via GtkBuilder !
*/



#include "../include/scol_gtk_builder.h"




static int scol_gtk_builder_reflexes_counter ()
{
    static int counter = -1;
    counter++;
    return counter;
}

/*
BEGIN
*/

static gboolean scol_gtk_builder_reflex (GObject *object, gpointer m2m)
{
    int c, cb, tmp_res;
    mmachine m;

    g_message ("Callback !!");
    m = (mmachine) m2m;

g_message ("0 : %d - 1 : %d - 2 : %d ; 3 : %d - 4 : %d", MMget (m, 0), MMget (m, 1), MMget (m, 2), MMget (m, 3), MMget (m, 4));
    /*c = MMget (m, 1);*/
    INVERT (m, 0, 1);
    c = MMpull (m);
g_message ("c = %d", c);
g_message ("0 : %d - 1 : %d - 2 : %d ; 3 : %d - 4 : %d", MMget (m, 0), MMget (m, 1), MMget (m, 2), MMget (m, 3), MMget (m, 4));
    cb = OBJbeginreflex (m, ObjGtkWidgetType, (int) object, c);
    if (cb)
    {
        g_message ("scol_gtk_builder_reflex error : no callback defined ! errcode = %d", cb);
        return FALSE;
    }

    if (OBJcallreflex (m, 0)) return TRUE;
    return FALSE;
}

static void scol_gtk_builder_reflexes_define (GtkBuilder *builder,
                                                GObject *object,            /* the current processed object */
                                                const gchar *signal_name,   /* the name ofthesignal */
                                                const gchar *handler_name,  /* the name of the function */
                                                GObject *connect_object,    /* will keep object alive */
                                                GConnectFlags flags,
                                                gpointer m2m)                 /* current mmachine */
{
    int c, m_handle;
    /*GtkWidget *widget;*/
    mmachine m;
    /*GValue v = {0};

    g_object_get_property (object, "object", &v);
g_message ("signal_name : %s", g_value_get_string (&v));
g_value_unset (&v);*/
    m = (mmachine) m2m;
    c = ITOM (scol_gtk_builder_reflexes_counter ());
    /*widget = GTK_WIDGET (object);*/

    /*
    In the stack, we have :
    stage 0 : the channel
    */
    /*MMpush (m, MMgetglobal (m, OFFSCCUR)); */ /*  set the current channel */
    SCOL_gtk_memory_createObjectTAB (m, object, ObjGtkWidgetType, OBJ2DGTK_WIDGET_HANDLE);
    m_handle = MMget (m, 0);
    MMpush (m, c);
    /*MMset (m, 1, c);
    MMset (m, 0, m_handle);
    MMpush (m, m_handle);*/  /* reflex */
    MMpush (m, ITOM (10));     /* user param */

    /*
    At this time, in the stack, we have :
    stage 0 : the user parameter
    stage 1 : the Scol reflex
    stage 2 : the Scol object
    stage 3 : the counter
    */
g_message ("0 : %d - 1 : %d - 2 : %d ; 3 : %d - 4 : %d", MMget (m, 0), MMget (m, 1), MMget (m, 2), MMget (m, 3), MMget (m, 4));
    OBJaddreflex (m, ObjGtkWidgetType, c);
    /*
    Now, we have in the stack :
    stage 0 : the Scol object
    stage 1 : the counter
    */
    /*MMpush (m, 0);*/
g_message ("before c :> 0 : %d - 1 : %d - 2 : %d ; 3 : %d - 4 : %d", MMget (m, 0), MMget (m, 1), MMget (m, 2), MMget (m, 3), MMget (m, 4));
g_message ("c before : %d  %d", c, MMget (m, 1));
/*g_message ("message : %d", OBJfindTM(m, ObjGtkWidgetType, MMget(m, 0)));*/



/*MMpush (m, MMget (m, 1));*/
/*MMpush (m, MMget (m, 0));*/
MMpush (m, NIL);

    if (connect_object)
        g_signal_connect_object (object, signal_name, G_CALLBACK (scol_gtk_builder_reflex), (gpointer) m, flags);
    else
        g_signal_connect_data (object, signal_name, G_CALLBACK (scol_gtk_builder_reflex), (gpointer) m, NULL /* TODO */, G_CONNECT_AFTER);
g_message ("0 : %d - 1 : %d - 2 : %d ; 3 : %d - 4 : %d", MMget (m, 0), MMget (m, 1), MMget (m, 2), MMget (m, 3), MMget (m, 4));
    return;
}

void scol_gtk_builder_reflexes_set (mmachine m, GtkBuilder *builder)
{
    gtk_builder_connect_signals_full (builder, (GtkBuilderConnectFunc) scol_gtk_builder_reflexes_define, (gpointer) m);
    return;
 /*

    GSList *objects;
    GObject object;

    objects = gtk_builder_get_objects (builder);

    while (objects != NULL)
    {
        object = objects->data;



        objects = objects->next;
    }
    g_slist_free (objects);*/
}


/*
END
*/




static void scol_gtk_builder_add_scol_object (GObject *object, mmachine m)
{
    int exist;

    g_object_ref (object);

    exist = OBJfindTH (m, ObjGtkWidgetType, (int) object);
    if (exist == -1)    /* object not already created because not found in the current machine. So we create it ! */
    {
        MMpush (m, MMgetglobal (m, OFFSCCUR));  /*  set the current channel */
        SCOL_gtk_memory_createObjectTAB (m, object, ObjGtkWidgetType, OBJ2DGTK_WIDGET_HANDLE);
        MMpull (m);
        return;
    }

    MMpush (m, MMfetch (m, exist, OFFOBJMAG));
    return;
}


static void scol_gtk_builder_add_scol_objects (GtkBuilder *builder, mmachine m)
{
    GSList *objects = NULL;

    objects = gtk_builder_get_objects (builder);

    while (objects != NULL)
    {
        scol_gtk_builder_add_scol_object (objects->data, m);
        objects = objects->next;
    }
    return;
}

static void scol_gtk_builder_add_scol_childs (GObject *object, mmachine m)
{
    return;
}


/**
 * \brief Add datas to a builder.
 *
 * \param widget : GtkBuilder * : any builder already created
 * \param s : const gchar * : a filename or a string which contains the datas
 * \param flag : int : a flag : 0 from a file, 1 from a string
 * \return : int : a positive value if success, a negative value if an argument is bad, 0 if another error
 */
gint scol_gtk_builder_add_datas (GtkBuilder *widget, const gchar *s, int flag)
{
    guint r;
    GError *pErr = NULL;

    if ((s == NULL) || (widget == NULL))
        return -1;

    switch (flag)
    {
        case (0) :  /* from file */
           r =  gtk_builder_add_from_file (widget, s, &pErr);
           break;

        case (1) :
            if (s[strlen (s)] != '\0')
                return -2;
            r = gtk_builder_add_from_string (widget, s, -1, &pErr);
            break;

        default :
            r = -3;
    }

    if (pErr != NULL)
    {
        g_warning ("scol_gtk_builder_add_datas error : %s  %s", g_quark_to_string (pErr->domain), pErr->message);
        g_error_free (pErr);
        return r;
    }
    return r;
}





/**
 * \brief Create a builder from a glade file - fun [Chn P] ObjGtkWidget
 *
 * stage 0 : P : a file read reference
 * stage 1 : Chn : a channel
 * return : ObjGtkWidget : the builder object or nil if error (in this case, cause in the log)
 */
int SCOL_gtkBuilderNewFromFile (mmachine m)
{
    int mchannel, mfilename;
    int k, len;
    gchar *filename;
    GtkBuilder *builder;

    g_message ("SCOL_gtkBuilderNewFromFile : entering");

    mfilename = MMpull (m);
    mchannel = MMget (m, 0);

    if (mchannel == NIL)
    {
        g_warning ("SCOL_gtkBuilderNewFromFile error : channel is nil");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    if (mfilename == NIL)
    {
        g_warning ("SCOL_gtkBuilderNewFromFile error : filename is nil");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    builder = gtk_builder_new ();

    len = MMsizestr (m, MTOP (mfilename));
    filename = g_malloc (sizeof (gchar) * (len + 1));
    if (filename == NULL)
        g_error ("SCOL_gtkBuilderNewFromFile error : not enough memory");

    strncpy (filename, MMstartstr (m, MTOP (mfilename)), len);
    filename[len] = '\0';

    k = scol_gtk_builder_add_datas (builder, filename, 0);
    if (k < 1)
    {
        g_warning ("SCOL_gtkBuilderNewFromFile an error occurs. This code is '%d '", k);
        MMpull (m);
        MMpush (m, NIL);
        g_free (filename);
        return 0;
    }
    g_free (filename);
    /*scol_gtk_builder_reflexes_set (m, builder);*/ /* See french discussion above */
    /*MMpush (m, MMgetglobal (m, OFFSCCUR));*/
    scol_gtk_builder_add_scol_objects (builder, m);
    k = SCOL_gtk_memory_createObjectTAB (m, builder, ObjGtkWidgetType, OBJ2DGTK_WIDGET_HANDLE);

    return k;
}




/**
 * \brief Create a builder from a glade file - fun [Chn S] ObjGtkWidget
 *
 * stage 0 : S : a string which contains the datas
 * stage 1 : Chn : a channel
 * return : ObjGtkWidget : the builder object or nil if error (in this case, cause in the log)
 */
int SCOL_gtkBuilderNewFromString (mmachine m)
{
    int mchannel, mstring;
    int k, len;
    gchar *s;
    GtkBuilder *builder;

    g_message ("SCOL_gtkBuilderNewFromString : entering");

    mstring = MTOP (MMpull (m));
    mchannel = MMget (m, 0);

    if (mchannel == NIL)
    {
        g_warning ("SCOL_gtkBuilderNewFromString error : channel is nil");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }
    if (mstring == NIL)
    {
        g_warning ("SCOL_gtkBuilderNewFromString error : string is nil");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    builder = gtk_builder_new ();

    len = MMsizestr (m, mstring);
    s = g_malloc (MMsizestr (m, mstring));
    if (s == NULL)
        g_error ("SCOL_gtkBuilderNewFromFile error : not enough memory");
    strncpy (s, MMstartstr (m, mstring), len);
    s[len] = '\0';

    k = scol_gtk_builder_add_datas (builder, s, 1);
    if (k < 1)
    {
        g_warning ("SCOL_gtkBuilderNewFromString an error occurs. This code is '%d '", k);
        MMpull (m);
        MMpush (m, NIL);
        g_free (s);
        return 0;
    }
    g_free (s);
    return SCOL_gtk_memory_createObjectTAB (m, builder, ObjGtkWidgetType, OBJ2DGTK_WIDGET_HANDLE);
}

/**
 * \brief Create a Scol object from the name of the widget (in the gtkBuilder)
 * fun [ObjGtkWidget S] ObjGtkWidget
 *
 * Stack :
 * stage 0 : S : id : the name of the widget (as defined in the .xml file / .glade file)
 * stage 1 : ObjGtkWidget : builder : the builder
 * Return : ObjGtkWidget : this Scol object (created if needed)
 */
int SCOL_gtkBuilderWidgetGet (mmachine m)
{
    int mbuilder, mid;
    GtkBuilder *builder;
    GObject *object;

    g_message ("SCOL_gtkBuilderWidgetGet : entering");

    mid = MTOP (MMpull (m));
    mbuilder = MTOP (MMpull (m));

    if ((mbuilder == NIL) || (mid == NIL))
    {
        g_warning ("SCOL_gtkBuilderWidgetGet error : argument is nil");
        MMpush (m, NIL);
        return 0;
    }

    builder = (GtkBuilder *) MMfetch (m, mbuilder, OBJ2DGTK_WIDGET_HANDLE);
    object = gtk_builder_get_object (builder, MMstartstr (m, mid));

    if (object == NULL)
    {
        g_warning ("SCOL_gtkBuilderWidgetGet error : object '%s' not found", MMstartstr (m, mid));
        MMpush (m, NIL);
        return 0;
    }

    scol_gtk_builder_add_scol_object (object, m);
    return 0;
}

int SCOL_gtkBuilderCBset (mmachine m)
{
    int mobject, msignal, mfun, mup;
    int len, reflexflag;
    GObject *object;
    gchar *ssignal;

    g_message ("SCOL_gtkBuilderCBset : entering");

    mup = MMpull (m);
    mfun = MMpull (m);
    msignal = MTOP (MMpull (m));
    mobject = MTOP (MMpull (m));

    if ((mobject == NIL) || (msignal == NIL))
    {
        g_warning ("SCOL_gtkBuilderCBset error : argument is nil");
        return 0;
    }

    object = (GObject *) MMfetch (m, mobject, OBJ2DGTK_WIDGET_HANDLE);
    len = MMsizestr (m, msignal);
    ssignal = g_malloc (sizeof (gchar) * (len + 1));
    strncpy (ssignal, MMstartstr (m, msignal), len);
    ssignal[len] = '\0';

    reflexflag = scol_gtk_builder_reflexes_counter ();

    MMpush (m, reflexflag);
    MMpush (m, PTOM (mobject));
    MMpush (m, mfun);
    MMpush (m, mup);
    OBJaddreflex (m, ObjGtkWidgetType, reflexflag);

    scol_gtk_builder_cb_define (object, ssignal, m);
    g_free (ssignal);
    MMpush (m, PTOM (mobject));
    return 0;
}







/* API définitions : */

char* gtk_bld_name[GTK_BUILDER_PKG_NB]=
{
    "_gtkBuilderNewFromFile",
    "_gtkBuilderNewFromString",
    "_gtkBuilderWidgetGet",
    "_gtkBuilderCBset"
};

int (*gtk_bld_fun[GTK_BUILDER_PKG_NB])(mmachine m)=
{
    SCOL_gtkBuilderNewFromFile,
    SCOL_gtkBuilderNewFromString,
    SCOL_gtkBuilderWidgetGet,
    SCOL_gtkBuilderCBset
};

int gtk_bld_narg[GTK_BUILDER_PKG_NB]=
{
    2,
    2,
    2,
    4           /* _gtkBuilderCBset */
};

char* gtk_bld_type[GTK_BUILDER_PKG_NB]=
{
    "fun [Chn P] ObjGtkWidget",
    "fun [Chn S] ObjGtkWidget",
    "fun [ObjGtkWidget S] ObjGtkWidget",
    "fun [ObjGtkWidget S u0 u1] ObjGtkWidget"          /* _gtkBuilderCBset */
};

int SCOLloadGTKbuilder (mmachine m)
{
    int k;

    g_message ("SCOLloadGTKbuilder : entering");

    k = PKhardpak (m, "GTK2DbuilderEngine", GTK_BUILDER_PKG_NB, gtk_bld_name, gtk_bld_fun, gtk_bld_narg, gtk_bld_type);
    return k;
}


