/*
 * term.c
 *
 * Copyright 2004, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 */


#include<types.h>
#include<proc.h>
#include<signal.h>
#include<term.h>
#include<lib.h>
#include<debug.h>


//#define DEBUG_TERM 1

#ifdef DEBUG_TERM
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


/***************************************************************************************
 *
 * プロセスコントロール
 *
 ***************************************************************************************/


//================================== パブリック ========================================


/*
 * 子プロセスかつ同GIDのプロセスにシグナルを送る。
 * parameters : begin process,groupe ID,signal
 */
STATIC void sendSignalToGrp(PROC *proc,int signal)
{
	PROC *p;


	for (p = proc; (p = getNextProc(p,proc->pgid)) != NULL;)
		sendSignal(p,signal);
	sendSignal(proc,signal);
}


//================================== グローバル ========================================


/*
 * GLOBAL
 * カレントプロセスが端末のフォアグラウンドグループかどうか。
 */
int isForeground(PROC *proc)
{
	if (proc->ctlterm == NULL)
		return 0;
	if (((TERM_CTL*)proc->ctlterm)->ctlProc == NULL)
		return 1;
	return ((TERM_CTL*)proc->ctlterm)->foregrnd == proc->pgid;
}


/*
 * GLOBAL
 * カレントプロセスが端末制御プロセスかどうか。
 * return : yes=1 no=0;
 */
int isCtlproc(PROC *proc)
{
	if (proc->ctlterm == NULL)
		return 0;
	return ((TERM_CTL*)proc->ctlterm)->ctlProc == proc;
}


/*
 * ターミナルコントロールプロセスを開放する
 * parameters : process
 */
void releaseTerm(PROC *proc)
{
	
	if (((TERM_CTL*)(proc->ctlterm))->ctlProc == proc)
		((TERM_CTL*)(proc->ctlterm))->ctlProc = NULL;
}


/***************************************************************************************
 *
 * ターミナルバッファ
 *
 ***************************************************************************************/


//================================== プライベート ========================================


/*
 * メモリーを最後からコピー。
 * parameters : destination buffer,source buffer
 */
STATIC INLINE void memcpyBack(char *dst,char *src,int n)
{
	while (--n >= 0)
		dst[n] = src[n];
}

/* 1文字追加。 */
STATIC INLINE void putToBuf(char c,LINE_BUF *lbuf)
{
	if (lbuf->crt == LINE_BUF_SIZE)
		return;

	/* バッファに挿入 */
	if (lbuf->last == LINE_BUF_SIZE)
		--lbuf->last;
	memcpyBack(lbuf->buf + lbuf->crt + 1,lbuf->buf + lbuf->crt,lbuf->last - lbuf->crt);
	lbuf->buf[lbuf->crt] = c;
	++lbuf->last;
	++lbuf->crt;
}


//================================== パブリック ========================================


/* カレント位置を左に移動 */
STATIC void leftCursorBuf(LINE_BUF *lbuf)
{
	lbuf->pastCrt = lbuf->crt;
	if (lbuf->crt > lbuf->lineTop)
		--lbuf->crt;
}

/* カレント位置を右に移動 */
STATIC void rightCursorBuf(LINE_BUF *lbuf)
{
	lbuf->pastCrt = lbuf->crt;
	if (lbuf->crt < lbuf->last)
		++lbuf->crt;
}

/* 文字列を追加 */
STATIC void inputStrBuf(char *str,size_t n,LINE_BUF *lbuf)
{
	int i;
	
	
	lbuf->pastCrt = lbuf->crt;
	lbuf->pastLast = lbuf->last;
	for (i = 0; i < n; ++i)
		putToBuf(str[i],lbuf);
}

/* カレント+1以降の文字列を前に移動、最後に空白挿入。 */
STATIC void deleteBuf(LINE_BUF *lbuf)
{
	lbuf->pastLast = lbuf->last;
	if(lbuf->last > lbuf->crt)
	{
		memcpy(lbuf->buf + lbuf->crt,lbuf->buf + lbuf->crt+1,lbuf->last - lbuf->crt);
		lbuf->buf[lbuf->last - 1] = '\0';
		--lbuf->last;
	}
}

/* カレント以降の文字列を前に移動、最後に空白挿入。 */
STATIC void backSpaceBuf(LINE_BUF *lbuf)
{
	lbuf->pastCrt = lbuf->crt;
	lbuf->pastLast = lbuf->last;
	if(lbuf->crt>lbuf->lineTop)
	{
		memcpy(lbuf->buf+lbuf->crt-1,lbuf->buf+lbuf->crt,lbuf->last-lbuf->crt);
		lbuf->buf[lbuf->last-1]='\0';
		--lbuf->crt;
		--lbuf->last;
	}
}

/* ライン消去 */
STATIC void eraseLineBuf(LINE_BUF *lbuf)
{
	lbuf->pastCrt = lbuf->crt;
	lbuf->pastLast = lbuf->last;
	memset(lbuf->buf+lbuf->lineTop,0,lbuf->last-lbuf->lineTop);
	lbuf->crt=lbuf->last=lbuf->lineTop;
}

/* 改行 */
STATIC void newLineBuf(LINE_BUF *lbuf)
{
	if(lbuf->last<LINE_BUF_SIZE)
		lbuf->buf[lbuf->last++]='\n';
	lbuf->lineTop=lbuf->crt=lbuf->last;
}

/* EOF */
STATIC void eof(LINE_BUF *lbuf)
{
	if(lbuf->lineTop == lbuf->last)	/* 入力文字なし。 */
	{
		lbuf->buf[lbuf->last++] = TERM_EOF;
		lbuf->lineTop = lbuf->crt = lbuf->last;
	}
}


//================================== グローバル ========================================


