#pragma ident "@(#) jmvwgetnstr.c 2.0.1 2006.07.20,21:15"
/*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*
   NAME      :jmvwgetnstr()

   FUNCTION  :
	int jmvwgetnstr(WINDOW *win, int y, int x, unsigned char *str, int n, int wmode, int cmode);
              
   RETURN    :漢字、数字、アルファベット等ASCII文字。

  説明

  本プログラムはsubformライブラリでの文字入力に漢字を利用出来るようにしたものです。
      
  多言語化対応
  日本語に対応。    

  診断
      

  例
      添付の sample.c を参照

  参照
	ncurses
      
  バグ
      
      
  著者
	
  	本プログラムはinori氏により開発されS.Onoにより改編されています。
	改編：Shigeki Ono. 2001/01/15
      
  標準準拠
      


  作者後書


*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*X*/

#include <stdlib.h>
#include <string.h>
#include <curses.h>
#include "jmvwgetnstr.h"

#define FLG_OFF					(0)
#define FLG_ON					(!FLG_OFF)

#define KT_ANSI					(1)
#define KT_KNJ1					(2)
#define KT_KNJ2					(3)

#define TEXT			1
#define NUMERIC			2
#define BOOL			3
#define CURRENCY		4
#define DATE			5
#define SINGLE			6
#define DOUBLE			7
#define BYTE			8

#define	isnumeric(c)	((c >= 0x30 && c <= 0x39 || c == 0x2e || c == 0x2d) ? (FLG_ON) : (FLG_OFF))	

static void setktype(char *ktype, unsigned char *str) {
	int k1;
	
	k1 = FLG_OFF;
	while (*str) {
		if (k1 == FLG_ON) {
			*ktype = KT_KNJ2;
			k1 = FLG_OFF;
		} else if (*str <= 0x07f) {
			*ktype = KT_ANSI;
		} else {
			*ktype = KT_KNJ1;
			k1 = FLG_ON;
		}
		ktype++, str++;
	}
	*ktype = 0;
}

static unsigned char int2uchar(int n) {
	return  (unsigned char)(n & 0x0ff);
}

