
#include "dialog.h"
#include "gpanel.h"

#include "gsys/gag_trace.h"
#include "ggui-message-c.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <math.h>

static global_struct widgets[NSTRUCT];
static int update[NSTRUCT];

static int nb_widgets;
static widget_info_t *widget_info_objects[NSTRUCT];
static int nb_actions;

int widget_get_index( widget_t *generic)
{
    return ((global_struct *)generic - widgets);
}

widget_info_t *widget_info_new( int size, widget_t *generic)
{
    widget_info_t *ret;

    ret = calloc( 1, size);
    ((widget_info_struct *)ret)->generic = generic;
    ((widget_info_struct *)ret)->need_redraw = 0;
    widget_info_objects[widget_get_index( generic)] = ret;
    return ret;
}

static void widget_info_delete( widget_info_t *widget_info)
{
    ((widget_info_struct *)widget_info)->generic = NULL;
    free( widget_info);
}

widget_info_t *widget_info_find( int index)
{
    return widget_info_objects[index];
}

void widget_info_open( void)
{
    int i;

    for (i = 0; i < NSTRUCT; i++)
        widget_info_objects[i] = NULL;
}

void widget_info_close( void (*on_widget_info_close)( widget_info_struct *))
{
    int i;

    for (i = 0; i < NSTRUCT; i++) {
        if (widget_info_objects[i] != NULL) {
            if (on_widget_info_close != NULL)
                on_widget_info_close( widget_info_objects[i]);
            widget_info_delete( widget_info_objects[i]);
            widget_info_objects[i] = NULL;
        }
    }
}

static void create_item( widget_api_t *api, dialog_info_t *dialog, int i)
{
    switch (widgets[i].generic.type) {

    case SEPARATOR:
    case SHOW:
        if (dialog->group_box_created) {
            dialog->group_box_created = 0;
        }
        break;

    default:
        if (!dialog->group_box_created && api->dialog_info_create_group_box != NULL) {
            api->dialog_info_create_group_box( dialog);
            dialog->group_box_created = 1;
        }
        break;
    }

    switch (widgets[i].generic.type) {

    case SEPARATOR:
        api->dialog_separator_add( dialog);
        break;

    case CHAIN:
        api->dialog_chain_add( dialog, &widgets[i].chain);
        break;

    case SLIDER:
        api->dialog_slider_add( dialog, &widgets[i].slider);
        break;

    case SHOW:
        api->dialog_show_add( dialog, &widgets[i].show);
        break;

    case LOGIC:
        api->dialog_logic_add( dialog, &widgets[i].logic);
        break;

    case BROWSER:
        api->dialog_browser_add( dialog, &widgets[i].file);
        break;

    case CHOICE:
        api->dialog_choice_add( dialog, &widgets[i].choice);
        break;

    case BUTTON:
        api->dialog_button_add( dialog, &widgets[i].button);
        break;

    }
    if (api->dialog_on_create_item != NULL)
        api->dialog_on_create_item( dialog, &widgets[i].generic);
}

static void widgets_init( )
{
    int window_id;
    int i;
    int j;

    window_id = 1;
    nb_actions = 0;
    for (i = 0; i < nb_widgets; i++) {
        if (widgets[i].generic.type == BUTTON) {
            if (widgets[i].button.showlength == 0) {
                nb_actions++;
                widgets[i].button.popup_window_id = 0;
                continue;
            }
            widgets[i].button.popup_window_id = window_id++;
        }
        update[i] = 0;
    }

    /* find and mark widget pointing to a same SIC variable for subsequent
    common update */
    for (i = 0; i < nb_widgets; i++) {
        for (j = i + 1; j < nb_widgets; j++) {
            if (strcmp( widgets[i].generic.variable,widgets[j].generic.variable) == 0) {
                update[i] = 1;
                update[j] = 1;
            }
        }
    }
}

void dialog_info_prepare( dialog_info_t *dialog, int dialog_id)
{
    int i;

    dialog->nb_items = 0;
    for (i = 0; i < nb_widgets; i++) {
        if (widgets[i].generic.type == BUTTON && widgets[i].button.showlength == 0)
            continue;
        if (widgets[i].generic.window_id == dialog_id)
            dialog->nb_items++;
    }
    dialog->nb_actions = nb_actions;
}

void dialog_info_build( widget_api_t *api, dialog_info_t *dialog, int dialog_id)
{
    int i;

    dialog->dialog_id = dialog_id;
    /* create the rows of widgets */
    dialog->group_box_created = 0;
    for (i = 0; i < nb_widgets; i++) {
        if (widgets[i].generic.type == BUTTON && widgets[i].button.showlength == 0)
            continue;
        if (widgets[i].generic.window_id == dialog_id)
            create_item( api, dialog, i);
    }
}

