/* GNU Chess 5.0 - input.c - Input thread and related
   Copyright (c) 2002 Free Software Foundation, Inc.

   GNU Chess is based on the two research programs 
   Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.

   GNU Chess is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Chess is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Chess; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Contact Info: 
     bug-gnu-chess@gnu.org
     cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
     lukas@debian.org
*/

/*
 * This used to use readline but this does not work well with gnuchess
 * looking for input while searching. 
 * Since these days console mode is only used for debugging readline is
 * no longer a priority.
 * Eliminated readline. If you want readline use "rlwrap".
 *
 * We also suppressed prompts as with the current setup they spring up at 
 * unpredictable places.
 *
 *
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>

#include "util.h"
#include "log.h"
#include "input.h"
#include "protocol.h"

/*
 * Status variable used by the input thread to signal
 * pending input. Thought about using flags for this
 * but this can be refined and is conceptually different
 * from flags.
 */
enum {
  INPUT_NONE,
  INPUT_AVAILABLE
};


static volatile int input_status = INPUT_NONE;
  
/*
 * Function to wake up the input thread, should be called after
 * input has been parsed.
 */
static void input_wakeup(void);


static char inputstr[MAXSTR];


/* The generic input routine */

static void getline_(){
  if(!fgets(inputstr,sizeof(inputstr), stdin)){
      /* Hack: pretend user has typed "quit" on EOF */
    strncpy(inputstr,"quit",sizeof(inputstr));
    inputstr[sizeof(inputstr)-1]='\0';
  }
}

static pthread_t input_thread;

/* Mutex and condition variable for thread communication */

static pthread_mutex_t input_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t input_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t wakeup_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t wakeup_cond = PTHREAD_COND_INITIALIZER;

/*
 * Posix threads are used to allow efficent polling for
 * input when the program is pondering.
 *
 * input_status is a boolean to indicate if a command
 * is being parsed and processed. It is set by
 * input function, and must be cleared by the thread
 * that uses the input.
 *
 * The main loop may explicitly wait_for_input, or
 * when pondering will examine the input_status
 * variable in Iterate.
 *
 */

static void *input_func(void *arg __attribute__((unused))){
    while (!(Protocol->state_flags & QUIT)) {
	pthread_mutex_lock(&input_mutex);
	getline_();
	input_status = INPUT_AVAILABLE;
	pthread_cond_signal(&input_cond);
	/*
	 * Hack to make sure we don't get stuck at the above getline_()!
	 */
	split_input(inputstr);

	if(tokeneq(token[0],"quit")){
	    SET(Protocol->state_flags,QUIT);
	}
	
	pthread_mutex_unlock(&input_mutex);

	if(Protocol->state_flags&QUIT){
	    break;
	}

	pthread_mutex_lock(&wakeup_mutex);
	/*
	 * Posix waits can wake up spuriously
	 * so we must ensure that we keep waiting
	 * until we are woken by something that has 
	 * consumed the input
	 */
	while ( input_status == INPUT_AVAILABLE ){
	    pthread_cond_wait(&wakeup_cond, &wakeup_mutex);
	}
	pthread_mutex_unlock(&wakeup_mutex);
    }
    return NULL;
}

static void input_wakeup(void){
    pthread_mutex_lock(&input_mutex);
    input_status = INPUT_NONE;
    pthread_mutex_unlock(&input_mutex);
    pthread_mutex_lock(&wakeup_mutex);
    pthread_cond_signal(&wakeup_cond);
    pthread_mutex_unlock(&wakeup_mutex);
}

void InputWakeup(){
    LogSetNewlinePrefix(LogDefault, "KILL: ");
       Log("%s\n",inputstr);
    LogSetNewlinePrefix(LogDefault,DEFAULT_NEWLINE_PREFIX);
    input_wakeup();
}

char *InputLook(){
    if(input_status==INPUT_AVAILABLE){
	LogSetNewlinePrefix(LogDefault, "PEEK: ");
	Log("%s\n",inputstr);
	LogSetNewlinePrefix(LogDefault,DEFAULT_NEWLINE_PREFIX);
	return inputstr;
    }else{
	return NULL;
    }
}


void InputWait(void){
  while (input_status == INPUT_NONE) {
    pthread_mutex_lock(&input_mutex);
    if (input_status == INPUT_NONE)
      pthread_cond_wait(&input_cond, &input_mutex);
    pthread_mutex_unlock(&input_mutex);
  }
}

void InputInit(void){
  pthread_create(&input_thread, NULL, input_func, NULL);
}

void InputCleanup(void){
  pthread_join(input_thread, NULL);
}



