
#include "event-stack.h"

#include "gcore/gcomm.h"
#include "ggui/dialog.h"
#include <stdio.h>
#include <stdlib.h>

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

/*****************************************************************************/

int gtv_called_from_main(void)
{
    return sic_get_current_task_id() == sic_get_master_task_id();
}

/*****************************************************************************/

#define _CREATE_WINDOW_EVENT 1
#define _REFRESH_EVENT 2
#define _CLEAR_EVENT 3
#define _DESTROY_EVENT 4
#define _POINTS_EVENT 5
#define _PEN_EVENT 6
#define _FLUSH_STACK_EVENT 7
#define _DESTROY_SEGMENT_EVENT 8
#define _DESTROY_DIRECTORY_EVENT 9
#define _COLORMAP_EVENT 10
#define _MODIFIED_VARIABLE_EVENT 11
#define _WINDOW_OPERATION_EVENT 12
#define _CALLBACK_EVENT 13
#define _REFRESH_NEXT_EVENT -1

static char* _event_names[] = {
    "<NULL>",
    "CREATE_WINDOW_EVENT",
    "REFRESH_EVENT",
    "CLEAR_EVENT",
    "DESTROY_EVENT",
    "POINTS_EVENT",
    "PEN_EVENT",
    "FLUSH_STACK_EVENT",
    "DESTROY_SEGMENT_EVENT",
    "DESTROY_DIRECTORY_EVENT",
    "COLORMAP_EVENT",
    "MODIFIED_VARIABLE_EVENT",
    "WINDOW_OPERATION_EVENT",
    "<NULL>",
};

typedef struct _basic_event {
    int type;
    int stack_index;
    void (*on_event)(void *event);
} _basic_event_t;

#define _EVENT_STACK_MAX 1024
static _basic_event_t *_event_stack[_EVENT_STACK_MAX] = { NULL, };
static int _event_stack_index = 0;
static int _event_stack_start_index = 0;

static int _event_stack_opened = 0;

static void _open_event_stack()
{
    sic_open_event_stack();
    if (_event_stack_opened)
        fprintf(stderr, "_open_event_stack failed\n");
    _event_stack_opened = 1;
}

static void _close_event_stack()
{
    if (!_event_stack_opened)
        fprintf(stderr, "_close_event_stack failed\n");
    _event_stack_opened = 0;
    sic_close_event_stack();
}

static int _push_event( void (*on_event)(void *event), _basic_event_t *event)
{
    static int overflow = 0;
#ifdef _GTV_EVENT_TRACE
    int nb;
#endif

//     if (gtv_open_segment_writer() && gtv_called_from_main()) {
//         fprintf( stderr, "***************************************************\n");
//         fprintf( stderr, "* Push event inside gtv_open_segments_for_writing *\n");
//         fprintf( stderr, "***************************************************\n");
//         return 1;
//     }
    if (overflow) {
        fprintf( stderr, "Overflow. Don't push event.\n");
        return 1;
    }
    if (_event_stack[_event_stack_index] != NULL) {
        overflow = 1;
        fprintf( stderr, "Event stack overflow. Flush stack.\n");
        _close_event_stack();
        gtv_push_flush( 0);
        _open_event_stack();
        overflow = 0;
    }
    _event_stack[_event_stack_index] = event;
    event->stack_index = _event_stack_index;
    event->on_event = on_event;
    _event_stack_index++;
    if (_event_stack_index == _EVENT_STACK_MAX) {
        _event_stack_index = 0;
    }
#ifdef _GTV_EVENT_TRACE
    nb = (_event_stack_index - _event_stack_start_index) % _EVENT_STACK_MAX;
    if (nb > 10)
        printf( "_push_event: %d events\n", nb);
#endif
    if (gtv_graph_api->graph_push_event != NULL)
        gtv_graph_api->graph_push_event( event);
    return 0;
}

static void disable_event( int index)
{
    _event_stack[index]->type = 0;
}

static void _disable_events()
{
    int i;

    i = _event_stack_start_index;
    while (i != _event_stack_index) {
        disable_event( i);
        i++;
        if (i == _EVENT_STACK_MAX)
            i = 0;
    }
}

