/*
 * mlterm-menu - a popup-menu-style configuration tool for mlterm
 *
 * $Id$
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <gtk/gtk.h>

#include <kiklib/kik_debug.h>
#include <kiklib/kik_conf_io.h>
#include <kiklib/kik_def.h>		/* USE_WIN32API */

#if  defined(USE_WIN32API)
#define CONFIG_PATH "."
#elif  defined(SYSCONFDIR)
#define CONFIG_PATH SYSCONFDIR
#else
#define CONFIG_PATH "/etc"
#endif

#define MENU_RCFILE "mlterm/menu"

#ifndef  gtk_menu_append
#define  gtk_menu_append(menu,child)  gtk_menu_shell_append((GtkMenuShell*)(menu),(child))
#endif

static GScannerConfig menu_scanner_config = {
    " \t\n",
    G_CSET_A_2_Z G_CSET_a_2_z "-_",
    G_CSET_A_2_Z G_CSET_a_2_z "-_",
    "#\n",
    TRUE,
    FALSE,
    TRUE,
    FALSE,
    TRUE,
    TRUE,
    FALSE,
    TRUE,
    FALSE,
    TRUE,
    TRUE,
    TRUE,
    FALSE,
    TRUE,
    TRUE,
    TRUE,
    FALSE,
    FALSE,
    TRUE,
    FALSE,
    FALSE,
};

static char* progname;

GtkWidget* create_menu();

int append_menu_from_file(GtkMenu* menu, char* filename);
int append_menu_from_scanner(GtkMenu* menu, GScanner* scanner, int level);
int append_pty_list(GtkMenu* menu);

void activate_callback(GtkWidget* widget, gpointer data);
#if  ! defined(G_PLATFORM_WIN32)
void activate_callback_copy(GtkWidget* widget, gpointer data);
#endif
void toggled_callback(GtkWidget* widget, gpointer data);
char* get_value(char* dev, char* key);

int main(int argc, char* argv[])
{
    GtkWidget* menu;

#if  ! GTK_CHECK_VERSION(2,90,0)
    gtk_set_locale();
#endif

    progname = argv[0];
    gtk_init(&argc, &argv);
    kik_set_sys_conf_dir(CONFIG_PATH);

    menu = create_menu();

    g_signal_connect(menu, "deactivate", gtk_main_quit, NULL);
    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, 0);

    gtk_main();

    return 0;
}


GtkWidget* create_menu(void)
{
    GtkWidget* menu;
    char* rc_path;
    int userexist = 0;

    menu = gtk_menu_new();

    if ((rc_path = kik_get_user_rc_path(MENU_RCFILE))) {
        userexist = append_menu_from_file(GTK_MENU(menu), rc_path);
        free(rc_path);
    }
    if (userexist == 0 && (rc_path = kik_get_sys_rc_path(MENU_RCFILE))) {
        append_menu_from_file(GTK_MENU(menu), rc_path);
        free(rc_path);
    }

    gtk_widget_show_all(menu);

    return menu;
}


int append_menu_from_file(GtkMenu* menu, char* filename)
{
    FILE* file;
    GScanner* scanner;

    file = fopen(filename, "r");
    if (file == NULL) {
        return 0;
    }

    scanner = g_scanner_new(&menu_scanner_config);
    g_scanner_input_file(scanner, fileno(file));

    append_menu_from_scanner(menu, scanner, 0);

    g_scanner_destroy(scanner);

    fclose(file);

    return 1;
}

int append_menu_from_scanner(GtkMenu* menu, GScanner* scanner, int level)
{
    GtkWidget* item;
    GtkWidget* submenu;
    GTokenType tt;
    GTokenValue tv;

    while (g_scanner_peek_next_token(scanner) != G_TOKEN_EOF) {
        tt = g_scanner_get_next_token(scanner);

        switch (tt) {
        case G_TOKEN_STRING:
            tv = g_scanner_cur_value(scanner);
            item = gtk_menu_item_new_with_label(tv.v_string);
            gtk_menu_append(menu, item);

            switch (g_scanner_peek_next_token(scanner)) {
            case G_TOKEN_STRING:
                g_scanner_get_next_token(scanner);
                tv = g_scanner_cur_value(scanner);

                g_signal_connect(item, "activate",
                                   G_CALLBACK(activate_callback),
                                   (gpointer) strdup(tv.v_string));
                break;
            case G_TOKEN_LEFT_CURLY:
                g_scanner_get_next_token(scanner);

                submenu = gtk_menu_new();
                append_menu_from_scanner(GTK_MENU(submenu),
                                         scanner, level + 1);
                gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);

                break;
            default:
                break;
            }
            break;
        case G_TOKEN_IDENTIFIER:
            tv = g_scanner_cur_value(scanner);
            if (strcmp(tv.v_identifier, "pty_list") == 0) {
                append_pty_list(GTK_MENU(menu));
            } else if (strcmp(tv.v_identifier, "-") == 0) {
                gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new());
            } else {
                fprintf(stderr,
                        "%s: undefined identifier `%s'.\n",
                        progname, tv.v_identifier);
            }
            break;
        case G_TOKEN_RIGHT_CURLY:
            if ((level - 1) < 0) {
                fprintf(stderr, "%s: mismatched parentheses.\n", progname);
                return 0;
            } else {
                return 1;
            }
            break;
        default:
            break;
        }
    }

    return 1;
}

