
#include "xsub.h"

#include "event-stack.h"
#include "gtv-message-c.h"
#include "gsys/cfc.h"
#include "gsys/sysc.h"

#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <sys/unistd.h>
#endif

#define open_x CFC_EXPORT_NAME( open_x)
#define x_pen_invert CFC_EXPORT_NAME( x_pen_invert)
#define x_pen_rgb CFC_EXPORT_NAME( x_pen_rgb)
#define x_pen_color CFC_EXPORT_NAME( x_pen_color)
#define x_movp1 CFC_EXPORT_NAME( x_movp1)
#define x_movp2 CFC_EXPORT_NAME( x_movp2)
#define x_clear CFC_EXPORT_NAME( x_clear)
#define x_open_window CFC_EXPORT_NAME( x_open_window)
#define x_close_window CFC_EXPORT_NAME( x_close_window)
#define x_resize_window CFC_EXPORT_NAME( x_resize_window)
#define x_move_window CFC_EXPORT_NAME( x_move_window)
#define x_flush_points CFC_EXPORT_NAME( x_flush_points)
#define x_close CFC_EXPORT_NAME( x_close)
#define x_curs CFC_EXPORT_NAME( x_curs)
#define x_size CFC_EXPORT_NAME( x_size)
#define x_screen_size CFC_EXPORT_NAME( x_screen_size)
#define x_clal CFC_EXPORT_NAME( x_clal)
#define x_clpl CFC_EXPORT_NAME( x_clpl)
#define x_cmdwin CFC_EXPORT_NAME( x_cmdwin)
#define ximage_inquire CFC_EXPORT_NAME( ximage_inquire)
#define ximage_loadrgb CFC_EXPORT_NAME( ximage_loadrgb)
#define xcolormap_create CFC_EXPORT_NAME( xcolormap_create)
#define xcolormap_delete CFC_EXPORT_NAME( xcolormap_delete)
#define ximage_plot CFC_EXPORT_NAME( ximage_plot)
#define read_gtvirt_vals CFC_EXPORT_NAME( read_gtvirt_vals)
#define set_gtvirt_vals CFC_EXPORT_NAME( set_gtvirt_vals)
#define c_new_genv_array CFC_EXPORT_NAME( c_new_genv_array)
#define c_delete_genv_array CFC_EXPORT_NAME( c_delete_genv_array)
#define c_delete_win_genv CFC_EXPORT_NAME( c_delete_win_genv)
#define c_set_win_genv CFC_EXPORT_NAME( c_set_win_genv)
#define c_get_win_genv CFC_EXPORT_NAME( c_get_win_genv)
#define x_fill_poly CFC_EXPORT_NAME( x_fill_poly)
#define createwindow CFC_EXPORT_NAME( createwindow)
#define createwindow_wait CFC_EXPORT_NAME( createwindow_wait)
#define reusewindow CFC_EXPORT_NAME( reusewindow)
#define x_destroy_window CFC_EXPORT_NAME( x_destroy_window)
#define PleaseDestroyTheWindow CFC_EXPORT_NAME( PleaseDestroyTheWindow)
#define c_set_win_null_genv CFC_EXPORT_NAME( c_set_win_null_genv)
#define x_dash CFC_EXPORT_NAME( x_dash)
#define x_weigh CFC_EXPORT_NAME( x_weigh)
#define x_affiche_image CFC_EXPORT_NAME( x_affiche_image)
#define x_draw_rgb CFC_EXPORT_NAME( x_draw_rgb)
#define x_corner CFC_EXPORT_NAME( x_corner)
#define create_edit_lut_window CFC_EXPORT_NAME( create_edit_lut_window)
#define ask_for_corners CFC_EXPORT_NAME( ask_for_corners)
#define other_x_curs CFC_EXPORT_NAME( other_x_curs)
#define x_refresh_window CFC_EXPORT_NAME( x_refresh_window)
#define x_refresh_genv CFC_EXPORT_NAME( x_refresh_genv)
#define x_flush CFC_EXPORT_NAME( x_flush)
#define get_win_pixel_info CFC_EXPORT_NAME(get_win_pixel_info)
#define x_destroy_segment CFC_EXPORT_NAME(x_destroy_segment)
#define x_destroy_directory CFC_EXPORT_NAME(x_destroy_directory)
#define x_lens CFC_EXPORT_NAME(x_lens)

/*----------------------------------------------------------------------

                         VARIABLES GLOBALES

 ----------------------------------------------------------------------*/

#define NTAB      24

/* variables gerant le mode d'affichage */

#define NO_DITHERING	0
#define BW_DITHERING	1
#define GRAY_DITHERING  2
#define COLOR_DITHERING	3

#define MAX_INTENSITY 0xFFFF /* according to X...*/
#define RED_PERCENT   0.299
#define GREEN_PERCENT 0.587
#define BLUE_PERCENT  0.114

