/**
 * Copyright (c) 2007-2012, Timothy Stack
 *
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither the name of Timothy Stack nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <util.h>
#include <sys/time.h>

#include <readline/readline.h>

#include "vt52_curses.hh"

static const int KEY_TIMEOUT = 500 * 1000;

static int got_line = 0;
static int got_timeout = 0;

static void sigalrm(int sig)
{
    got_timeout = 1;
}

static void line_ready(char *line)
{
    fprintf(stderr, "got line: %s\n", line);
    add_history(line);
    got_line = 1;
}

static void child_readline(void)
{
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);
    
    rl_callback_handler_install("/", (void (*)())line_ready);
    while (1) {
	fd_set ready_rfds = rfds;
	int rc;

	rc = select(STDIN_FILENO + 1, &ready_rfds, NULL, NULL, NULL);
	if (rc < 0) {
	    switch (errno) {
	    case EINTR:
		break;
	    }
	}
	else {
	    if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
		struct itimerval itv;

		itv.it_value.tv_sec = 0;
		itv.it_value.tv_usec = KEY_TIMEOUT;
		itv.it_interval.tv_sec = 0;
		itv.it_interval.tv_usec = 0;
		setitimer(ITIMER_REAL, &itv, NULL);
		
		rl_callback_read_char();
	    }
	}

	if (got_timeout) {
	    fprintf(stderr, "got timeout\n");
	    got_timeout = 0;
	}
	if (got_line) {
	    rl_callback_handler_remove();
	    got_line = 0;
	    rl_callback_handler_install("/", (void (*)())line_ready);
	}
    }
}

static void finish(int sig)
{
    endwin();
    exit(0);
}

int main(int argc, char *argv[])
{
    int fd, retval = EXIT_SUCCESS;
    signal(SIGALRM, sigalrm);
    
    fd = open("/tmp/rltest.err", O_WRONLY|O_CREAT|O_APPEND, 0666);
    dup2(fd, STDERR_FILENO);
    fprintf(stderr, "startup\n");

    if (0) {
	while(1) {
	    char *ret = readline("/");

	    add_history(ret);
	}
    }
    
    (void) signal(SIGINT, finish);      /* arrange interrupts to terminate */

    WINDOW *mainwin = initscr();      /* initialize the curses library */
    keypad(stdscr, TRUE);  /* enable keyboard mapping */
    (void) nonl();         /* tell curses not to do NL->CR/NL on output */
    (void) cbreak();       /* take input chars one at a time, no wait for \n */
    (void) noecho();       /* don't echo input */

    if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0)
	perror("fcntl");

    {
	int master, slave;
	pid_t rl;
	
	if (openpty(&master, &slave, NULL, NULL, NULL) < 0) {
	    perror("openpty");
	}
	else if ((rl = fork()) < 0) {
	    perror("fork");
	}
	else if (rl == 0) {
	    close(master);
	    master = -1;

	    dup2(slave, STDIN_FILENO);
	    dup2(slave, STDOUT_FILENO);

	    setenv("TERM", "vt52", 1);
	    
	    child_readline();
	}
	else {
	    vt52_curses vc(mainwin);
	    fd_set rfds;
	    
	    FD_ZERO(&rfds);
	    FD_SET(STDIN_FILENO, &rfds);
	    FD_SET(master, &rfds);

	    while (1) {
		fd_set ready_rfds = rfds;
		int rc;

		rc = select(master + 1, &ready_rfds, NULL, NULL, NULL);
		if (rc < 0) {
		    break;
		}
		else {
		    char buffer[1024];
		    
		    if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
			int ch;

			if ((ch = getch()) != ERR) {
			    const char *bch;
			    int len;

			    bch = vc.map_input(ch, len);

			    if (len > 0) {
				fprintf(stderr, "stdin: %x\n", ch);
				if (write(master, bch, len) < 0)
				    perror("write");
			    }
			}
		    }
		    if (FD_ISSET(master, &ready_rfds)) {
			int lpc;
			
			rc = read(master, buffer, sizeof(buffer));

			fprintf(stderr, "child: ");
			for (lpc = 0; lpc < rc; lpc++) {
			    fprintf(stderr, "%x ", buffer[lpc]);
			}
			fprintf(stderr, "\n");
			
			vc.map_output(buffer, rc);
		    }
		}
		refresh();
	    }
	}
    }

    finish(0);
    
    return retval;
}