static void delete_event( void *event)
{
    int type = ((_basic_event_t *)event)->type;
    int index = ((_basic_event_t *)event)->stack_index;

    if (type >= 0) {
        _open_event_stack();
        free(event);
        // event pushed
        _event_stack[index] = NULL;
        if (index == _event_stack_start_index) {
            while (_event_stack_start_index != _event_stack_index &&
            _event_stack[_event_stack_start_index] == NULL) {
                _event_stack_start_index++;
                if (_event_stack_start_index == _EVENT_STACK_MAX)
                    _event_stack_start_index = 0;
            }
        }
        _close_event_stack();
    } else {
        free(event);
    }
}

static int _gen_find_next_event( int i, int type, int (*fct)(_basic_event_t *,void *),
 void *data)
{
    int match;

    if (i < 0)
        i = _event_stack_index;
    match = 0;
    while (i != _event_stack_start_index) {
        i--;
        if (i < 0)
            i = _EVENT_STACK_MAX - 1;
        if (_event_stack[i] != NULL && (_event_stack[i]->type == type || (!type
         && _event_stack[i]->type))) {
            if (fct == NULL || (*fct)(_event_stack[i], data)) {
                match = 1;
                break;
            }
        }
    }
    if (match)
        return i;
    else
        return -1;
}

static int _gen_find_event( int type, int (*fct)(_basic_event_t *,void *),
 void *data)
{
    return _gen_find_next_event( -1, type, fct, data);
}

static int _gen_find_event_before( int type, int exclude_type,
 int (*fct)(_basic_event_t *,void *), void *data)
{
    int match;
    int i;

    match = 0;
    i = _event_stack_index;
    while (i != _event_stack_start_index) {
        i--;
        if (i < 0)
            i = _EVENT_STACK_MAX - 1;
        if (_event_stack[i] != NULL && _event_stack[i]->type == exclude_type &&
         (*fct)(_event_stack[i], data))
            break;
        if (_event_stack[i] != NULL && _event_stack[i]->type == type &&
         (*fct)(_event_stack[i], data)) {
            match = 1;
            break;
        }
    }
    if (match)
        return i;
    else
        return -1;
}

void gtv_on_event( void *event)
{
    ((_basic_event_t *)event)->on_event( event);
}

void gtv_pop_events()
{
    int i;
    int save_stack_index;

    _open_event_stack();
    save_stack_index = _event_stack_index;
    i = _event_stack_start_index;
    _event_stack_start_index = -1; /* disable index update in delete_event */
    while (i != _event_stack_index) {
        _event_stack[i]->on_event( _event_stack[i]);
        // delete_event is called from on_event handler
        i++;
        if (i == _EVENT_STACK_MAX)
            i = 0;
    }
    _event_stack_start_index = _event_stack_index;
    if (_event_stack_index != save_stack_index)
        fprintf( stderr, "gtv_pop_events: stack index changed\n");
    _close_event_stack();
}

/*****************************************************************************/

typedef struct _create_window_event {
    _basic_event_t basic;
    gtv_toolbar_args_t *args;
} _create_window_event_t;