#define MaxGrey       32768	/* limits on the grey levels used */
#define Threshold     16384	/* in the dithering process */
#define MinGrey           0

#define RightToLeft	 1
#define LeftToRight	-1

/*----------------------------------------------------------------------
 *
 * Gestion dynamique de l'environnement
 *
 * definition of the graphical environment struct
 * in fact, not all values of the structure are reported in load/store_genv_val
 * these functions only load and store the parts of the genv struct that are
 * used as c global variables.  the gtvirt values of the genv struct, which
 * are used only by the fortran functions, are not saved as c global variables
 * the address of the successor genv struct in the chain is also not saved
 * ----------------------------------------------------------------------
 */

static int _x_open_done = 0;

#ifdef mingw
#undef WIN32
#endif

#ifdef WIN32
#include <gcore/gcomm.h>
static int _zero_reader_writer = -1;
static int _authorize_readers = -1;
static int _nb_readers = 0;
static int _nb_readers_waiting = 0;
#else
#include <pthread.h>
static pthread_rwlock_t _rwlock;
#endif

static int _access_counters = -1;
static int _nb_writers = 0;

static void gtv_open_segments_init()
{
#ifdef WIN32
    _access_counters = sic_sem_create_index( 1);
    _zero_reader_writer = sic_sem_create_index( 1);
    _authorize_readers = sic_sem_create_index( 0);
#else
    _access_counters = 1;
    pthread_rwlock_init( &_rwlock, NULL);
#endif
}

static void _gtv_open_segments_for_writing( int from_main, char *msg)
{
    if (from_main ? !gtv_called_from_main() : gtv_called_from_main()) {
        gtv_c_message(seve_e, "GTV", "%s: call from wrong thread", msg);
    }
    if (_access_counters < 0)
        gtv_open_segments_init();
#ifdef WIN32
    sic_sem_wait_index(_access_counters);
    _nb_writers++;
    sic_sem_post_index(_access_counters);
    sic_sem_wait_index(_zero_reader_writer);
#else
    pthread_rwlock_wrlock( &_rwlock);
    _nb_writers++;
#endif
    if (_nb_writers > 1)
        gtv_c_message(seve_f, "GTV", "%s: Nested calls", msg);
}

void CFC_API gtv_open_segments_for_writing_from_main()
{
    _gtv_open_segments_for_writing( 1,
     "gtv_open_segments_for_writing_from_main");
}

void CFC_API gtv_open_segments_for_writing_from_graph()
{
    _gtv_open_segments_for_writing( 0,
     "gtv_open_segments_for_writing_from_graph");
}

int gtv_open_segment_writer()
{
    return _nb_writers;
}

static void _gtv_close_segments_for_writing( int from_main, char *msg)
{
    if (from_main ? !gtv_called_from_main() : gtv_called_from_main()) {
        gtv_c_message(seve_e, "GTV", "%s: call from wrong thread", msg);
    }
#ifdef WIN32
    sic_sem_wait_index(_access_counters);
    _nb_writers--;
    if (!_nb_writers) {
        sic_sem_post_index(_zero_reader_writer);
        while (_nb_readers_waiting) {
            sic_sem_post_index(_authorize_readers);
            _nb_readers_waiting--;
        }
    }
    sic_sem_post_index(_access_counters);
#else
    _nb_writers--;
    pthread_rwlock_unlock( &_rwlock);
#endif
}

void CFC_API gtv_close_segments_for_writing_from_main()
{
    _gtv_close_segments_for_writing( 1,
     "gtv_close_segments_for_writing_from_main");
}

void CFC_API gtv_close_segments_for_writing_from_graph()
{
    _gtv_close_segments_for_writing( 0,
     "gtv_close_segments_for_writing_from_graph");
}

static void _gtv_open_segments_for_reading( int from_main, char *msg)
{
#ifdef WIN32
    int wait_zero;
#endif
    if (from_main ? !gtv_called_from_main() : gtv_called_from_main()) {
        gtv_c_message(seve_e, "GTV", "%s: call from wrong thread", msg);
    }

    if (_access_counters < 0)
        gtv_open_segments_init();
#ifdef WIN32
    sic_sem_wait_index(_access_counters);
    if (_nb_writers) {
        _nb_readers_waiting++;
        sic_sem_post_index(_access_counters);
        sic_sem_wait_index(_authorize_readers);
        sic_sem_wait_index(_access_counters);
    }
    wait_zero = !_nb_readers;
    _nb_readers++;
    sic_sem_post_index(_access_counters);
    if (wait_zero)
        sic_sem_wait_index(_zero_reader_writer);
#else
    pthread_rwlock_rdlock( &_rwlock);
#endif
}

void CFC_API gtv_open_segments_for_reading_from_main()
{
    _gtv_open_segments_for_reading( 1,
     "gtv_open_segments_for_reading_from_main");
}

