/* $Id: io.c,v 1.9 2002/04/28 19:35:29 in2 Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "config.h"
#include "pttstruct.h"
#include "perm.h"
#include "modes.h"
#include "common.h"
#include "proto.h"

#if defined(linux)
#define OBUFSIZE  2048
#define IBUFSIZE  128
#else
#define OBUFSIZE  4096
#define IBUFSIZE  256
#endif

extern int current_font_type;
extern char *fn_proverb;
extern userinfo_t *currutmp;
extern unsigned int currstat;
extern pid_t currpid;
extern int errno;
extern screenline_t *big_picture;
extern int t_lines, t_columns;  /* Screen size / width */
extern int curr_idle_timeout;
extern water_t water[6], *swater[5], *water_which;
extern char water_usies;

static char outbuf[OBUFSIZE], inbuf[IBUFSIZE];
static int obufsize = 0, ibufsize = 0;
static int icurrchar = 0;

/* ----------------------------------------------------- */
/* �w����ܰʺA�ݪO                                      */
/* ----------------------------------------------------- */
extern userec_t cuser;

static void hit_alarm_clock() {
    if(HAS_PERM(PERM_NOTIMEOUT) || PERM_HIDE(currutmp) || currstat == MAILALL)
	return;
//    if(time(0) - currutmp->lastact > IDLE_TIMEOUT - 2) {
    if(time(0) - currutmp->lastact > curr_idle_timeout - 2) {
	clear();
	if(currpid > 0) kill(currpid, SIGHUP);
    }
//    alarm(IDLE_TIMEOUT);
    alarm(curr_idle_timeout);
}

void init_alarm() {
    signal(SIGALRM, (void (*)(int))hit_alarm_clock);
//    alarm(IDLE_TIMEOUT);
    alarm(curr_idle_timeout);
}

/* ----------------------------------------------------- */
/* output routines                                       */
/* ----------------------------------------------------- */

void oflush() {
    if(obufsize) {
	write(1, outbuf, obufsize);
	obufsize = 0;
    }
}

void init_buf()
{

   memset(inbuf,0,IBUFSIZE);
}
void output(char *s, int len) {
    /* Invalid if len >= OBUFSIZE */

    if(obufsize + len > OBUFSIZE) {
	write(1, outbuf, obufsize);
	obufsize = 0;
    }
    memcpy(outbuf + obufsize, s, len);
    obufsize += len;
}

int ochar(int c) {
    if(obufsize > OBUFSIZE - 1) {
	write(1, outbuf, obufsize);
	obufsize = 0;
    }
    outbuf[obufsize++] = c;
    return 0;
}

/* ----------------------------------------------------- */
/* input routines                                        */
/* ----------------------------------------------------- */

static int i_newfd = 0;
static struct timeval i_to, *i_top = NULL;
static int (*flushf) () = NULL;

void add_io(int fd, int timeout) {
    i_newfd = fd;
    if(timeout) {
	i_to.tv_sec = timeout;
	i_to.tv_usec = 16384;  /* Ptt: �令16384 �קK������for loop�Ycpu time
				  16384 ���C��64�� */
	i_top = &i_to;
    } else
	i_top = NULL;
}

int num_in_buf() {
    return icurrchar - ibufsize;
}

int watermode = -1, wmofo = -1; 
/*
  WATERMODE(WATER_ORIG) | WATERMODE(WATER_NEW):
  Ptt ���y�^�U�Ϊ��Ѽ�
      watermode = -1  �S�b�^���y
                = 0   �b�^�W�@�����y  (Ctrl-R)
	        > 0   �b�^�e n �����y (Ctrl-R Ctrl-R)

  WATERMODE(WATER_OFO)  by in2
        wmofo     = -1  �S�b�^���y
	          = 0   ���b�^���y
		  = 1   �^���y���S������y
        wmofo     >=0 �ɦ�����y�N�u���, ���|��water[]��,
	              �ݦ^�����y���ɭԤ@���g�J.
*/

/*
	dogetch() is not reentrant-safe. SIGUSR[12] might happen at any time,
	and dogetch() might be called again, and then ibufsize/icurrchar/inbuf
	might be inconsistent.
	We try to not segfault here... 
*/

