 /* 
  * UAE - The Un*x Amiga Emulator
  * 
  * Interface to the Tcl/Tk GUI
  * 
  * Copyright 1996 Bernd Schmidt
  */

#include "sysconfig.h"
#include "sysdeps.h"
#include <signal.h>

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "disk.h"
#include "gui.h"

static int inpipe, outpipe;
static char *child_argv[4];
static char filename1[80];
static char filename2[80];
static int made_file = 0;
static struct stat stbuf;
static pid_t child_pid;
static int child_died = 0;

int quit_program;

static void sigchldhandler(int foo)
{
    child_died = 1;
}

int gui_init(void)
{
    struct timeval tv;
    int result;
    
    quit_program = 0;

    gettimeofday(&tv, NULL);
    sprintf(filename1, "/tmp/uaea%d", tv.tv_sec * 1000 + tv.tv_usec / 1000);
    result = mknod (filename1, 0600 | S_IFIFO, 0);
    if (result < 0)
	return result;
    made_file = 1;
    
    sprintf(filename2, "/tmp/uaeb%d", tv.tv_sec * 1000 + tv.tv_usec / 1000);
    result = mknod (filename2, 0600 | S_IFIFO, 0);
    if (result < 0)
	return result;
    made_file = 2;
    
    inpipe = open (filename1, O_RDONLY|O_NONBLOCK);
    if (inpipe < 0)
	return inpipe;
    
    /* Why doesn't O_WRONLY work? */
    outpipe = open (filename2, O_RDWR);
    if (outpipe < 0)
	return outpipe;
    
    fstat (inpipe, &stbuf);
    child_pid = fork();
    if (child_pid < 0)
	return child_pid;
    if (child_pid > 0) {
	fd_set fs;
	printf("Waiting for GUI process to start...\n");
	FD_ZERO(&fs);
	FD_SET(inpipe, &fs);
	signal(SIGCHLD, sigchldhandler);
	result = select(inpipe+1, &fs, NULL, NULL, NULL);
	if (result < 0) {
	    kill(child_pid, SIGTERM);
	    return -1;
	} else {
	    char buffer[20];
	    fstat (inpipe, &stbuf);
	    if (stbuf.st_size != strlen("Startup")+1) {
		kill(child_pid, SIGTERM);
		return -1;
	    }
	    read (inpipe, buffer, 8);
	    if (strncmp("Startup\n", buffer, 8) != 0) {
		kill(child_pid, SIGTERM);
		return -1;
	    }
	}
	if (made_file > 0)
	    unlink(filename1);
	if (made_file > 1)
	    unlink(filename2);
	made_file = 0;
	printf("OK.\n");
	return 0;
    }
    /* FIXME: how can I prevent that the child exits when I hit ^C in the
     * shell window? */
    child_argv[0] = "uae-ui.tk";
    child_argv[1] = my_strdup(filename1);
    child_argv[2] = my_strdup(filename2);
    child_argv[3] = NULL;
    execvp("uae-ui", child_argv);
    /* Shouldn't get here */
    exit(0);

}

void gui_exit(void)
{
    if (made_file > 0)
	unlink(filename1);
    if (made_file > 1)
	unlink(filename2);
    if (inpipe >= 0)
	close(inpipe);
    if (outpipe >= 0)
	close(outpipe);
    if (child_pid)
	kill(child_pid, SIGTERM);
}

void gui_led(int led, int on)
{
    char line[80];
    
    if (child_died || no_gui)
	return;
    
    sprintf(line, "%s %d\n", led == 0 ? "power" : "driveled", on);
    write(outpipe, line, strlen(line));
    if (led > 0) {
	sprintf(line, "%d\n", led-1);
	write(outpipe, line, strlen (line));
    }
}

void gui_filename(int num, char *name)
{
    char line[80];

    if (child_died || no_gui)
	return;

    sprintf(line, "drivename\n");
    write(outpipe, line, strlen(line));
    sprintf(line, "%d\n", num);
    write(outpipe, line, strlen(line));
    sprintf(line, "%s\n", name);
    write(outpipe, line, strlen(line));
}

static void getline(char *p)
{
    int cnt = 80;
    char buf;
    do {
	read(inpipe, &buf, 1);
	*p++ = buf;
    } while (--cnt && buf != '\n');
    *(p-1) = 0;
}

void gui_handle_events(void)
{
    char command[100];
    off_t oldsize = stbuf.st_size;
    
    if (child_died || no_gui)
	return;

    fstat (inpipe, &stbuf);
    if (stbuf.st_size > oldsize) {
	getline(command);
	if (strcmp (command, "eject") == 0) {
	    int drive;
	    getline(command);
	    drive = atoi(command);
	    disk_eject (drive);
	} else if (strcmp (command, "insert") == 0) {
	    int drive;
	    getline(command);
	    drive = atoi(command);
	    if (disk_empty(drive)) {
		fd_set fs;
		FD_ZERO(&fs);
		FD_SET(inpipe, &fs);
		write(outpipe, "ok\n", 3);
		select(inpipe+1, &fs, NULL, NULL, NULL);
		getline(command);
		disk_insert (drive, command);
	    } else
		write(outpipe, "no\n", 3);
	} else if (strcmp (command, "debug") == 0) {
	    activate_debugger();
	} else if (strcmp (command, "bye") == 0) {
	    broken_in = 1;
	    regs.spcflags |= SPCFLAG_BRK;
	    quit_program = 1;
	} else if (strcmp (command, "reset") == 0) {
	    m68k_reset();
	}

    }
}