void CFC_API gtv_open_segments_for_reading_from_graph()
{
    _gtv_open_segments_for_reading( 0,
     "gtv_open_segments_for_reading_from_graph");
}

static void _gtv_close_segments_for_reading( int from_main, char *msg)
{
#ifdef WIN32
    int post_zero;
#endif
    if (from_main ? !gtv_called_from_main() : gtv_called_from_main()) {
        gtv_c_message(seve_e, "GTV", "%s: call from wrong thread", msg);
    }

#ifdef WIN32
    sic_sem_wait_index(_access_counters);
    _nb_readers--;
    post_zero = !_nb_readers;
    sic_sem_post_index(_access_counters);
    if (post_zero)
        sic_sem_post_index(_zero_reader_writer);
#else
    pthread_rwlock_unlock( &_rwlock);
#endif
}

void CFC_API gtv_close_segments_for_reading_from_main()
{
    _gtv_close_segments_for_reading( 1,
     "gtv_close_segments_for_reading_from_main");
}

void CFC_API gtv_close_segments_for_reading_from_graph()
{
    _gtv_close_segments_for_reading( 0,
     "gtv_close_segments_for_reading_from_graph");
}


/* ---------------------------------------------------------
 * Module 0
 * Gestion d'un environnement graphique
 * ---------------------------------------------------------
 */

graph_api_t *gtv_graph_api = NULL;

void set_graph_api( graph_api_t *api)
{
    gtv_graph_api = api;
}

void init_genv( G_env *env)
{
    env->fen_num = 0;
    env->npts = 0;
    env->destroy_by_program = 0;
}

int gtv_on_resize( G_env *env, int width, int height)
{
#define on_resize CFC_EXPORT_NAME(on_resize)
    extern void CFC_API on_resize(G_env **, int *);
    int error = 0;

    if (env->Width_graphique == width && env->Height_graphique == height)
        return 0;

    env->Width_graphique = width;
    env->Height_graphique = height;

    if (env->genv_array == NULL)
        return 0;

    on_resize(&env, &error);
    return 1;
}

void gtv_on_destroy_genv( G_env *env)
{
#define win_destroy_one_genv CFC_EXPORT_NAME(win_destroy_one_genv)
    extern void CFC_API win_destroy_one_genv(fortran_type, int *, int *);
    int error = 0;

    if (env->destroy_by_program)
        return;

    gtv_open_segments_for_writing_from_graph();
    win_destroy_one_genv( env->adr_dir, &env->fen_num, &error);
    gtv_close_segments_for_writing_from_graph();
}

void gtv_refresh_segment( fortran_type adr_seg, G_env *env, int mode)
{
#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;

    if (env->genv_array != NULL) {
        gtv_open_segments_for_reading_from_graph();
        win_gtview_work_1seg(env->adr_dir, &env->fen_num, adr_seg, &mode, &error);
        gtv_close_segments_for_reading_from_graph();
    }
}

void gtv_refresh_win( fortran_type adr_dir, G_env *env, int mode)
{
#define win_gtview_work_recursdir CFC_EXPORT_NAME(win_gtview_work_recursdir)
    extern void CFC_API win_gtview_work_recursdir(fortran_type, int*, int*, int*);
    int error = 0;

    if (env->genv_array != NULL) {
        gtv_open_segments_for_reading_from_graph();
        win_gtview_work_recursdir(env->adr_dir, &env->fen_num, &mode, &error);
        gtv_close_segments_for_reading_from_graph();
    }
}

void gtv_refresh( G_env *env, int mode)
{
    gtv_refresh_win( env->adr_dir, env, mode);
}

void gtv_lens_register( G_env *parent_env, G_env *env)
{
#define lens_register CFC_EXPORT_NAME(lens_register)
    extern void CFC_API lens_register(fortran_type, void*, void*, int*);
    int error = 0;

    lens_register( parent_env->adr_dir, &parent_env, &env, &error);
}

int gtv_lens_limits( G_env *parent_env, G_env *env, int x, int y, float zoom)
{
#define lens_limits CFC_EXPORT_NAME(lens_limits)
    extern void CFC_API lens_limits(void*, void*, int*, int*, float*, int*);
    int error = 0;

    if (env->genv_array != NULL) {
        lens_limits( &parent_env, &env, &x, &y, &zoom, &error);
    }
    return error;
}

void gtv_get_dir_name( G_env *env, char *dir_name, int dir_name_length)
{
#define cree_chemin_dir CFC_EXPORT_NAME(cree_chemin_dir)
    extern void CFC_API cree_chemin_dir(fortran_type, CFC_FString, int*
     CFC_DECLARE_STRING_LENGTH(tmp));
    CFC_DECLARE_LOCAL_STRING(tmp);
    int length;

    CFC_STRING_VALUE(tmp) = dir_name;
    CFC_STRING_LENGTH(tmp) = dir_name_length;

    cree_chemin_dir(env->adr_dir, tmp, &length
     CFC_PASS_STRING_LENGTH(tmp));

    if (length >= dir_name_length)
        length = dir_name_length - 1;
    dir_name[length] = '\0';
}