static int dogetch() {
    int len;

    if(ibufsize <= icurrchar) {

	if(flushf)
	    (*flushf)();

	refresh();

	if(i_newfd) {
	
	    struct timeval timeout;
	    fd_set readfds;
			
	    if(i_top) timeout=*i_top; /* copy it because select() might change it */
			
	    FD_ZERO(&readfds);
	    FD_SET(0, &readfds);
	    FD_SET(i_newfd, &readfds);

	    /* jochang: modify first argument of select from FD_SETSIZE */
	    /* since we are only waiting input from fd 0 and i_newfd(>0) */
		
	    while((len = select(i_newfd+1, &readfds, NULL, NULL, i_top?&timeout:NULL))<0)
	    {
		if(errno != EINTR)
		    abort_bbs(0);
		    /* raise(SIGHUP); */
	    }

	    if(len == 0)
		return I_TIMEOUT;

	    if(i_newfd && FD_ISSET(i_newfd, &readfds))
		return I_OTHERDATA;
	}


        while((len = read(0, inbuf, IBUFSIZE)) <= 0) {
            if(len == 0 || errno != EINTR)
                abort_bbs(0);
                /* raise(SIGHUP); */
        }              
	ibufsize = len;
	icurrchar = 0;
    }

    if(currutmp) 
	currutmp->lastact = time(0);
    return inbuf[icurrchar++];
}

static int water_which_flag=0;
int igetch() {
    register int ch;
    while((ch = dogetch())) {
	switch(ch) {
	case Ctrl('L'):
	    redoscr();
	    continue;
	case Ctrl('U'):
	    if(currutmp != NULL &&  currutmp->mode != EDITING
	       && currutmp->mode !=  LUSERS && currutmp->mode) {

		screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t));
		int y, x, my_newfd;

		getyx(&y, &x);
		memcpy(screen0, big_picture, t_lines * sizeof(screenline_t));
		my_newfd = i_newfd;
		i_newfd = 0;
		t_users();
		i_newfd = my_newfd;
		memcpy(big_picture, screen0, t_lines * sizeof(screenline_t));
		move(y, x);
		free(screen0);
		redoscr();
		continue;
	    } else
		return (ch);
        case KEY_TAB:
	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) )
		if( currutmp != NULL && watermode > 0 ){
		    watermode = (watermode + water_which->count)
			% water_which->count + 1;
		    t_display_new();
		    continue;
		}
	    return ch;
	    break;

        case Ctrl('R'):
	    if(currutmp == NULL)
		return (ch);

	    if( currutmp->msgs[0].pid && 
		WATERMODE(WATER_OFO) && wmofo == -1 ){
		int y, x, my_newfd;
		screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t));
		memcpy(screen0, big_picture, t_lines * sizeof(screenline_t));
		getyx(&y, &x);		
		my_newfd = i_newfd;
		i_newfd = 0;
		my_write2();
		memcpy(big_picture, screen0, t_lines * sizeof(screenline_t));
		i_newfd = my_newfd;
		move(y, x);
		free(screen0);
		redoscr();
		continue;
	    }
	   else if(!WATERMODE(WATER_OFO))
            {
		if( watermode > 0 ){
		    watermode = (watermode + water_which->count)
			% water_which->count + 1;
		    t_display_new();
		    continue;
		}
		else if( currutmp->mode == 0 &&
			 (currutmp->chatid[0]==2 || currutmp->chatid[0]==3) &&
			 water_which->count != 0 && watermode == 0) {
		    /* �ĤG���� Ctrl-R */
		    watermode = 1;
		    t_display_new();
		    continue;
		}
		else if(watermode==-1 && currutmp->msgs[0].pid) {
		    /* �Ĥ@���� Ctrl-R (�������Q��L���y) */
		    screenline_t *screen0;
		    int y, x, my_newfd;
		    screen0 = calloc(t_lines, sizeof(screenline_t));
		    getyx(&y, &x);
		    memcpy(screen0, big_picture, t_lines*sizeof(screenline_t));
		    
		    /* �p�G���btalk���ܥ����B�z���e�L�Ӫ��ʥ] (���hselect) */
		    my_newfd = i_newfd;
		    i_newfd = 0;
		    show_call_in(0, 0);
		    watermode = 0;
		    my_write(currutmp->msgs[0].pid, "���y��L�h �G ",
			     currutmp->msgs[0].userid, 0, NULL);
		    i_newfd = my_newfd;
		
		    /* �٭�ù� */
		    memcpy(big_picture, screen0, t_lines*sizeof(screenline_t));
		    move(y, x);
		    free(screen0);
		    redoscr();
		    continue;
		}
             }
	    return ch;
        case '\n':   /* Ptt�� \n���� */
	    continue;
        case Ctrl('T'):
	    if( WATERMODE(WATER_ORIG) || WATERMODE(WATER_NEW) ){
		if(watermode > 0) {
		    if(watermode>1)
			watermode--;
		    else
			watermode = water_which->count;	
		    t_display_new();
		    continue;
		}
	    }
	    return (ch);

        case Ctrl('F'):
	    if( WATERMODE(WATER_NEW) ){
		if(watermode >0){
		    if( water_which_flag == (int)water_usies )
			water_which_flag = 0;
		    else
			water_which_flag =
		                   (water_which_flag+1) % (int)(water_usies+1);
		    if(water_which_flag==0)
			water_which = &water[0];
		    else
			water_which = swater[water_which_flag-1];
		    watermode = 1;
		    t_display_new();
		    continue;
		}
	    }
	    return ch;

        case Ctrl('G'):
	    if( WATERMODE(WATER_NEW) ){
		if( watermode > 0 ){
		    water_which_flag=(water_which_flag+water_usies)%(water_usies+1);
		    if(water_which_flag==0)
			water_which = &water[0];
		    else
			water_which = swater[water_which_flag-1];
		    watermode = 1;
		    t_display_new();
		    continue;
		}
	    }
	    return ch;

        default:
	    return ch;
	}
    }
    return 0;
}