static _basic_event_t *_new_create_window_event( _create_window_event_t *event)
{
    _create_window_event_t *new_event;

    new_event = malloc( sizeof(_create_window_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static void _on_create_window(void *event)
{
    if (((_basic_event_t *)event)->type) /* if enabled */
        gtv_graph_api->graph_create_drawing_area( ((_create_window_event_t *)event)->args);
    delete_event( event);
}

/*****************************************************************************/

typedef struct _window_event {
    _basic_event_t basic;
    G_env *env;
} _window_event_t;

static _basic_event_t *_new_window_event( _window_event_t *event)
{
    _window_event_t *new_event;

    new_event = (_window_event_t *)malloc( sizeof(_window_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static int _match_window_event( _basic_event_t *event, void *data)
{
    return ((_window_event_t *)event)->env == data;
}

/*****************************************************************************/

typedef struct _refresh_event {
    _window_event_t window;
    fortran_type adr_dir;
    int mode;
    struct _refresh_event *next;
    struct _refresh_event *last;
} _refresh_event_t;

static _refresh_event_t *_new_refresh_event( _refresh_event_t *event)
{
    _refresh_event_t *new_event;

    new_event = (_refresh_event_t *)malloc(sizeof(_refresh_event_t));
    *new_event = *event;

    return new_event;
}

void _draw_refresh_event( _refresh_event_t *refresh)
{
#define win_gtview_work_1seg CFC_EXPORT_NAME(win_gtview_work_1seg)
    extern void CFC_API win_gtview_work_1seg (fortran_type, int*, fortran_type, int*, int*);
    int error = 0;

    G_env *env = refresh->window.env;
    if (env->genv_array != NULL) {
        _refresh_event_t *previous = NULL;
        gtv_open_segments_for_reading_from_graph();
        _open_event_stack();
        do {
            win_gtview_work_1seg(env->adr_dir, &env->fen_num, refresh->adr_dir,
             &refresh->mode, &error);
            refresh = refresh->next;
            if (previous != NULL)
                // first event will be deleted in _on_refresh()
                delete_event( previous);
            previous = refresh;
        } while (refresh != NULL);
        _close_event_stack();
        gtv_close_segments_for_reading_from_graph();
    }
}

static void _on_refresh(void *event)
{
    _refresh_event_t *refresh = (_refresh_event_t *)event;

    //printf("_on_refresh: %d %d\n", refresh->window.env != NULL ?
    //refresh->window.env->fen_num : -1, refresh->mode);
    if (((_basic_event_t *)event)->type) { /* if enabled */
        /* disable event to avoid conflict with push process */
        ((_basic_event_t *)event)->type = 0;
        if (refresh->mode == GTV_REFRESH_MODE_APPEND) {
            _draw_refresh_event( refresh);
        } else {
            int mode = refresh->mode;
            if (mode != GTV_REFRESH_MODE_REFRESH && mode !=
             GTV_REFRESH_MODE_COLOR)
                gtv_graph_api->graph_invalidate_window( refresh->window.env);
            else
                mode = GTV_REFRESH_MODE_REWIND;
            gtv_graph_api->graph_refresh( refresh->adr_dir, mode,
             refresh->window.env);
        }
    }
    delete_event( event);
}

static int _match_refresh_event( _basic_event_t *event, void *data)
{
    _refresh_event_t *refresh = (_refresh_event_t *)event;

    return refresh->window.env == data;
}

static int _find_refresh_event( G_env *env)
{
    return _gen_find_event( _REFRESH_EVENT, _match_refresh_event, env);
}

static int _find_next_refresh_event( int i, G_env *env)
{
    return _gen_find_next_event( i, _REFRESH_EVENT, _match_refresh_event, env);
}

/*****************************************************************************/

typedef _window_event_t _clear_event_t;

static void _do_clear( _clear_event_t *event)
{
    gtv_graph_api->graph_clear_window( event->env);
}

static void _on_clear(void *event)
{
    if (((_basic_event_t *)event)->type) /* if enabled */
        _do_clear( (_clear_event_t *)event);
    delete_event( event);
}

static int _find_clear_event( G_env *env)
{
    return _gen_find_event( _CLEAR_EVENT, _match_window_event, env);
}

/*****************************************************************************/

typedef _window_event_t _destroy_event_t;

static void _on_destroy(void *event)
{
    if (((_basic_event_t *)event)->type) /* if enabled */
        gtv_graph_api->graph_destroy_window( ((_destroy_event_t *)event)->env);
    delete_event( event);
}

static int _find_destroy_event( G_env *env)
{
    return _gen_find_event( _DESTROY_EVENT, _match_window_event, env);
}

/*****************************************************************************/

typedef struct _points_event {
    _window_event_t window;
    int npts;
    struct _point tabpts[1024];
} _points_event_t;

static _basic_event_t *_new_points_event( _points_event_t *event)
{
    _points_event_t *new_event;

    new_event = (_points_event_t *)malloc( sizeof(_points_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static void _points_add( _points_event_t *event, int x, int y)
{
    event->tabpts[event->npts].x = x;
    event->tabpts[event->npts].y = y;
    event->npts++;
}

static void _do_points( _points_event_t *event)
{
    gtv_graph_api->graph_flush_points( ((_window_event_t *)event)->env,
     event->tabpts, event->npts);
}

static void _on_points(void *event)
{
    if (((_basic_event_t *)event)->type) /* if enabled */
        _do_points( (_points_event_t *)event);
    delete_event( event);
}

static int _find_points_event( G_env *env)
{
    return _gen_find_event_before( _POINTS_EVENT, _PEN_EVENT,
     _match_window_event, env);
}

/*****************************************************************************/

typedef struct _pen_event {
    _window_event_t window;
    int pen;
    char color[32];
    int r;
    int g;
    int b;
    int width;
    int flag_dash;
    int ds[4];
} _pen_event_t;

static _basic_event_t *_new_pen_event( _pen_event_t *event)
{
    _pen_event_t *new_event;

    new_event = (_pen_event_t *)malloc( sizeof(_pen_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static void _pen_merge( _pen_event_t *event, _pen_event_t *to_merged)
{
    if (to_merged->pen >= 0)
        event->pen = to_merged->pen;
    if (to_merged->width >= 0)
        event->width = to_merged->width;
    if (to_merged->flag_dash >= 0) {
        int i;

        event->flag_dash = to_merged->flag_dash;
        for (i = 0; i < 4; i++)
            event->ds[i] = to_merged->ds[i];
    }
}

static void _do_pen( _pen_event_t *event)
{
    G_env *env = ((_window_event_t *)event)->env;
    if (event->pen >= 0)
        gtv_graph_api->graph_x_pen_invert( env);
    else if (event->r >= 0)
        gtv_graph_api->graph_x_pen_rgb( env, event->r, event->g, event->b);
    else if (event->color[0])
        gtv_graph_api->graph_x_pen_color( env, event->color);
    if (event->width >= 0)
        gtv_graph_api->graph_x_weigh( env, event->width);
    if (event->flag_dash >= 0)
        gtv_graph_api->graph_x_dash( env, event->flag_dash, event->ds);
}

static void _on_pen(void *event)
{
    if (((_basic_event_t *)event)->type) /* if enabled */
        _do_pen( (_pen_event_t *)event);
    delete_event( event);
}

static int _find_pen_event( G_env *env)
{
    return _gen_find_event_before( _PEN_EVENT, _POINTS_EVENT,
     _match_window_event, env);
}

/*****************************************************************************/

typedef struct _flush_stack_event {
    _basic_event_t basic;
    void *data;
} _flush_stack_event_t;

static void _on_do_quit(void *data)
{
    gtv_graph_api->graph_x_quit();
    sic_post_widget_created();
}

static void _on_flush_stack(void *event)
{
    //_flush_stack_event_t *e = (_flush_stack_event_t *)event;
    sic_post_widget_created();
}

static void _push_flush_stack( void (*handler)(void *data), void *data, int
 do_quit)
{
    _flush_stack_event_t event;

    event.basic.type = _FLUSH_STACK_EVENT;
    event.basic.stack_index = -1;
    if (handler != NULL) {
        event.basic.on_event = handler;
    } else if (do_quit) {
        _disable_events();
        event.basic.on_event = _on_do_quit;
    } else {
        event.basic.on_event = _on_flush_stack;
    }
    event.data = data;

    // no need to call new on event because _on_flush_stack is called before
    // leaving routine.
    gtv_graph_api->graph_push_event( &event);
    sic_wait_widget_created( );
}

/*****************************************************************************/

static void _on_zoom(void *data)
{
    gtv_graph_api->graph_x_curs( ((_flush_stack_event_t *)data)->data);
}

/*****************************************************************************/

static void _on_lens(void *data)
{
    gtv_graph_api->graph_activate_lens( ((_flush_stack_event_t *)data)->data);
}

/*****************************************************************************/

typedef struct _destroy_fortran_type_event {
    _basic_event_t basic;
    fortran_type pointer;
} _destroy_fortran_type_event_t;

static _basic_event_t *_new_destroy_fortran_type_event( _destroy_fortran_type_event_t *event)
{
    _destroy_fortran_type_event_t *new_event;

    new_event = malloc( sizeof(_destroy_fortran_type_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

/*****************************************************************************/

static void _on_destroy_segment(void *event)
{
#define gtv_delsegments CFC_EXPORT_NAME(gtv_delsegments)
    extern void CFC_API gtv_delsegments(fortran_type);

    gtv_delsegments( ((_destroy_fortran_type_event_t *)event)->pointer);
    delete_event( event);
}

/*****************************************************************************/

static void _on_destroy_directory(void *event)
{
#define gtv_deldirectories CFC_EXPORT_NAME(gtv_deldirectories)
    extern void CFC_API gtv_deldirectories(fortran_type);

    gtv_deldirectories( ((_destroy_fortran_type_event_t *)event)->pointer);
    delete_event( event);
}

/*****************************************************************************/

typedef struct _colormap_event {
    _basic_event_t basic;
    size_t colormap;
} _colormap_event_t;

static _basic_event_t *_new_colormap_event( _colormap_event_t *event)
{
    _colormap_event_t *new_event;

    new_event = malloc( sizeof(_colormap_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

/*****************************************************************************/

static void _on_colormap_set_default(void *event)
{
    if (gtv_graph_api->graph_xcolormap_set_default != NULL)
        gtv_graph_api->graph_xcolormap_set_default(
         ((_colormap_event_t *)event)->colormap);
    delete_event( event);
}

static void _on_colormap_delete(void *event)
{
    if (gtv_graph_api->graph_xcolormap_delete != NULL)
        gtv_graph_api->graph_xcolormap_delete(
         ((_colormap_event_t *)event)->colormap);
    delete_event( event);
}

/*****************************************************************************/

typedef struct _modified_variable_event {
    _basic_event_t basic;
    int widget_index;
    widget_api_t *api;
} _modified_variable_event_t;

static _basic_event_t *_new_modified_variable_event(
 _modified_variable_event_t *event)
{
    _modified_variable_event_t *new_event;

    new_event = malloc( sizeof(_modified_variable_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

/*****************************************************************************/

typedef struct _window_operation_event {
    _window_event_t window;
    window_operation_e oper;
    int data1;
    int data2;
} _window_operation_event_t;

static _basic_event_t *_new_window_operation_event( _window_operation_event_t
 *event)
{
    _window_operation_event_t *new_event;

    new_event = (_window_operation_event_t *)malloc(
     sizeof(_window_operation_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static void _on_window_operation(void *event)
{
    if (((_basic_event_t *)event)->type) { /* if enabled */
        G_env *env = ((_window_event_t *)event)->env;
        _window_operation_event_t *e = (_window_operation_event_t *)event;
        switch (e->oper) {
        case WINDOW_OPERATION_MOVE:
            gtv_graph_api->graph_move_window( env, e->data1, e->data2);
            break;
        case WINDOW_OPERATION_RESIZE:
            gtv_graph_api->graph_resize_window( env, e->data1, e->data2);
            break;
        case WINDOW_OPERATION_RAISE:
            gtv_graph_api->graph_x_clal( env);
            break;
        case WINDOW_OPERATION_LOWER:
            gtv_graph_api->graph_x_clpl( env);
            break;
        }
    }
    delete_event( event);
}

/*****************************************************************************/

typedef struct _callback_event {
    _basic_event_t basic;
    void (*callback)(void *);
    void *data;
} _callback_event_t;

static _basic_event_t *_new_callback_event( _callback_event_t
 *event)
{
    _callback_event_t *new_event;

    new_event = (_callback_event_t *)malloc(
     sizeof(_callback_event_t));
    *new_event = *event;

    return (_basic_event_t *)new_event;
}

static void _on_callback(void *event)
{
    if (((_basic_event_t *)event)->type) { /* if enabled */
    	(*((_callback_event_t *)event)->callback)(((_callback_event_t *)event)->data);
    }
    delete_event( event);
}

/*****************************************************************************/

static int _find_modified_variable_event()
{
    return _gen_find_event( _MODIFIED_VARIABLE_EVENT, NULL, NULL);
}

static void _on_modified_variable(void *event)
{
    ((_basic_event_t *)event)->type = 0; // disable event
    widget_update( ((_modified_variable_event_t *)event)->api, ((_modified_variable_event_t *)event)->widget_index);
    delete_event( event);
}

/*****************************************************************************/

void gtv_push_create_window( gtv_toolbar_args_t *args)
{
    _create_window_event_t event;
    _basic_event_t *new_event;

    event.basic.type = _CREATE_WINDOW_EVENT;
    event.args = args;

    if (!gtv_called_from_main()) {
        fprintf( stderr, "gtv_push_create_window: TODO\n");
        exit( 1);
        //_do_create_drawing_area( &event);
        return;
    }
    _open_event_stack();
    new_event = _new_create_window_event( &event);
    _push_event( _on_create_window, new_event);
    _close_event_stack();
}

static int _inspect_previous_event( _basic_event_t *event, void *data)
{
    switch (event->type) {
    case _REFRESH_EVENT:
    case _CLEAR_EVENT:
    case _DESTROY_EVENT:
    case _POINTS_EVENT:
    case _PEN_EVENT:
        if (((_window_event_t *)event)->env == data)
            return 1;
        break;
    case _DESTROY_SEGMENT_EVENT:
    case _DESTROY_DIRECTORY_EVENT:
        return 1;
    }
    return 0;
}

void gtv_push_refresh( fortran_type adr_dir, int mode, G_env *env)
{
    _refresh_event_t event;
    int i;

    /*
    if (mode == GTV_REFRESH_MODE_APPEND)
        printf(" gtv_push_refresh append: %lx %d %d\n", (long)adr_dir, env != NULL ? env->fen_num : -1, mode);
    */
    event.window.basic.type = _REFRESH_EVENT;
    event.adr_dir = adr_dir;
    event.mode = mode;
    event.window.env = env;
    event.next = NULL;
    event.last = NULL;
    if (env == NULL) {
        fprintf(stderr, "gtv_push_refresh: error, env == NULL\n");
        return;
    }

#if 0
    if (!gtv_called_from_main()) {
        fprintf( stderr, "gtv_push_refresh: NOT NORMAL\n");
        exit( 1);
    }
#endif
    _open_event_stack();
    if (env != NULL && (i = _find_destroy_event( env)) >= 0) {
        /* window to be destroyed, do nothing */
#ifdef _GTV_EVENT_TRACE
        printf( "gtv_push_refresh: found 1 destroy\n");
#endif
    } else {
        int do_push = 1;
        i = -1;
        while ((i = _gen_find_next_event( i, 0, _inspect_previous_event,
         event.window.env)) >= 0) {
            _refresh_event_t *refresh;

    //printf("gtv_push_refresh: modes %d %d\n",event.mode, refresh->mode);
            if (_event_stack[i]->type != _REFRESH_EVENT) {
                break;
            } 
            refresh = (_refresh_event_t *)_event_stack[i];
            if (refresh->mode == GTV_REFRESH_MODE_UPDATE
             || refresh->mode == GTV_REFRESH_MODE_REFRESH
             || refresh->mode == GTV_REFRESH_MODE_REWIND
             || refresh->mode == GTV_REFRESH_MODE_COLOR) {
                do_push = 0;
                if (event.mode == GTV_REFRESH_MODE_UPDATE
                 || event.mode == GTV_REFRESH_MODE_REWIND) {
                    refresh->mode = event.mode;
                }
                break;
            } else if (event.mode == GTV_REFRESH_MODE_UPDATE
             || event.mode == GTV_REFRESH_MODE_REFRESH
             || event.mode == GTV_REFRESH_MODE_REWIND
             || event.mode == GTV_REFRESH_MODE_COLOR) {
                disable_event( i);
                break;
            } else {
                _refresh_event_t *new_event;
                if (event.mode != GTV_REFRESH_MODE_APPEND || event.mode
                 != refresh->mode) {
                    fprintf(stderr, "gtv_push_refresh: unknown modes %d %d\n",
                     event.mode, refresh->mode);
                }
                do_push = 0;
                new_event = _new_refresh_event( &event);
                new_event->window.basic.type = _REFRESH_NEXT_EVENT;
                refresh->last->next = new_event;
                refresh->last = new_event;
                break;
            }
        }
        if (do_push) {
            _refresh_event_t *new_event = _new_refresh_event( &event);
            new_event->last = new_event;
            _push_event( _on_refresh, (_basic_event_t *)new_event);
        } else {
#ifdef _GTV_EVENT_TRACE
            printf( "<info> gtv_push_refresh: found 1 refresh\n");
#endif
        }
    }
    _close_event_stack();
}

void gtv_push_clear( G_env *env)
{
    _clear_event_t event;
    _basic_event_t *new_event;
    int i;

    event.basic.type = _CLEAR_EVENT;
    event.env = env;
    if (!gtv_called_from_main()) {
        fprintf( stderr, "gtv_push_clear not called from main: %d\n",
         env->fen_num);
        //_do_clear( &event); // make blinking appears
        return;
    }

    _open_event_stack();
    if ((i = _find_destroy_event( env)) >= 0) {
        /* window to be destroyed, do nothing */
#ifdef _GTV_EVENT_TRACE
        printf( "gtv_push_clear: found 1 destroy\n");
#endif
    } else {
        i = -1;
        while ((i = _find_next_refresh_event( i, event.env)) >= 0) {
            disable_event( i);
#ifdef _GTV_EVENT_TRACE
            printf( "gtv_push_clear: found 1 refresh\n");
#endif
        }
        i = _find_clear_event( env);
        if (i >= 0) {
            disable_event( i);
#ifdef _GTV_EVENT_TRACE
            printf( "gtv_push_clear: found 1 clear\n");
#endif
        }
        new_event = _new_window_event( &event);
        _push_event( _on_clear, new_event);
    }
    _close_event_stack();
}

void gtv_push_destroy( G_env *env)
{
    _destroy_event_t event;
    _basic_event_t *new_event;
    int i;

    event.basic.type = _DESTROY_EVENT;
    event.env = env;
    
    _open_event_stack();
    i = -1;
    while ((i = _find_next_refresh_event( i, event.env)) >= 0) {
        disable_event( i);
#ifdef _GTV_EVENT_TRACE
        printf( "gtv_push_destroy: found 1 refresh\n");
#endif
    }
    i = _find_clear_event( env);
    if (i >= 0) {
        disable_event( i);
#ifdef _GTV_EVENT_TRACE
        printf( "gtv_push_destroy: found 1 clear\n");
#endif
    }
    new_event = _new_window_event( &event);
    _push_event( _on_destroy, new_event);
    _close_event_stack();
}

void gtv_push_zoom( gtv_zoom_data_t *data)
{
    data->called_from_main_thread = 0;
    _push_flush_stack( _on_zoom, data, 0);
    data->called_from_main_thread = 1;
}

void gtv_push_lens( G_env *env)
{
    _push_flush_stack( _on_lens, env, 0);
}

void gtv_push_flush( int do_quit)
{
    _push_flush_stack( NULL, NULL, do_quit);
}

void _push_set_point( G_env *env, int x, int y, int flg_open)
{
    _points_event_t event;
    _basic_event_t *new_event;

    event.window.basic.type = _POINTS_EVENT;
    event.window.env = env;
    event.npts = 1;
    event.tabpts[0].x = x;
    event.tabpts[0].y = y;

    if (flg_open)
        _open_event_stack();
    new_event = _new_points_event( &event);
    _push_event( _on_points, new_event);
    if (flg_open)
        _close_event_stack();
}

void gtv_push_set_point( G_env *env, int x, int y)
{
    _push_set_point( env, x, y, 1);
}

void gtv_push_add_point( G_env *env, int x, int y)
{
    int i;

    _open_event_stack();
    if ((i = _find_points_event( env)) >= 0) {
        _points_add( (_points_event_t *)_event_stack[i], x, y);
    } else {
        _push_set_point( env, x, y, 0);
    }
    _close_event_stack();
}

void _push_pen( G_env *env, int pen, char* color, int r, int g, int b, int width, int flag_dash, int *ds)
{
    _pen_event_t event;
    int i;

    event.window.basic.type = _PEN_EVENT;
    event.window.env = env;
    event.pen = pen;
    strcpy(event.color, color != NULL ? color : "");
    event.r = r;
    event.g = g;
    event.b = b;
    event.width = width;
    event.flag_dash = flag_dash;
    if (ds != NULL)
        for (i = 0; i < 4; i++)
            event.ds[i] = ds[i];

    _open_event_stack();
    if ((i = _find_pen_event( env)) >= 0) {
        _pen_merge( (_pen_event_t *)_event_stack[i], &event);
    } else {
        _basic_event_t *new_event = _new_pen_event( &event);
        _push_event( _on_pen, new_event);
    }
    _close_event_stack();
}

void gtv_push_set_pen_invert( G_env *env)
{
    _push_pen( env, 1, NULL, -1, -1, -1, -1, -1, NULL);
}

void gtv_push_set_pen_rgb( G_env *env, int r, int g, int b)
{
    _push_pen( env, -1, NULL, r, g, b, -1, -1, NULL);
}

void gtv_push_set_pen_color( G_env *env, char* color)
{
    _push_pen( env, -1, color, -1, -1, -1, -1, -1, NULL);
}

void gtv_push_set_pen_width( G_env *env, int width)
{
    _push_pen( env, -1, NULL, -1, -1, -1, width, -1, NULL);
}

void gtv_push_set_pen_dash( G_env *env, int flag_dash, int *ds)
{
    _push_pen( env, -1, NULL, -1, -1, -1, -1, flag_dash, ds);
}

void gtv_push_destroy_segment( fortran_type adr_segment)
{
    _destroy_fortran_type_event_t event;
    _basic_event_t *new_event;

    event.basic.type = _DESTROY_SEGMENT_EVENT;
    event.pointer = adr_segment;

    _open_event_stack();
    new_event = _new_destroy_fortran_type_event( &event);
    _push_event( _on_destroy_segment, new_event);
    _close_event_stack();
}

void gtv_push_destroy_directory( fortran_type adr_directory)
{
    _destroy_fortran_type_event_t event;
    _basic_event_t *new_event;

    event.basic.type = _DESTROY_DIRECTORY_EVENT;
    event.pointer = adr_directory;

    _open_event_stack();
    new_event = _new_destroy_fortran_type_event( &event);
    _push_event( _on_destroy_directory, new_event);
    _close_event_stack();
}

void gtv_push_colormap_set_default( size_t colormap)
{
    _colormap_event_t event;
    _basic_event_t *new_event;

    event.basic.type = _COLORMAP_EVENT;
    event.colormap = colormap;

    _open_event_stack();
    new_event = _new_colormap_event( &event);
    _push_event( _on_colormap_set_default, new_event);
    _close_event_stack();
}

void gtv_push_colormap_delete( size_t colormap)
{
    _colormap_event_t event;
    _basic_event_t *new_event;

    event.basic.type = _COLORMAP_EVENT;
    event.colormap = colormap;

    _open_event_stack();
    new_event = _new_colormap_event( &event);
    _push_event( _on_colormap_delete, new_event);
    _close_event_stack();
}

void gtv_push_modified_variable( widget_api_t *api, int widget_index)
{
    _modified_variable_event_t event;
    _basic_event_t *new_event;
    int i;

    _open_event_stack();
    if ((i =_find_modified_variable_event()) < 0 || ((_modified_variable_event_t *)_event_stack[i])->widget_index != widget_index) {
        event.basic.type = _MODIFIED_VARIABLE_EVENT;
        event.widget_index = widget_index;
        event.api = api;
        new_event = _new_modified_variable_event( &event);
        _push_event( _on_modified_variable, new_event);
    }
    _close_event_stack();
}

void gtv_push_window_operation( G_env *genv, window_operation_e oper, int
 data1, int data2)
{
    _window_operation_event_t event;
    _basic_event_t *new_event;

    event.window.basic.type = _WINDOW_OPERATION_EVENT;
    event.window.env = genv;
    event.oper = oper;
    event.data1 = data1;
    event.data2 = data2;
    
    _open_event_stack();
    new_event = _new_window_operation_event( &event);
    _push_event( _on_window_operation, new_event);
    _close_event_stack();
}

void gtv_push_callback(void (*callback)(void *), void *data)
{
    _callback_event_t event;
    _basic_event_t *new_event;

    _open_event_stack();
	event.basic.type = _CALLBACK_EVENT;
	event.callback = callback;
	event.data = data;
	new_event = _new_callback_event( &event);
	_push_event( _on_callback, new_event);
    _close_event_stack();
}

char* gtv_event_name(void *event)
{
    return _event_names[((_basic_event_t *)event)->type];
}