void gtv_lut_fromeditor(float *red, float *green, float *blue, int size)
{
#define gt_lut_fromeditor CFC_EXPORT_NAME(gt_lut_fromeditor)
    extern void CFC_API gt_lut_fromeditor(float *, float *, float *, int *, int *);
    int error = 0;

    gt_lut_fromeditor( red, green, blue, &size, &error);
}

static void _flush_points(G_env *env)
{
    gtv_graph_api->graph_flush_points( env, env->tabpts, env->npts);
    env->npts = 0;
}

void CFC_API get_win_pixel_info( G_env **genv, int *x1, int *y1, int *x2,
    int *y2)
{
    *x1 = 1;
    *x2 = (*genv)->Width_graphique-1;
    *y1 = (*genv)->Height_graphique-1;
    *y2 = 1; /* Return more than 1 to reserve space at top of the window,
                e.g. 20 for the Motif toolbar. Can be used for any margin
                on any side. Fortran will deal correctly with such offsets. */
}

/*----------------------------------------------------------------------
 * Module 12
 *
 *  SUBROUTINE X_CLEAR
 *      effacer
 */
void CFC_API x_clear( G_env **env)
{
    if (!_x_open_done)
        return;
    gtv_push_clear( *env);
}

/*----------------------------------------------------------------------
 * Module
 *
 *  SUBROUTINE X_DESTROY_WINDOW(env)
 *
 *      Detruit une fenetre
 *----------------------------------------------------------------------
 */
void CFC_API x_destroy_window( size_t *genv_array, G_env **genv)
{
    (*genv)->destroy_by_program = 1;
    if (!_x_open_done)
        return;
    gtv_push_destroy( *genv);
}

void CFC_API x_destroy_segment( fortran_type adr_segment)
{
#define gtv_delsegments CFC_EXPORT_NAME(gtv_delsegments)
    extern void CFC_API gtv_delsegments(fortran_type);

    if (_x_open_done)
        gtv_push_destroy_segment( adr_segment);
    else
        gtv_delsegments( adr_segment);
}

void CFC_API x_destroy_directory( fortran_type adr_directory)
{
#define gtv_deldirectories CFC_EXPORT_NAME(gtv_deldirectories)
    extern void CFC_API gtv_deldirectories(fortran_type);

    if (_x_open_done)
        gtv_push_destroy_directory( adr_directory);
    else
        gtv_deldirectories( adr_directory);
}

void CFC_API x_open_window( G_env **genv, int *mode)
{
#if 1
    if (*mode == GTV_REFRESH_MODE_UPDATE)
        gtv_graph_api->graph_clear_window( *genv);
#endif
    if (gtv_graph_api->graph_open_window != NULL)
        gtv_graph_api->graph_open_window( *genv, *mode);
}

void CFC_API x_close_window( G_env **genv, int *mode)
{
    if (gtv_graph_api->graph_close_window != NULL)
        gtv_graph_api->graph_close_window( *genv, *mode);
}

void CFC_API x_resize_window( G_env **genv, int *width, int *height)
{
    gtv_push_window_operation( *genv, WINDOW_OPERATION_RESIZE, *width, *height);
}

void CFC_API x_move_window( G_env **genv, int *x, int *y)
{
    gtv_push_window_operation( *genv, WINDOW_OPERATION_MOVE, *x, *y);
}

/*------------------------------------------------------------------
 * Deplacons la fenetre dans le coin 1,2,3,4,5,6,7,8,9 selon
 * keypad numerique habituel. Bien pratique avec ces c... de
 * Window Manager qui les mettent n'importe ou...
 */
void CFC_API x_corner( G_env **genv, int *code)
{
    int x[3],y[3],i = *code-1;

    gtv_graph_api->graph_get_corners( *genv, x, y);
    gtv_push_window_operation( *genv, WINDOW_OPERATION_MOVE, x[i % 3], y[i/3]);
}

/*----------------------------------------------------------------------
 * Module 26
 * SUBROUTINE CREATEWINDOW(BACKG,GRAPH_ENV,DIRNAME,X1,X2,Y1,Y2,LD)
 *
 * Creation d'une fenetre supplementaire
 *  Note philosophique:
 * ------------------
 * Pour des raisons non encore elucidees (...) on a un plantage dans
 * createwindow, sur la requete XNextEvent, quand:
 *	On a cree plusieurs fenetres
 *	On donne l'ordre device none
 *	Puis l'ordre device x ...
 * Que Saint Trucmuche, patron des programmeurs fous nous vienne en aide...
 * ----------------------------------------------------------------------
 */