int oldgetdata(int line, int col, char *prompt, char *buf, int len, int echo) {
    register int ch, i;
    int clen;
    int x = col, y = line;
    extern unsigned char scr_cols;
#define MAXLASTCMD 12
    static char lastcmd[MAXLASTCMD][80];

    strip_ansi(buf, buf, STRIP_ALL);

    if(prompt) {

	move(line, col);

	clrtoeol();

	outs(prompt);

	x += strip_ansi(NULL,prompt,0);
    }

    if(!echo) {
	len--;
	clen = 0;
	while((ch = igetch()) != '\r') {
	    if(ch == '\177' || ch == Ctrl('H')) {
		if(!clen) {
		    bell();
		    continue;
		}
		clen--;
		if(echo) {
		    ochar(Ctrl('H'));
		    ochar(' ');
		    ochar(Ctrl('H'));
		}
		continue;
	    }
// Ptt
#ifdef BIT8
	    if(!isprint2(ch))
#else
		if(!isprint(ch))
#endif
		{
		    if(echo)
			bell();
		    continue;
		}

	    if(clen >= len)  {
		if(echo)
		    bell();
		continue;
	    }
	    buf[clen++] = ch;
	    if(echo)
		ochar(ch);
	}
	buf[clen] = '\0';
	outc('\n');
	oflush();
    } else {
	int cmdpos = -1;
	int currchar = 0;

	standout();
	for(clen = len--; clen; clen--)
	    outc(' ');
	standend();
	buf[len] = 0;
	move(y, x);
	edit_outs(buf);
	clen = currchar = strlen(buf);

	while(move(y, x + currchar), (ch = igetkey()) != '\r') {
	    switch(ch) {
	    case KEY_DOWN:
	    case Ctrl('N'):
                buf[clen] = '\0';  /* Ptt */
                strncpy(lastcmd[cmdpos], buf, 79);
                cmdpos += MAXLASTCMD - 2;
	    case Ctrl('P'):
	    case KEY_UP:
                if(ch == KEY_UP || ch == Ctrl('P')) {
		    buf[clen] = '\0';  /* Ptt */
		    strncpy(lastcmd[cmdpos], buf, 79);
                }
                cmdpos++;
                cmdpos %= MAXLASTCMD;
                strncpy(buf, lastcmd[cmdpos], len);
                buf[len] = 0;

                move(y, x);                   /* clrtoeof */
                for(i = 0; i <= clen; i++)
		    outc(' ');
                move(y, x);
                edit_outs(buf);
                clen = currchar = strlen(buf);
                break;
	    case KEY_LEFT:
                if(currchar)
		    --currchar;
                break;
	    case KEY_RIGHT:
                if(buf[currchar])
		    ++currchar;
                break;
	    case '\177':
	    case Ctrl('H'):
                if(currchar) {
		    currchar--;
		    clen--;
		    for(i = currchar; i <= clen; i++)
			buf[i] = buf[i + 1];
		    move(y, x + clen);
		    outc(' ');
		    move(y, x);
		    edit_outs(buf);
                }
                break;
	    case Ctrl('Y'):
                currchar = 0;
	    case Ctrl('K'):
                buf[currchar] = 0;
                move(y, x + currchar);
                for(i = currchar; i < clen; i++)
		    outc(' ');
                clen = currchar;
                break;
	    case Ctrl('D'):
                if(buf[currchar]) {
		    clen--;
		    for(i = currchar; i <= clen; i++)
			buf[i] = buf[i + 1];
		    move(y, x + clen);
		    outc(' ');
		    move(y, x);
		    edit_outs(buf);
                }
                break;
	    case Ctrl('A'):
                currchar = 0;
                break;
	    case Ctrl('E'):
                currchar = clen;
                break;
	    default:
                if(isprint2(ch) && clen < len && x + clen < scr_cols) {
		    for(i = clen + 1; i > currchar;i--)
			buf[i] = buf[i - 1];
		    buf[currchar] = ch;
		    move(y, x + currchar);
		    edit_outs(buf + currchar);
		    currchar++;
		    clen++;
		}
                break;
	    }/* end case */
	}  /* end while */

	if(clen > 1)
	    for(cmdpos = MAXLASTCMD - 1; cmdpos; cmdpos--) {
                strcpy(lastcmd[cmdpos], lastcmd[cmdpos - 1]);
                strncpy(lastcmd[0], buf, len);
	    }
	if(echo)
	    outc('\n');
	refresh();
    }
    if((echo == LCECHO) && ((ch = buf[0]) >= 'A') && (ch <= 'Z'))
	buf[0] = ch | 32;
#ifdef SUPPORT_GB    
    if(echo == DOECHO &&  current_font_type == TYPE_GB)
    {
	strcpy(buf,hc_convert_str(buf, HC_GBtoBIG, HC_DO_SINGLE));
    }
#endif
    return clen;
}

