/***   [keyin.c]
*
*	キーボード入力		(C)ささがわ
*
*	For GNU C Compiler (GCC)   Version 1.39
*
***/

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "graph.h"
#include "kyb.h"
#include "mos.h"
#include "keyin.h"

#define KEY_BUFSIZ	255		/* ﾊﾞｯﾌｧｻｲｽﾞ */
#define KEY_ROLLSPD	8		/* ｽｸﾛｰﾙ･ｽﾋﾟｰﾄﾞ 1 2 4 8 */

/* 関数ﾌﾟﾛﾄﾀｲﾌﾟ宣言 */
static void Curonoff(int);
static void bufins(char);
static void bufdel(void);
static void bufbks(void);
static void shift(int, int, int);
static char gputc(int);

/* ｸﾞﾛｰﾊﾞﾙ変数宣言 */
static char	buf[KEY_BUFSIZ + 1];	/* ﾊﾞｯﾌｧ宣言 */
static int	len;	/*	文字列の長さ				0 <= x <= KEY_BUFSIZ	*/
static int	cur;	/*	ﾋﾞｭｰﾎﾞｯｸｽ中のｶｰｿﾙの位置		0 <= x <= kt->keta - 1	*/
static int	start;	/*	ﾊﾞｯﾌｧ中のﾋﾞｭｰﾎﾞｯｸｽの位置	0 <= x <= len - cur		*/
static struct keyin_t	*kt;	/* 環境構造体 */

/* 初期化関数(ｸﾞﾛｰﾊﾞﾙ) */
void Keyin_init(struct keyin_t *keyt) {
	len = 0;
	start = 0;
	cur = 0;
	kt = keyt;
	MOS_disp(0);
	EGB_boxf(kt->x, kt->y - 15, kt->x + kt->keta * 8 - 1, kt->y, kt->backC, kt->backC);
	Curonoff(1);
	MOS_disp(1);
	KYB_clrbuf();
}

/* 再表示関数(ｸﾞﾛｰﾊﾞﾙ) */
void Keyin_redisp(void) {
	int		i;
	
	MOS_disp(0);
	for (i = 0; i < kt->keta; i++)
		gputc(i);
	Curonoff(1);
	MOS_disp(1);
}

/* カーソル移動関数(ｸﾞﾛｰﾊﾞﾙ) */
int Keyin_cur(int cr) {
	if (cr < 0 || kt->keta <= cr)
		return -1;
	
	if (cr > len - start)
		cr = len - start;
	if (cur != cr) {
		MOS_disp(0);
		Curonoff(0);
		cur = cr;
		Curonoff(1);
		MOS_disp(1);
	}
	
	return cur;
}