int CFC_API createwindow(
     int *backg,
     G_env **graph_env,
     CFC_FzString dirname,
     int *width, int *height,
     fortran_type dir_cour,
     int *ld)
{
    static char tmp[256];
    G_env *genv;

    strncpy( tmp, CFC_fz2c_string( dirname), *ld);
    tmp[*ld] = '\0';

    /* Creeons un nouveau contexte graphique */
    genv = gtv_graph_api->graph_creer_genv();
    /* Now open a (graphic) window */
    gtv_graph_api->graph_new_graph(*backg, genv, tmp, *width, *height, dir_cour,
     0);
    *graph_env = genv;
    return(1);
}

/*----------------------------------------------------------------------
 * Wait for the window to be effectively created. This must be done out
 * of the thread protection (open/close segments) because it waits an
 * answer from the graphic thread.
 */
void CFC_API createwindow_wait()
{
    sic_wait_widget_created( );
}

/*----------------------------------------------------------------------
 * Module 26-bis
 * SUBROUTINE REUSEWINDOW(GRAPH_ENV,DIRNAME,X1,X2,Y1,Y2,LD)
 *
 * Re-Use (for other purpose, usually after a CLEAR PLOT) the
 * current (but no longer used) window...
 * ----------------------------------------------------------------------
 */
int CFC_API reusewindow(
     G_env **graph_env,
     CFC_FzString dirname,
     int *width, int *height,
     fortran_type dir_cour,
     int *ld)
{
    char tmp[256];

    strncpy( tmp, CFC_fz2c_string( dirname), *ld);
    tmp[*ld] = '\0';

    /* Use window characteristics, Now open a (graphic) window */
    gtv_graph_api->graph_new_graph(1, *graph_env, tmp, *width, *height, dir_cour,
     1);
    return(1);
}

int CFC_API x_refresh_window( fortran_type adr_dir, int *code)
{
    if (!_x_open_done || gtv_graph_api->graph_refresh== NULL)
        return 1;
    gtv_push_refresh(adr_dir, *code, NULL);
    return 0;
}

int CFC_API x_refresh_genv( fortran_type adr_dir, int *code, G_env **genv)
{
    if (!_x_open_done || gtv_graph_api->graph_refresh== NULL)
        return 1;
    gtv_push_refresh(adr_dir, *code, *genv);
    return 0;
}

/*
 *----------------------------------------------------------------------
 *  SUBROUTINE X_CLOSE
 *      fermer la connexion avec X
 *----------------------------------------------------------------------
 */
void CFC_API x_close(void)
{
    _x_open_done = 0;
    gtv_graph_api->graph_x_close();
}

void CFC_API x_flush( int *do_quit)
{
    if (!_x_open_done)
        return;
    if (*do_quit)
        x_close();
    gtv_push_flush( *do_quit);
    if (gtv_graph_api->graph_flush != NULL)
        gtv_graph_api->graph_flush();
}

/*----------------------------------------------------------------------
 * Module 34
 * SUBROUTINE X_CREE_STRUCT_IMAGE(adresse_image,add,n_li,n_col)  */

/* ne sert plus a rien : */
/* supprimer en passant adr_data directement a x_affiche_image. */
/* dans gt_image.f et edit_lut.f */
/*void x_cree_struct_image(size_t *adr_data,size_t *add,int *n_li,int *n_col)
{ *adr_data=(size_t)*add; }*/

/* each window has a graphical environment genv, and each directory has a
 * table of addresses of the genv structs for all the windows in that
 * directory.  the address of that table (ie the address returned by
 * c_new_genv_array(), is stored in the directory descriptor, and the
 * current table is kept as a c global variable
 */

/*----------------------------------------------------------------------
 *   SUBROUTINE CREE_GENV_ARRAY
 *
 *   called by GTCLEAR, GTOPEN, and NEW_WINDOW to create the table of
 *   graphical environment addresses for each directory.  called when
 *   the first window for the directory is created.  By default, there is
 *   room for 5 windows.  this number is somewhat arbitrary, but based on the
 *   fact that most people seldom have more than 5 windows on the screen at one
 *   time
 *   the function returns the address of the table to the calling (fortran)
 *   program as an integer
 */
size_t CFC_API c_new_genv_array(int *size)
{
  G_env **ret;
  ret = (G_env **)calloc( *size + 1, sizeof(G_env *));
  ret[0] = (G_env *)(size_t)*size;
  return (size_t)ret;
}

/*----------------------------------------------------------------------
 *   SUBROUTINE RM_GENV_ARRAY
 *
 *   called by GTCLEAR to free the table of graphical environment addresses
 *   for each directory
 */
void CFC_API c_delete_genv_array( size_t *genv_array)
{
    G_env **garray = (G_env **)*genv_array;
    size_t size = (size_t)garray[0];
    int i;

    for (i = 1; i <= size; i++) {
        if (garray[i] != NULL)
            garray[i]->genv_array = NULL;
    }
    free((char *)garray);
}