int append_pty_list(GtkMenu* menu)
{
    GtkWidget* item;
    char* my_pty ;
    char* pty_list;
    char* name_locale;
    char* name_utf8;
    char* pty;
    char* command;
    int is_active;
    GSList* group = NULL;

    if ((my_pty = get_value(NULL, "pty_name")) == NULL)
        return 1;
    if ((pty_list = get_value(NULL, "pty_list")) == NULL)
        return 1;

    while (pty_list) {
        pty = pty_list;
        pty_list = strchr(pty_list, ':');
        if (pty_list)
            *pty_list = 0;
        else
            break;
        if (*(pty_list + 1) == '1')
            is_active = 1;
        else
            is_active = 0;
        pty_list = strchr(pty_list + 1, ';');
        if (pty_list)
            pty_list++;

        if (is_active && strcmp(my_pty, pty) != 0) continue ;

        if ((name_locale = get_value(pty, "pty_name")) == NULL)
            name_locale = pty;
        if (strncmp(name_locale, "/dev/", 5) == 0)
            name_locale += 5;
        name_utf8 = g_locale_to_utf8(name_locale, -1, NULL, NULL, NULL);

        command = malloc(strlen(pty) + 12);
        sprintf(command, "select_pty %s", pty);

        item = gtk_radio_menu_item_new_with_label(group, name_utf8);
        group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));

        g_signal_connect(item, "toggled",
                           G_CALLBACK(toggled_callback),
                           (gpointer) command);

        gtk_menu_append(menu, item);
        g_free(name_utf8);

        if (strcmp(my_pty, pty) == 0) {
            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
        }
    }

    return 1;
}


void activate_callback(GtkWidget* widget, gpointer data)
{
    char* command = data;

    printf("\x1b]5379;%s\x07", command);
}


#if ! defined(G_PLATFORM_WIN32)
void activate_callback_copy(GtkWidget* widget, gpointer data)
{
    u_char* sel = (u_char*)data;
    size_t len = strlen( sel);
    size_t count;
    GtkClipboard* clipboard;
    
    if (!(clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD))) {
        return;
    }
    
    /* Replace CR to NL. */
    for (count = 0 ; count < len ; count++) {
        if (sel[count] == '\r') {
	    sel[count] = '\n' ;
	}
    }

    gtk_clipboard_set_text(clipboard, sel, len);

    /*
     * Gtk+ win32 doesn't support gtk_clipboard_store for now.
     * (see gdk_display_store_clipboard() in gdk/win32/gdkdisplay-win32.c).
     */
    gtk_clipboard_store(clipboard);
}
#endif


void toggled_callback(GtkWidget* widget, gpointer data)
{
    char* command = data;
    
    if(gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(widget))) {
      printf("\x1b]5379;%s\x07", command);
    }
}


char* get_value(char* dev, char* key)
{
    size_t count;
    char * ret;
    size_t len;
    char c;

    if (dev)
        printf("\x1b]5381;%s:%s\x07", dev, key);
    else
        printf("\x1b]5381;%s\x07", key);
    fflush(stdout);

    len = 1024;
    ret = malloc(len);
    count = 0;
    while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n') {
        ret[count] = c;
	if (++count >= len) {
	    ret = realloc( ret , (len += 1024));
	}
    }
    
    ret[count] = '\0';

    if (count < 2 + strlen(key) || strcmp(ret, "#error") == 0) {
	return NULL;
    }

    /*
     * #key=value
     */
    return ret + 2 + strlen(key);
}