static int inschr(WINDOW *win,unsigned char *str, int n, int pos, int ch[], int insmode) {	// ch[0]: ansi or knj1   ch[1]: knj2
	int len;
	int adj;
	char *ktype;
	
	if (ch[0] == 0 || (ch[0] > 0x07f && !ch[1]) || !str || n < 0 || pos < 0 || pos > n-1)
		return  E_INVALID_ARG;
	
	if ((ktype = (char *)calloc(1, n+3)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	len = strlen(str);
	if (insmode) {
		adj = (ch[1])? 1 : 0;
		if (n - len - adj < 1) {
			free(ktype);
			return  E_CANT_INSERT;
		}
		memmove(str+pos+1+adj, str+pos, n-pos-adj);
		*(str+pos) = int2uchar(ch[0]);
		if (ch[1]) *(str+pos+1) = int2uchar(ch[1]);
	} else {
		if (ch[1] && pos == n-1) {
			free(ktype);
			return  E_CANT_INSERT;
		}
		*(str+pos) = int2uchar(ch[0]);
		if (ch[1])
			*(str+pos+1) = int2uchar(ch[1]);
		else if (*(ktype+pos) == KT_KNJ1)
			*(str+pos+1) = ' ';
	}
	free(ktype);
	
	return  0;
}

static int rinschr(unsigned char *str, int n, int pos, int ch[], int insmode) {	// ch[0]: ansi or knj1   ch[1]: knj2
	int len;
	int adj;
	char *ktype;
	
	if (ch[0] == 0 || (ch[0] > 0x07f && !ch[1]) || !str || n < 0 || pos < 0 || pos > n-1)
		return  E_INVALID_ARG;

	if (!isnumeric(ch[0]))
		return  E_INVALID_ARG;
	
	if ((ktype = (char *)calloc(1, n+3)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	len = strlen(str);
	if(len !=n) {
		free(ktype);
		return(0);
	}
	adj = (ch[1])? 1 : 0;
	if (insmode) {
		if(str[0] != ' ') {
			free(ktype);
			return  E_CANT_INSERT;
		}
			memmove(str, str+1+adj, pos+adj);
		if (ch[1]) {
			free(ktype);
			return  E_CANT_INSERT;
		} else {
			*(str+pos) = int2uchar(ch[0]);
		}
	} else {
		if (ch[1])
		{
			free(ktype);
			return  E_CANT_INSERT;
		} else {
			memmove(str+((pos-1)-adj), str+(pos-1), 1+adj);
			*(str+(pos)) = int2uchar(ch[0]);
		}
		//if (*(ktype+(pos-1)) == KT_KNJ1)
		//	*(str+pos) = ' ';
	}
	free(ktype);
	
	return  0;
}

static int delchr(WINDOW *win,unsigned char *str, int n, int pos) {	// ch[0]: ansi or knj1   ch[1]: knj2
	int len;
	int adj;
	char *ktype;
	
	if (!str || n < 0 || pos < 0 || pos > n-1)
		return  E_INVALID_ARG;
	
	if ((ktype = (char *)calloc(1, n)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	len = strlen(str);
	if (len < 1) {
		free(ktype);
		return  E_CANT_DELETE;
	}
	adj = (*(ktype+pos) == KT_KNJ1) ? 1 : 0;
	wattroff(win, A_UNDERLINE);
	if(adj)
	{
	//	wattroff(win, A_UNDERLINE);
		memmove(str+pos, str+pos+1+adj, n-pos-adj);
	}
	else
	{
	//	wattron(win, A_UNDERLINE);
		memmove(str+pos, str+pos+1+adj, n-pos-adj);
	}

	free(ktype);
	
	return  0;
}

static int rdelchr(unsigned char *str, int n, int pos) {	// ch[0]: ansi or knj1   ch[1]: knj2
	int len;
	int adj;
	char *ktype;
	
	if (!str || n < 0 || pos < 0 || pos > n-1)
		return  E_INVALID_ARG;
	
	if ((ktype = (char *)calloc(1, n)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	len = strlen(str);
	if (len < 1) {
		free(ktype);
		return  E_CANT_DELETE;
	}
	adj = (*(ktype+(pos-1)) == KT_KNJ1) ? 1 : 0;
	memmove(str+1+adj, str, n-1-adj);
	memset(str+0,0x20,1);

	free(ktype);
	
	return  0;
}

static int moveleft(unsigned char *str, int n, int *cur) {
	char *ktype;
	
	if ((ktype = (char *)calloc(1, n)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	if (!str) {
		free(ktype);
		return  E_INVALID_BUF;
	} else if (*cur < 1) {
		free(ktype);
		return  E_INVALID_POS;	// move fail ;-<
	}
	
	(*cur)--;
	if (*(ktype+*cur) == KT_KNJ2) {
		free(ktype);
		return  moveleft(str, n, cur);
	} else {
		free(ktype);
		return  0;	// move OK!
	}
}

static int moveright(unsigned char *str, int n, int *cur) {
	char *ktype;
	
	if ((ktype = (char *)calloc(1, n)) == NULL)
		return  E_NOT_ENOUGH_MEM;
	setktype(ktype, str);
	
	if (!str) {
		free(ktype);
		return  E_INVALID_BUF;
	} else if (*cur >= strlen(str) || *cur >= n-1) {
		if (*(ktype+*cur) == KT_KNJ2)
			(*cur)--;
		free(ktype);
		return  E_INVALID_POS;	// move fail ;-<
	}
		
	(*cur)++;
	if (*(ktype+*cur) == KT_KNJ2) {
		free(ktype);
		return  moveright(str, n, cur);
	} else {
		free(ktype);
		return  0;
	}
}

int jmvwgetnstr(WINDOW *win, int y, int x, unsigned char *str, int n, int wmode, int cmode) {
	int winmaxy, winmaxx;
	unsigned char *buf;
	int ch[2];
	int cur;
	int insmode = FLG_ON;
	int adj;

	getmaxyx(win, winmaxy, winmaxx);
	
	// argument check
	if (!win)
		return  E_INVALID_WINDOW;
	else if (y<0 || winmaxy<=y || x<0 || winmaxx<=x)
		return  E_INVALID_POS;
	else if (n<1 || winmaxx<=x+n)
		return  E_INVALID_LEN;
	else if (!str)
		return  E_INVALID_STR;
	else if ((buf = (unsigned char *)calloc(1, n+3)) == NULL)		// +3 ---- +1/+3 for terminate \0, +2/+3 for inserting KANJI
		return  E_NOT_ENOUGH_MEM;
	
	// initialize terminal for this function
	savetty();
	cbreak();
	noecho();
	keypad(win, TRUE);
	meta(win, TRUE);
	
	// copy to buffer from default string
	strncpy(buf, str, n);
	*(buf+n) = 0;
	
	// cur = (strlen(buf)+1 >= n) ? (n-1) : strlen(buf);			// cursor move to last
	cur = 0;														// cursor move to top
	
	// command loop
	ch[1] = 0;
	//insmode = !insmode;
	insmode = wmode;
	switch(cmode)
	{
	case NUMERIC: case CURRENCY:
		cur = strlen(buf)-1;
		for (;;) {
			mvwprintw(win, y, x, "%-*.*s", n, n, buf);					// print editing buffer
			wmove(win, y, x+cur);
			touchwin(win);
			wrefresh(win);
			ch[0] = wgetch(win);
			if (ch[0] == KEY_ESC) {
				resetty();
				free(buf);
				return  ch[0];
			} else if ((ch[0] == KEY_ENTER)
			  		|| (ch[0] == KEY_RETURN)
			  		|| (ch[0] == KEY_TAB)
			  		|| (ch[0] == KEY_F(3))
			  		|| (ch[0] == KEY_F(4))
			  		|| (ch[0] == KEY_UP)
			  		|| (ch[0] == KEY_DOWN))  {
				break;
			}
			else if (ch[0] == KEY_LEFT)
				moveleft(buf, n, &cur);
			else if (ch[0] == KEY_RIGHT)
				moveright(buf, n, &cur);
			else if (ch[0] == KEY_BACKSPACE) {
			//	if ((cur >= n-1 && *(buf+cur)) || !moveleft(buf, n, &cur))
				{
					cur = n-1;
					rdelchr(buf, n, cur);
				}
			} else if (ch[0] == KEY_DC) {
				cur = n-1;
				rdelchr(buf, n, cur);
			}
			else if (ch[0] == KEY_IC)
				insmode = !insmode;
			else {
				if (ch[0] > 0x7f)
					ch[1] = wgetch(win);
				if (!rinschr(buf, n, cur, ch, insmode))
				{
					if(!insmode) {
						moveright(buf, n, &cur);
					}
				}
				ch[1] = 0;
			}
		
		}
		break;
	case TEXT: case DATE:
		cur = 0;
		for (;;) {
			mvwprintw(win, y, x, "%-*.*s", n, n, buf);					// print editing buffer
			wmove(win, y, x+cur);
			touchwin(win);
			wrefresh(win);
			ch[0] = wgetch(win);
			if (ch[0] == KEY_ESC) {
				resetty();
				free(buf);
				return  ch[0];
			} else if ((ch[0] == KEY_ENTER)
			  		|| (ch[0] == KEY_RETURN)
			  		|| (ch[0] == KEY_TAB)
			  		|| (ch[0] == KEY_F(3))
			  		|| (ch[0] == KEY_F(4))
			  		|| (ch[0] == KEY_UP)
			  		|| (ch[0] == KEY_DOWN))  {
				wattron(win, A_UNDERLINE | A_DIM | A_PROTECT);
				break;
			}
			else if (ch[0] == KEY_LEFT)
				moveleft(buf, n, &cur);
			else if (ch[0] == KEY_RIGHT)
				moveright(buf, n, &cur);
			else if (ch[0] == KEY_BACKSPACE) {
				if ((cur >= n-1 && *(buf+cur)) || !moveleft(buf, n, &cur))
					delchr(win,buf, n, cur);
			} else if (ch[0] == KEY_DC)
				delchr(win,buf, n, cur);
			else if (ch[0] == KEY_IC)
				insmode = !insmode;
			else {
				if (ch[0] > 0x7f)
					ch[1] = wgetch(win);
				if (!inschr(win,buf, n, cur, ch, insmode))
					moveright(buf, n, &cur);
				ch[1] = 0;
			}
		}
		break;
	}
	
	// copy back to return string from buffer
	strncpy((char *)str, (char *)buf, (size_t)n);
	*(str+n) = 0;
	
	// restore terminal status
	resetty();
	free(buf);
	return  ch[0];
}


#ifdef DEBUG

int main(int argc, char *argv[]) {
	int ch = 0;
	unsigned char buf[10+1];
	WINDOW *win;
	WINDOW *win2;

	initscr();
	cbreak();
	noecho();
	keypad(stdscr, TRUE);
	meta(stdscr, TRUE);
	
	win = newwin(0,0,0,0);
	box(win, ACS_VLINE, ACS_HLINE);
	wrefresh(win);
	win2 = subwin(win, LINES -4, COLS -4, 2, 2);
	box(win2, ACS_VLINE, ACS_HLINE);
//	mvwprintw(win2, 1, 1, "[%-10.10s]", "");
	mvwprintw(win2, 1, 1, "%s", "          ");
	strcpy(buf, "1234567890");
	touchwin(win2);
	wrefresh(win2);
	refresh();
	ch = jmvwgetnstr(win2, 1, 2, buf, 10,1,1);
	mvwprintw(win2, 2, 1, "[%s]  length=%d  exitkey=%04x \n", buf, strlen(buf), ch);
	mvwprintw(win2, 4, 1, "Push any key !!");
	touchwin(win2);
	wrefresh(win2);
	(void)wgetch(win2);
	
	endwin();

	return 0;
}

#endif