void CFC_API c_delete_win_genv( size_t *genv_array, int *win_num)
{
    int w_num = *win_num;
    G_env **garray = (G_env **)*genv_array;
    size_t size = (size_t)garray[0];
    int i;

    if (w_num < 0 || w_num >= size)
        return;

    if (garray[w_num + 1] != NULL)
        garray[w_num + 1]->genv_array = NULL;

    for (i = w_num + 1; i < size; i++) {
        if (garray[i + 1] != NULL)
            garray[i + 1]->fen_num--;
        garray[i] = garray[i + 1];
    }
    garray[size] = NULL;
}

/*----------------------------------------------------------------------
 *   SUBROUTINE PUT_NEXT_ENV
 *
 *   called by GTOPEN and CREE_FEN to put the address of the new graphical
 *   environment (genv struct, created for each window) into the current table
 *   of genv addresses
 *   the current table of genv addresses
 */
void CFC_API c_set_win_genv( size_t *genv_array, int *win_num, G_env **genv)
{
    int w_num = *win_num;
    G_env **garray = (G_env **)*genv_array;

    if (w_num >= (size_t)garray[0]) {
        gtv_c_message(seve_f, "X", "No more genv for new windows");
        x_destroy_window( genv_array, genv);
        return;
    }
    garray[w_num + 1] = *genv;
    (*genv)->genv_array = garray;
    (*genv)->fen_num = w_num;
}

/*----------------------------------------------------------------------
 *   SUBROUTINE GET_NEXT_ENV
 *
 *   called by CD_BY_NAME and GTV_COMPRESS to get the address of the graphical
 *   environment (genv struct, created for each window) from the current table
 *   of genv addresses.  the address is returned to the calling (fortran)
 *   program as an integer
 */
size_t CFC_API c_get_win_genv( size_t *genv_array, int *win_num)
{
    int w_num = *win_num;
    G_env **garray = (G_env **)*genv_array;

    if (garray == NULL || w_num < 0 || w_num >= (size_t)garray[0])
        return (size_t)NULL;
    return (size_t)garray[w_num + 1];
}

/*----------------------------------------------------------------------
 *   SUBROUTINE PUT_NULL_ENV
 *
 *   called by DESTROY_WINDOW to set the graphical environment address to
 *   NULL after the window is destroyed by x_destroy_window()
 */
void CFC_API c_set_win_null_genv( size_t *genv_array, int *win_num)
{
    int w_num = *win_num;
    G_env **garray = (G_env **)*genv_array;

    if (w_num < 0 || w_num >= (size_t)garray[0]) {
        gtv_c_message(seve_e, "X", "Window number error for destroy_window - window %d",w_num);
        return;
    }
    garray[w_num + 1]->genv_array = NULL;
    garray[w_num + 1] = NULL;
}

/*
 *----------------------------------------------------------------------
 * Module 1
 * SUBROUTINE OPEN_X
 *     OPEN_X Point d'entree pour GTOPEN
 *----------------------------------------------------------------------
 */
int CFC_API open_x(
    int *cross           /* !=0 if the user wants crosshair cursor  */
)
{
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
    _x_open_done = 1;
    return gtv_graph_api->graph_open_x( *cross);
}

void CFC_API create_edit_lut_window()
{
    gtv_graph_api->graph_create_edit_lut_window();
}

void CFC_API ask_for_corners( int *ax, int *bx, int *ay, int *by)
{
    gtv_graph_api->graph_ask_for_corners( ax, bx, ay, by);
}

void CFC_API other_x_curs(G_env **graph_env, CFC_FString cde)
{
    gtv_graph_api->graph_other_x_curs( *graph_env, CFC_f2c_string( cde));
}

/*----------------------------------------------------------------------
 *  SUBROUTINE X_PEN
 *  Changer de plume
 *----------------------------------------------------------------------
 */
void CFC_API x_pen_invert(G_env **graph_env)
{
    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_pen_invert", "Null graphic environment");
        return;
    }
    if (gtv_called_from_main()) {
        gtv_push_set_pen_invert( *graph_env);
    } else {
        _flush_points(*graph_env);
        gtv_graph_api->graph_x_pen_invert( *graph_env);
    }
}

void CFC_API x_pen_rgb(G_env **graph_env, int *r, int *g, int *b)
{

    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_pen_rgb", "Null graphic environment");
        return;
    }

    /* r g b values rescaled from 0-255 to 0-65536*/
    if (gtv_called_from_main()) {
        gtv_push_set_pen_rgb( *graph_env, *r * 256, *g * 256, *b * 256);
    } else {
        _flush_points(*graph_env);
        gtv_graph_api->graph_x_pen_rgb( *graph_env, *r * 256, *g * 256, *b * 256);
    }
}