/***
*	メイン関数(ｸﾞﾛｰﾊﾞﾙ)
*	   戻り値  0:正常終了   1:入力完了   -1:中断
***/
int Keyin(void) {
	int			ret;
	unsigned	kadr, kcode;
	
	if ((kcode = KYB_read(1, &kadr)) == 0xffff) {
		ret = 0;
	} else if (kcode == 0x1b) {
		ret = -1;				/* 中断 */
	} else if (kcode == 0x0d) {
		buf[len] = '\0';
		buf[kt->bufn - 1] = '\0';
		strcpy(kt->buf, buf);	/* 入力完了 */
		ret = 1;
	} else {
		MOS_disp(0);
		Curonoff(0);
		
		if (kcode == 0x1c) {	/* [→] */
			KYB_clrbuf();	/* ｶｰｿﾙの惰性防止 */
			if (start + cur == len || (len == KEY_BUFSIZ && start + cur >= len - 1)) {
				/* ｶｰｿﾙが、文字列かﾊﾞｯﾌｧの末尾にあるならば */
				;
			} else if (cur == kt->keta - 1) {
				/* ｶｰｿﾙがﾋﾞｭｰﾎﾞｯｸｽの末尾にあるならば */
				start++;
				shift(0, kt->keta - 1, -1);
			} else {
				cur++;
			}
		} else if (kcode == 0x1d) {		/* [←] */
			KYB_clrbuf();	/* ｶｰｿﾙの惰性防止 */
			if (start + cur == 0) {
				/* ｶｰｿﾙがﾊﾞｯﾌｧの先頭にあるならば */
				;
			} else if (start > 0 && cur == 1) {
				/* ﾋﾞｭｰﾎﾞｯｸｽがﾊﾞｯﾌｧの先頭を表示しておらず、ｶｰｿﾙが1の位置にあるならば */
				start--;
				shift(0, kt->keta - 1, 1);
			} else {
				cur--;
			}
		} else if (kcode == 0x08) {		/* [ﾊﾞｯｸｽﾍﾟｰｽ] */
			KYB_clrbuf();	/* ｶｰｿﾙの惰性防止 */
			if (start + cur == 0) {
				/* ｶｰｿﾙがﾊﾞｯﾌｧの先頭にあるならば */
				;
			} else if (start > 0 && cur == 1) {
				/* ﾋﾞｭｰﾎﾞｯｸｽがﾊﾞｯﾌｧの先頭を表示しておらず、ｶｰｿﾙが1の位置にあるならば */
				bufbks();
				start--;
				gputc(0);
			} else {
				bufbks();
				shift(--cur, kt->keta - 1, -1);
			}
		} else if (kcode == 0x7f) {		/* [削除] */
			KYB_clrbuf();	/* ｶｰｿﾙの惰性防止 */
			if (start + cur < len) {
				/* ｶｰｿﾙが文字列中にあるならば */
				bufdel();
				shift(cur, kt->keta - 1, -1);
			}
		} else if (kcode < 0x100 && !iscntrl(kcode)) {
			bufins(kcode);
			if (cur == kt->keta - 1) {
				/* ｶｰｿﾙがﾋﾞｭｰﾎﾞｯｸｽの末尾にあるならば */
				gputc(cur);
				if (len != KEY_BUFSIZ || start + cur < len - 1) {
					/* ｶｰｿﾙがﾊﾞｯﾌｧの末尾にないならば */
					start++;
					shift(0, cur, -1);
				}
			} else {
				shift(cur++, kt->keta - 1, 1);
			}
		}
		
		Curonoff(1);
		MOS_disp(1);
		
		ret = 0;
	}
	
	return ret;
}

/* ｶｰｿﾙ表示・非表示関数 */
static void Curonoff(int sw) {
	switch (sw) {
		case 0:
			gputc(cur);
			break;
		
		case 1:
			EGB_box(kt->x + 8 * cur, kt->y - 15, kt->x + 8 * cur + 1, kt->y, kt->curC);
			break;
	}
}

/* 一文字挿入関数 (位置:start + cur) */
static void bufins(char c) {
	int	i;
	
	for (i = len - 1; i >= start + cur; i--)
		buf[i + 1] = buf[i];
	buf[start + cur] = c;
	len++;
	if (len >= KEY_BUFSIZ)
		len = KEY_BUFSIZ;
}

/* 一文字削除関数 */
static void bufdel(void) {
	int	i;
	
	for (i = start + cur + 1; i <= len - 1; i++)
		buf[i - 1] = buf[i];
	len--;
}

/* ﾊﾞｯｸｽﾍﾟｰｽ関数 */
static void bufbks(void) {
	int	i;
	
	for (i = start + cur; i <= len - 1; i++)
		buf[i - 1] = buf[i];
	len--;
}

/* 左右ｽｸﾛｰﾙ関数 */
static void shift(int from, int to, int dir) {
	int		i;
	
	if (dir >= 0) {
		for (i = 0; i < 8 / KEY_ROLLSPD; i++)
			EGB_scrl(0, kt->x + 8 * from, kt->y - 15, kt->x + 8 * to + 7, kt->y, KEY_ROLLSPD, 0);
		gputc(from);
	} else if (dir < 0) {
		for (i = 0; i < 8 / KEY_ROLLSPD; i++)
			EGB_scrl(0, kt->x + 8 * from, kt->y - 15, kt->x + 8 * to + 7, kt->y, -KEY_ROLLSPD, 0);
		gputc(to);
	}
}

/* 一文字表示関数 (k:ｶｰｿﾙ位置) */
static char gputc(int k) {
	char	str[5];
	
	EGB_writeMode(EGB_work, 9);
	EGB_color(EGB_work, EGB_COL_BACK, kt->backC);
	
	if (start + k >= len)
		str[0] = ' ';
	else
		str[0] = buf[start + k];
	str[1] = 0;
	EGB_str(str, kt->x + 8 * k, kt->y, kt->foreC);
	
	EGB_writeMode(EGB_work, 0);
	return str[0];
}