/* Ptt */
int getdata_buf(int line, int col, char *prompt, char *buf, int len, int echo) {
    return oldgetdata(line, col, prompt, buf, len, echo);
}

char
getans(char *prompt)
{
  char ans[5];

  getdata(t_lines-1, 0, prompt, ans, sizeof(ans), LCECHO);
  return ans[0];
}

int getdata_str(int line, int col, char *prompt, char *buf, int len, int echo, char *defaultstr) {
    strncpy(buf, defaultstr, len);
    
    buf[len - 1] = 0;
    return oldgetdata(line, col, prompt, buf, len, echo);
}

int getdata(int line, int col, char *prompt, char *buf, int len, int echo) {
    buf[0] = 0;
    return oldgetdata(line, col, prompt, buf, len, echo);
}

int
rget(int x,char *prompt)
{
  register int ch;

  move(x,0);
  clrtobot(); 
  outs(prompt);
  refresh();

  ch = igetch();
  if( ch >= 'A' && ch <= 'Z') ch |= 32;

   return ch;
}


int KEY_ESC_arg;

int igetkey() {
    int mode;
    int ch, last;

    mode = last = 0;
    while(1) {
	ch = igetch();
	if(mode == 0) {
	    if(ch == KEY_ESC)
		mode = 1;
	    else
		return ch;              /* Normal Key */
	} else if (mode == 1) {         /* Escape sequence */
	    if(ch == '[' || ch == 'O')
		mode = 2;
	    else if(ch == '1' || ch == '4')
		mode = 3;
	    else {
		KEY_ESC_arg = ch;
		return KEY_ESC;
	    }
	} else if(mode == 2) {          /* Cursor key */
	    if(ch >= 'A' && ch <= 'D')
		return KEY_UP + (ch - 'A');
	    else if(ch >= '1' && ch <= '6')
		mode = 3;
	    else
		return ch;
	} else if (mode == 3) {         /* Ins Del Home End PgUp PgDn */
	    if(ch == '~')
		return KEY_HOME + (last - '1');
	    else
		return ch;
	}
	last = ch;
    }
}