static int update_widgets( widget_api_t *api, widget_t *generic, int from_index)
{
    generic_struct *widget = (generic_struct *)generic;
    int i;
    char dum[80];
    double double_value;
    int int_value;
    char *string_value;
    widget_info_t *widget_info;
    static int s_recur = 0;

    if (api == NULL)
        return 1;

    if (widget->type == SHOW)
        return 0;

    if (s_recur)
        return 0;
    s_recur++;

    switch (widget->type) {
    case SLIDER:
        double_value = ((slider_struct *)widget)->uservalue;
        int_value = (int)floor( double_value);
        string_value = dum;
        sprintf( string_value, "%g", double_value);
        break;
    case LOGIC:
        int_value = ((logic_struct *)widget)->userlogic;
        double_value = int_value;
        string_value = dum;
        sprintf( string_value, "%g", double_value);
        break;
    case CHOICE:
        string_value = ((choice_struct *)widget)->userchoice;
        int_value = atoi( string_value);
        double_value = atof( string_value);
        break;
    case CHAIN:
        string_value = ((chain_struct *)widget)->userchain;
        int_value = atoi( string_value);
        double_value = atof( string_value);
        break;
    case BROWSER:
        string_value = ((file_struct *)widget)->userchain;
        int_value = atoi( string_value);
        double_value = atof( string_value);
        break;
    default:
        /* should not happen */
        ggui_c_message(seve_e, "XM_DIALOG", "Very Strange\n");
        break;
    }

    for (i = 0; i < nb_widgets; i++) {
        if (i == from_index)
            continue;
        if (!widget->variable[0] || strcmp( widgets[i].generic.variable, widget->variable))
            continue;
        widget_info = widget_info_find( i);
        if (widget_info != NULL) {
            switch (widgets[i].generic.type) {
            case SLIDER:
                api->slider_info_set_value( widget_info, double_value);
                break;
            case LOGIC:
                api->logic_info_set_value( widget_info, int_value);
                break;
            case CHAIN:
                api->chain_info_set_value( widget_info, string_value);
                break;
            case CHOICE:
                api->choice_info_set_value( widget_info, string_value);
                break;
            case BROWSER:
                api->file_info_set_value( widget_info, string_value);
                break;
            case SHOW:
            default:
                /* should not happen */
                ggui_c_message(seve_e, "XM_DIALOG", "Very Strange\n");
                break;
            }
        } else {
            switch (widgets[i].generic.type) {
            case SLIDER:
                widgets[i].slider.uservalue = double_value;
                break;
            case LOGIC:
                widgets[i].logic.userlogic = int_value;
                break;
            case CHOICE:
                strcpy( widgets[i].choice.userchoice, string_value);
                break;
            case CHAIN:
                strcpy( widgets[i].chain.userchain, string_value);
                break;
            case BROWSER:
                strcpy( widgets[i].file.userchain, string_value);
                break;
            default:
                break;
            }
        }
        if (!update[i])
            /* only one widget associated to this variable */
            break;
    }

    s_recur--;
    return 1;
}

/* return true if update is needed when api is null */
int update_other_widgets( widget_api_t *api, widget_t *generic)
{
    int index;

    index = widget_get_index( generic);
    if (!update[index])
        /* only one widget associated to this variable */
        return 0;

    return update_widgets( api, generic, index);
}

void widget_update_variable( widget_api_t *api, sic_widget_def_t *widget)
{
    /* Il faut mettre a jour toutes les occurences de la variable du
    widget modified_widget qui est lu en memoire  */
    update_widgets( api, widget, -1);
}

void dialog_widget_set_need_redraw( int widget_index) {
    widget_info_t *widget_info = widget_info_find( widget_index);
    if (widget_info != NULL) {
        ((widget_info_struct *)widget_info)->need_redraw = 1;
    }
}

void widget_update( widget_api_t *api, int widget_index)
{
    widget_info_t *widget_info = widget_info_find( widget_index);
    sic_widget_def_t tmp;
    if (widget_info == NULL) {
        sic_get_widget_def( widget_index, &tmp);
        update_widgets( api, &tmp, -1);
    } else if (((widget_info_struct *)widget_info)->need_redraw) {
        ((widget_info_struct *)widget_info)->need_redraw = 0;
        sic_get_widget_def( widget_index, &tmp);
        update_widgets( api, &tmp, -1);
    }
}

