
/****************************************************************************
  This program takes the identifier of communication memory segment in its
  arguments.
  
  Keyboard input loop :

    - First read the result of last command executed by the main task.
    - Read keyboard until user press Return.
    - Send command to the main task.
    ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#ifndef WIN32
#include <unistd.h>
#include <string.h>
#else
#include "gsys/win32.h"
#endif

#include "gkbd_termio.h"
#include "gkbd_term.h"
#include "gkbd_histo.h"
#include "gkbd_line.h"
#include "gcore/gcomm.h"
#include "gsys/gag_trace.h"

#if defined(linux) || defined(darwin)
/* for RedHAt 5.1 */
#define JMP_BUF sigjmp_buf
#define SET_JMP( tag) sigsetjmp( tag, 1)
#define LONG_JMP( tag) siglongjmp( tag, 1)
#elif defined(WIN32)
#define JMP_BUF jmp_buf
#define SET_JMP( tag) setjmp( tag)
#define LONG_JMP( tag) longjmp( tag, 1)
#else
#define JMP_BUF jmp_buf
#define SET_JMP( tag) setjmp( tag)
#define LONG_JMP( tag) longjmp( tag, 1)
#endif

static JMP_BUF place_for_continue;
static int yesisatty = 0;
static command_line_t s_command_line;

void close_keyboard( )
{
    if (yesisatty) {
        reset_termio( );
    }
}

static void do_continue( )
{
    fflush( NULL);
    close_keyboard( );
    LONG_JMP( place_for_continue);
}

static void keyboard_on_exit( )
{
    gag_trace( "<trace: listener> keyboard_on_exit");
    close_keyboard( );
}

static void redraw_prompt( command_line_t *command_line)
{
    gag_trace( "<trace: listener> redraw_prompt");
    if (yesisatty) {
        s_command_line = *command_line;
        reset_kbd_line( command_line->prompt, command_line->line, &command_line->code);
    }
}

static void on_continue( int sig_num)
{
    gag_trace( "<trace: signal> on_continue on %d", sig_num);
    signal( sig_num, on_continue);
    do_continue( );
}

static void set_command_line( command_line_t * command_line, const char* line)
{
    size_t l;
    
    if (line != NULL)
        strcpy( command_line->line, line);

    l = strlen( command_line->line);

    /* skip line feed */
    if (l > 0 && command_line->line[l - 1] == '\n')
        l--;
    command_line->nc = l;
    command_line->code = 0;
}

static int s_keyboard_called_from_main_thread = 0;

static void read_keyboard( command_line_t * command_line)
{
    if (yesisatty) {
        command_line->nc = kbd_line( command_line->prompt, command_line->line, &command_line->code);
    } else {
        command_line->prompt[0] = '\0';
        if ((fgets( command_line->line, MAXBUF, stdin)) != NULL) {
            set_command_line( command_line, NULL);
        } else {
            command_line->code = code_eof;
        }
    }
    if (!s_keyboard_called_from_main_thread
     && command_line->code == code_eof) {
        set_command_line( command_line, "sic\\exit");
    }
}

static int s_keyboard_exit_loop = 0;

void keyboard_post_prompt( const char *prompt)
{
    command_line_t command_line;

    strcpy( command_line.prompt, prompt);
    set_command_line( &command_line, "");
    sic_post_prompt( &command_line);
}

void keyboard_exit_loop( )
{
    s_keyboard_exit_loop = 1;

    keyboard_post_prompt( "");
}

static void keyboard_loop( )
{
    int error_count = 0;

    while (1) {
        SET_JMP( place_for_continue);

        if (sic_wait_prompt( &s_command_line, 500) == -1) {
            /* work in progress */
            if (sic_wait_prompt( &s_command_line, -1) == -1) {
                error_count++;
                if (error_count > 10) {
                    fprintf( stderr, "sic_keyboard: too many errors, exiting!\n");
                    gag_trace( "<error> too many errors, exiting!");
                    break;
                }
            } else {
                error_count = 0;
            }
        }

        if (s_keyboard_exit_loop) {
            s_keyboard_exit_loop = 0;
            break;
        }

        SET_JMP( place_for_continue);

        read_keyboard( &s_command_line);
        if (s_keyboard_called_from_main_thread
         && s_command_line.code == code_eof)
            break;

        sic_post_command_from_prompt( &s_command_line);
    }
}

int run_keyboard( void *command_line)
{
    /*
    s_keyboard_called_from_main_thread = (command_line != NULL);
    */

    sic_add_redraw_prompt_listener( redraw_prompt);
    sic_add_exit_listener( keyboard_on_exit);

    /* determine if stdin is a tty or not and will enable exit on EOF if not */
    yesisatty = isatty( STDIN_FILENO);

    gkbd_histo_load( );
    if (command_line != NULL)
        gkbd_histo_push( (char *)command_line);
    keyboard_loop( );
    gkbd_histo_save( );

    return 0;
}