void CFC_API x_pen_color(G_env **graph_env, CFC_FString color CFC_DECLARE_STRING_LENGTH(color))
{
    char tmp[32];

    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_pen_color", "Null graphic environment");
        return;
    }
    CFC_f2c_strcpy( tmp, color, CFC_STRING_LENGTH_MIN( color, sizeof(tmp)));
    if (gtv_called_from_main()) {
        gtv_push_set_pen_color( *graph_env, tmp);
    } else {
        _flush_points(*graph_env);
        gtv_graph_api->graph_x_pen_color( *graph_env, tmp);
    }
}

/*----------------------------------------------------------------------
 *  SUBROUTINE X_MOVP1
 *  Se placer  plume haute
 *----------------------------------------------------------------------
 */
void CFC_API x_movp1(G_env **graph_env, int *ix, int *iy)
{
    G_env *genv = *graph_env;

    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_movp1", "Null graphic environment");
        return;
    }
    if (gtv_called_from_main()) {
        gtv_push_set_point( *graph_env, *ix, *iy);
    } else {
        _flush_points(*graph_env);
        genv->tabpts[0].x = *ix;
        genv->tabpts[0].y = *iy;
        genv->npts = 1;
    }
}

/*----------------------------------------------------------------------
 *  SUBROUTINE X_MOV_P2
 *  deplacement  plume basse
 *----------------------------------------------------------------------
 */
void CFC_API x_movp2(G_env **graph_env, int *ix, int *iy)
{
    G_env *genv = *graph_env;

    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_movp2", "Null graphic environment");
        return;
    }
    if (gtv_called_from_main()) {
        gtv_push_add_point( *graph_env, *ix, *iy);
    } else {
        genv->tabpts[genv->npts].x = *ix;
        genv->tabpts[genv->npts].y = *iy;
        genv->npts++;
        if (genv->npts == sizeof(genv->tabpts) / sizeof(genv->tabpts[0])) {
            _flush_points(genv);
            genv->tabpts[0].x = *ix;
            genv->tabpts[0].y = *iy;
            genv->npts = 1;
        }
    }
}

/* ---------------------------------------------------------------------
 * SUBROUTINE X_FILL_POLY
 * Remplissage de polygone
 * Note philosophique: j'ai constate que si le polygone a tracer n'est
 * pas ferme il n'est pas trace...
 * ---------------------------------------------------------------------
 */
void CFC_API x_fill_poly(G_env **graph_env, int *npts_poly, int *tabx, int *taby)
{
    gtv_graph_api->graph_x_fill_poly( *graph_env, *npts_poly, tabx, taby);
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_CURS
 *    curseur
 * Note philosophique 1:
 * --------------------
 * Le reticule que l'on dessine se fait avec la plume courante.
 * Je prefere un reticule blanc sur fond noir ou un reticule noir
 * sur fond blanc.
 * De plus, il faut eviter un reticule blanc sur fond blanc ...
 *----------------------------------------------------------------------
 */
void CFC_API x_curs(G_env **graph_env, int *fzoom, int *rect_xw, int *rect_yw,
                    int *xpos_curs, int *ypos_curs, CFC_FString cde)
{
    static gtv_zoom_data_t data = {0, };

    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_curs", "Null graphic environment");
        return;
    }
    data.env = *graph_env;
    data.flg_zoom = *fzoom == 1;
    data.width = *rect_xw;
    data.height = *rect_yw;
    data.xpos = xpos_curs;
    data.ypos = ypos_curs;
    data.command = CFC_f2c_string( cde);

    gtv_push_zoom( &data);
}