void save_context( widget_api_t *api, int win, int destroy_widgets)
{
    int i;
    widget_info_t *widget_info;

    for (i = 0; i < nb_widgets; i++) {
        if (win && widgets[i].generic.window_id != win)
            continue;
        widget_info = widget_info_find( i);
        if (widget_info == NULL)
            continue;

        switch (widgets[i].generic.type) {
        case LOGIC:
            api->logic_info_get_value( widget_info);
            break;
        case CHAIN:
            api->chain_info_get_value( widget_info);
            break;
        case CHOICE:
            api->choice_info_get_value( widget_info);
            break;
        case BROWSER:
            api->file_info_get_value( widget_info);
            break;
        }
        if (destroy_widgets) {
            widget_info_delete( widget_info);
            widget_info_objects[i] = NULL;
        }
    }
}

void gdialog_build_help( const char *help_file, const char *variable_name,
    char *output_text, int *nb_cols, int *nb_rows)
{

    build_help_variable(help_file,variable_name,output_text);

    *nb_cols = 0; /* Unused */
    *nb_rows = 0; /* Unused */
}


int on_close_dialog( widget_api_t *api, dialog_info_t *dialog_info, char *return_command, int returned_code)
{
    int i;
    int ret;
    char command[COMMANDLENGTH];

    /* Save context of main window and all associated windows */
    save_context( api, 0, 0);

    gag_trace( "<trace: enter> on_close_dialog");
    sic_open_widget_board( );

    for (i = 0; i < nb_widgets; i++) {
        sic_set_widget_def( i, &widgets[i]);
    }

    sic_set_widget_returned_code( returned_code);
    if (returned_code == -3 && api->dialog_close != NULL)
        api->dialog_close( dialog_info);

    sic_close_widget_board( );

    if (return_command[0]) {
      /* Merge GUI\END (trigger to update the variables from the Fortran side)
         with the command associated to GUI\GO. */
      strcpy(command, "GUI\\END; ");
      strcat(command, return_command);
    } else {
      strcpy(command, "GUI\\END");
    }
    ret = sic_post_command_text( command);

    gag_trace( "<trace: leave> on_close_dialog");
    return ret;
}

void on_run_dialog( void (*update_variable)( int, sic_widget_def_t *, void *), void *data)
{
    int i;

    gag_trace( "<trace: enter> on_run_dialog");

    sic_add_modified_variable_listener( update_variable, data);

    nb_widgets = sic_open_widget_board( );

    /* Read data in shared memory */
    for (i = 0; i < nb_widgets; i++) {
        sic_get_widget_def( i, &widgets[i]);
    }

    sic_close_widget_board( );

    widgets_init( );

    gag_trace( "<trace: leave> on_run_dialog");
}

button_struct *widget_find_button_from_window_id( int window_id)
{
    int i;

    /* look for button with same window_id */
    for (i = 0; i < nb_widgets; i++) {
        if (widgets[i].generic.type != BUTTON)
            continue;
        if (widgets[i].button.showlength == 0)
            continue;
        if (widgets[i].button.popup_window_id == window_id)
            return &widgets[i].button;
    }
    return NULL;
}

void *parse_menu_button_begin( )
{
    static int iter;
    iter = 0;
    return &iter;
}

button_struct *parse_menu_button_next( void *iter)
{
    int i;

    while ((i = (*(int *)iter)++) < nb_widgets) {
        button_struct *b;

        if (widgets[i].generic.type != BUTTON)
            continue;
        b = &widgets[i].button;
        if (b->showlength)
            continue;
        return b;
    }
    return NULL;
}

char *choice_get_value( choice_struct *choice)
{
    if (choice->mode == 1)
        return choice->choices[atoi(choice->userchoice) - 1];
    else
        return choice->userchoice;
}

static int choice_get_value_index( choice_struct *choice, const char *value)
{
    int j;

    for (j = 0; j < choice->nchoices; j++) {
        if (strcmp( value, choice->choices[j]) == 0)
            break;
    }
    return j < choice->nchoices ? j : -1;
}

void choice_set_value( choice_struct *choice, char *value)
{
    if (choice->mode == 1) {
        int i = choice_get_value_index( choice, value);
        sprintf( choice->userchoice, "%d", i + 1);
    } else {
        strncpy( choice->userchoice, value, CHAINLENGTH);
    }
}

int choice_get_index( choice_struct *choice)
{
    if (choice->mode == 1) {
        return atoi( choice->userchoice) - 1;
    } else {
        return choice_get_value_index( choice, choice->userchoice);
    }
}

void choice_set_index( choice_struct *choice, int index)
{
    if (choice->mode == 1) {
        sprintf( choice->userchoice, "%d", index + 1);
    } else {
        strncpy( choice->userchoice, choice->choices[index], X_CHOLEN);
    }
}