/*
 * バッファから読み込む
 * parameters : user buffer,max copy size
 * return : 先行入力バイト数 or EOF=0 or 入力なし=-1
 */
int readTermBuf(uchar *buf,size_t size,struct termios *tio,LINE_BUF *lbuf)
{
	int len;


	/* 入力なし */
	if (lbuf->last == 0)
		return -1;

	if (tio->c_lflag & ICANON)	/* Canonical mode. */
	{

		if ((uchar)*lbuf->buf == TERM_EOF)
		{
			*lbuf->buf = '\0';
			lbuf->lineTop = lbuf->crt = lbuf->last = 0;

			return 0;
		}

		len = 0;
		while (lbuf->buf[len++] != '\n')
			if (lbuf->last <= len)
				return -1;
		len = (len < size)? len : size;
	}
	else							/* Non-canonical mode. */
		len = (size <= lbuf->last)? size : lbuf->last;
		
	memcpy(buf,lbuf->buf,len);
	memcpy(lbuf->buf,lbuf->buf + len,lbuf->last - len);
	lbuf->crt -= len;
	lbuf->last -= len;
	lbuf->lineTop -= len;

	return len;
}


/*
 * コンソールバッファが読み込み可能か
 * return : FALSE or TRUE
 */
int isExistTermBuf(struct termios *tio, LINE_BUF *lbuf)
{
	int i;

	if ((uchar)*lbuf->buf == TERM_EOF){
		return TRUE;
	}

	if (tio->c_lflag & ICANON){
		for (i = 0; i < lbuf->last; ++i){
			if (lbuf->buf[i] == '\n'){
				return TRUE;
			}
		}
	}
	else{
		if (0 < lbuf->last){
			return TRUE;
		}
	}

	return FALSE;
}


/***************************************************************************************
 *
 * 端末入力の処理
 *
 ***************************************************************************************/


/* ファンクションキーコード */
#define	FUNK_HOME	"\033[H"
#define	FUNK_UP		"\033[A"
#define	FUNK_DOWN	"\033[B"
#define	FUNK_LEFT	"\033[D"
#define	FUNK_RIGHT	"\033[C"


//================================== グローバル ========================================


/*
 * 端末入力の処理
 * parameters : キャラクタ,termios構造体,端末コントロール構造体
 */
int handlTermChr(uchar ch,struct termios *tio,TERM_CTL *ctl,LINE_BUF *lbuf)
{
	cc_t *cc = tio->c_cc;

	/* 共通コマンド処理 */	
	if (ch == cc[VINTR])	/* Send SIGINT. */
	{
		/* グループにSIGINTを送る。 */
		sendSignalToGrp(ctl->ctlProc->child,SIGINT);
		return 0;
	}
	else if (ch == cc[VQUIT])	/* Send SIGQUIT. */
		return 0;
	else if (ch == cc[VSTART])	/* Send SIGCONT.  */
		return 0;
	else if (ch == cc[VSTOP])	/* Send SIGSTOP. */
		return 0;
	else if (ch == cc[VSUSP])	/* Send SIGTSTP. */
	{
		/* グループにSIGTSTPを送る。 */
		sendSignalToGrp(ctl->ctlProc->child,SIGTSTP);
		return 0;
	}
	
	if (tio->c_lflag & ICANON)		/* Canonical mode. */
	{
		if (ch == cc[VEOF])								/* ファイルの終了。 */
			eof(lbuf);
		else if ((ch == cc[VEOL]) || (ch == '\n'))		/* 行の終了。 */
		{
			newLineBuf(lbuf);
			if (tio->c_lflag & ECHO)
				ctl->newLine();
		}
		else if ((ch == cc[VERASE]) || (ch == 0x7f))	/* DEL */
		{
			deleteBuf(lbuf);
			if (tio->c_lflag & ECHO)
				ctl->delete();
		}
		else if (ch == cc[VKILL])						/* 全１行抹消。 */
		{
			eraseLineBuf(lbuf);
			if (tio->c_lflag & ECHO)
				ctl->eraseLine();
		}
		else{
			switch(ch)
			{
				case '\b':								/* １文字後退消去。 */
					backSpaceBuf(lbuf);
					if (tio->c_lflag & ECHO){
						ctl->backSpace();
					}
					break;
				case TRM_UP:
					inputStrBuf(FUNK_UP,3,lbuf);
					if (tio->c_lflag & ECHO){
						ctl->inputStr();
					}
					break;
				case TRM_DWN:
					inputStrBuf(FUNK_DOWN,3,lbuf);
					if (tio->c_lflag & ECHO){
						ctl->inputStr();
					}
					break;
				case TRM_LFT:
					leftCursorBuf(lbuf);
					if (tio->c_lflag & ECHO){
						ctl->leftCursor(1);
					}
					break;
				case TRM_RHT:
					rightCursorBuf(lbuf);
					if (tio->c_lflag & ECHO){
						ctl->rightCursor(1);
					}
					break;
				default:
					if (ch < MIN_MAPKEY){
						inputStrBuf(&ch,1,lbuf);
						if (tio->c_lflag & ECHO){
							ctl->inputStr();
						}
					}
			}
		}
	}
	else						/* Non-canonical mode. */
	{
		switch(ch)
		{
			case TRM_UP:
				inputStrBuf(FUNK_UP,3,lbuf);
				break;
			case TRM_DWN:
				inputStrBuf(FUNK_DOWN,3,lbuf);
				break;
			case TRM_LFT:
				inputStrBuf(FUNK_LEFT,3,lbuf);
				break;
			case TRM_RHT:
				inputStrBuf(FUNK_RIGHT,3,lbuf);
				break;
			default:
				inputStrBuf(&ch,1,lbuf);
		}
	}

	return 0;
}