void CFC_API x_lens(G_env **graph_env)
{
    gtv_push_lens( *graph_env);
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_WEIGH
 * appelle par le fortran, cette routine place la largeur de ligne.
 */
void CFC_API x_weigh(G_env **graph_env, int *width)
{
    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_weigh", "Null graphic environment");
        return;
    }
    if (gtv_called_from_main()) {
        gtv_push_set_pen_width( *graph_env, *width);
    } else {
        _flush_points(*graph_env);
        gtv_graph_api->graph_x_weigh( *graph_env, *width);
    }
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_DASH(dashed,pattern)
 * appelle par le fortran, cette routine place le style de pointille.
 */
void CFC_API x_dash(G_env **graph_env, int *flag_dash, int ds[4])
{
    if (*graph_env == NULL) {
        gtv_c_message(seve_e, "x_dash", "Null graphic environment");
        return;
    }
    if (gtv_called_from_main()) {
        gtv_push_set_pen_dash( *graph_env, *flag_dash, ds);
    } else {
        _flush_points(*graph_env);
        gtv_graph_api->graph_x_dash( *graph_env, *flag_dash, ds);
    }
}

/*----------------------------------------------------------------------
 *      SUBROUTINE XIMAGE_LOADRGB(R,G,B,N,DIRECT)
 *      INTEGER N,DIRECT
 *      INTEGER RED(N),GREEN(N),BLUE(N)
 *
 * Note 1: C'est a la charge de l'appelant de faire correctement de
 *         decalage <<8
 * Note 2: Loin de moi l'idee de faire des remarques desobligeantes a mes
 *         petits camarades, mais, je doute quand meme un peu que l'option
 *         reverse ait marche telle quelle ........
 *
 * --reecrit le 09.06.92-------------------------------------
 * la nouvelle colormap sera chargee comme pseudo-colormap associee
 * a l'environnement courant. Ce ne sera peut-etre pas celle-ci
 * qui apparaitra a l'affichage, car cela depends du display (gestion
 * par la fonction set_colors), mais elle sera au moins defini pour
 * l'env_cour.
 */
void CFC_API ximage_loadrgb(int red[], int green[], int blue[], int *n,
 int *dir)
{
    gtv_graph_api->graph_ximage_loadrgb( red, green, blue, *n, *dir);
}

size_t CFC_API xcolormap_create(float red[], float green[], float blue[], int *n, int *is_default)
/*-----------------------------------------------------------------------
 * SUBROUTINE XCOLORMAP_CREATE
 *-----------------------------------------------------------------------
 */
{
    if (_x_open_done && *is_default) {
        size_t ret = gtv_graph_api->graph_xcolormap_create( red, green, blue,
         *n, 0);
        gtv_push_colormap_set_default( ret);
        return ret;
    } else
        return gtv_graph_api->graph_xcolormap_create( red, green, blue, *n,
         *is_default);
}

void CFC_API xcolormap_delete( size_t *colormap)
/*-----------------------------------------------------------------------
 * SUBROUTINE XCOLORMAP_CREATE
 *-----------------------------------------------------------------------
 */
{
    if (_x_open_done)
        gtv_push_colormap_delete( *colormap);
    else
        gtv_graph_api->graph_xcolormap_delete( *colormap);
}

int CFC_API ximage_inquire(int *lcol, int *ocol, int *nx, int *ny,
 int *is_color, int *is_static)
{
    return gtv_graph_api->graph_ximage_inquire( lcol, ocol, nx, ny, is_color,
     is_static);
}

/*-----------------------------------------------------------------------
 * SUBROUTINE X_AFFICHE_IMAGE
 * On nous fournit une adresse d'une struct Image.
 * A nous de l'afficher en fonction du display que l'on possede.
 * Quelque soit le display, nous aurons une structure XImage et donc
 * un pixmap ou un bitmap, on les retournera pour qu'ils soient ecrits
 * dans le fichier.
 *-----------------------------------------------------------------------
 */
void CFC_API x_affiche_image( G_env **graph_env, size_t *adr_data, int *n_x0,
 int *n_y0, int *n_larg, int *n_haut, size_t *colormap)
{
    int val_trou = 0;
    gtv_graph_api->graph_x_affiche_image( *graph_env, adr_data, *n_x0, *n_y0,
     *n_larg, *n_haut, val_trou, *colormap);
}

/*-----------------------------------------------------------------------
 * SUBROUTINE X_DRAW_RGB
 * On nous fournit 3 tableaux r g b
 *-----------------------------------------------------------------------
 */
void CFC_API x_draw_rgb( G_env **graph_env, size_t *r, size_t *g, size_t *b,
 int *n_x0, int *n_y0, int *n_larg, int *n_haut)
{
    gtv_graph_api->graph_x_draw_rgb( *graph_env, r, g, b, *n_x0, *n_y0,
     *n_larg, *n_haut);
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_CLAL
 *  Clear Alpha
 */
void CFC_API x_clal( G_env **graph_env)
{
    gtv_push_window_operation( *graph_env, WINDOW_OPERATION_RAISE, 0, 0);
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_CLPL
 *  Clear Plot (Lower Window)
 */
void CFC_API x_clpl( G_env **graph_env)
{
    gtv_push_window_operation( *graph_env, WINDOW_OPERATION_LOWER, 0, 0);
}

/*----------------------------------------------------------------------
 *  SUBROUTINE X_CMDWIN
 *  set focus back to the command window
 */
void CFC_API x_cmdwin( void)
{
    gtv_graph_api->graph_x_cmdwin();
}

/*
 *----------------------------------------------------------------------
 *   SUBROUTINE X_SIZE
 *      Return the window size
 */
void CFC_API x_size( G_env **graph_env, int *larg, int *haut)
{
    gtv_graph_api->graph_x_size( *graph_env, larg, haut);
}

/*
 *----------------------------------------------------------------------
 *   SUBROUTINE X_SCREEN_SIZE
 *      Return the screen size
 */
void CFC_API x_screen_size( int *larg, int *haut)
{
    gtv_graph_api->graph_x_screen_size( larg, haut);
}

/*----------------------------------------------------------------------
 * SUBROUTINE X_FLUSH_POINTS
 *  Tracer
 *----------------------------------------------------------------------
 */
void CFC_API x_flush_points( G_env **graph_env)
{
    if (!gtv_called_from_main()) {
        _flush_points( *graph_env);
    }
}
