#include	<stdio.h>
#include	<curses.h>
#include	<locale.h>
#include	<signal.h>
#include	<errno.h>
#include	<varargs.h>
#include	<sys/socket.h>
#include	<netinet/in.h>
#include	<netinet/tcp.h>
#include	<netdb.h>
#include	<termios.h>
#include	<termio.h>
#include	<sys/termio.h>

#define	DB(x)	x
#define	VERSION	"Client version 0.1"

/* ソケット ディスクリプタ */
int	Soc=0;

/* CURSESウインドウ */
WINDOW	*BaseWin=NULL;
WINDOW	*TitleWin=NULL;
WINDOW	*MenuWin=NULL;
WINDOW	*WorkWin=NULL;
WINDOW	*DebugWin=NULL;

void main(int argc,char *argv[])
{
	if(argc<=1){
		fprintf(stderr,"TestClient server-hostname\n");
		exit(1);
	}

	/* サーバとの通信初期化 */
	if(InitSocket(argv[1])==-1){
		exit(-1);
	}

	/* CURSES初期化 */
	InitCurses();

	/* シグナルセット */
	InitSignal();

	/* メインメニュー */
	MainMenu();
}

int InitSocket(char *hostname)
{
struct servent	*se;
struct hostent	*servhost;
struct sockaddr_in	server;

	DB(fprintf(stderr,"InitSocket(%s)\n",hostname));

	if((se=getservbyname("TestServer","tcp"))==NULL){
		perror("getservbyname");
		return(-1);
	}

	if((servhost=gethostbyname(hostname))==NULL){
		perror("gethostbyname");
		return(-1);
	}

	if((Soc=socket(AF_INET,SOCK_STREAM,0))<0){
		perror("socket");
		return(-1);
	}

	memset((char *)&server,0,sizeof(server));
	server.sin_family=AF_INET;
	server.sin_port=se->s_port;
	memcpy((char *)&server.sin_addr,servhost->h_addr,servhost->h_length);
	if(connect(Soc,&server,sizeof(server))==-1){
		perror("connect");
		return(-1);
	}

	SendAndRecv(NULL,0);

	return(0);
}

int InitCurses()
{
void	end();

	DB(fprintf(stderr,"InitCurses()\n"));

	/* ロケールのセット */
	setlocale(LC_ALL,"");

	/* CURSES初期化 */
	BaseWin=initscr();

	/* 画面サイズのチェック */
	if(LINES<23||COLS<80){
		mvwprintw(BaseWin,0,0,"画面サイズは23行×80文字以上で使用してください。");
		wrefresh(BaseWin);
		beep();
		sleep(3);
		end(0);
	}

	wrefresh(BaseWin);

	/* 入力モードの設定 */
	cbreak();
	noecho();

	/* 子画面の作成 */
	CreateWindows();

	return(0);
}

int CreateWindows()
{
	wclear(BaseWin);
	wrefresh(BaseWin);

	/* 上部タイトル */
	TitleWin=newwin(3,COLS,0,0);
	box(TitleWin,0,0);
	CenterPrint(TitleWin,1,COLS,VERSION);
	wrefresh(TitleWin);

	/* 左のメニュー */
	MenuWin=newwin(LINES-3-3,10,3,0);
	box(MenuWin,0,0);
	wrefresh(MenuWin);

	/* 作業エリア */
	WorkWin=newwin(LINES-3-3,COLS-11,3,11);
	box(WorkWin,0,0);
	wrefresh(WorkWin);

	/* 下部デバッグ表示 */
	DebugWin=newwin(3,COLS,LINES-3,0);
	box(DebugWin,0,0);
	wrefresh(DebugWin);

	return(0);
}

void end(int sig)
{
	DB(fprintf(stderr,"end()\n"));

	wclear(BaseWin);
	wrefresh(BaseWin);
	endwin();
	SendAndRecv("end;",strlen("end;"));
	close(Soc);

	exit(0);
}

int InitSignal()
{
	signal(SIGINT,end);
	signal(SIGTERM,end);
	signal(SIGQUIT,end);
	signal(SIGHUP,end);

	return(0);
}

int DebugPrint(va_alist)
va_dcl
{
va_list	args;
char	*fmt;
char	buf[512];

	va_start(args);
	fmt=va_arg(args,char *);
	vsprintf(buf,fmt,args);
	va_end(args);

	if(DebugWin==NULL){	/* デバッグウインドウがまだできていない */
		fprintf(stderr,buf);
	}
	else{
		wmove(DebugWin,1,1);
		wclrtobot(DebugWin);
		box(DebugWin,0,0);
		wprintw(DebugWin,buf);
		wrefresh(DebugWin);
	}

	return(0);
}

int CenterPrint(WINDOW *win,int y,int cols,char *str)
{
int	x,len;

	len=strlen(str);
	x=cols/2-len/2;
	mvwprintw(win,y,x,str);

	return(0);
}

int MenuCursor(int pos,char *mark)
{
	mvwprintw(MenuWin,pos*2+2,2,mark);
	wmove(MenuWin,pos*2+2,2);
	wrefresh(MenuWin);

	return(0);
}

int MainMenu()
{
int	c,menu_pos;

	box(MenuWin,0,0);
	mvwprintw(MenuWin,0,1,"メニュー");
	mvwprintw(MenuWin,2,4,"登録");
	mvwprintw(MenuWin,4,4,"検索");
	mvwprintw(MenuWin,6,4,"削除");
	mvwprintw(MenuWin,8,4,"更新");
	mvwprintw(MenuWin,10,4,"終了");
	wrefresh(MenuWin);

	keypad(MenuWin,TRUE);

	menu_pos=0;
	MenuCursor(menu_pos,">");
	while(1){
		c=wgetch(MenuWin);
		DebugPrint("Key=%04o",c);
		switch(c){
			case	KEY_DOWN:
				MenuCursor(menu_pos," ");
				menu_pos++;
				if(menu_pos==5){
					menu_pos=0;
				}
				MenuCursor(menu_pos,">");
				break;
			case	KEY_UP:
				MenuCursor(menu_pos," ");
				menu_pos--;
				if(menu_pos==-1){
					menu_pos=4;
				}
				MenuCursor(menu_pos,">");
				break;
			case	KEY_ENTER:
			case	012:
				MenuCursor(menu_pos,"*");
				DoMenu(menu_pos);
				MenuCursor(menu_pos,">");
				break;
			default:
				break;
		}
	}

	return(0);
}

int DoMenu(int menu)
{
	switch(menu){
		case	0:	/* 登録 */
			InsertMenu();
			break;
		case	1:	/* 検索 */
			SelectMenu();
			break;
		case	2:	/* 削除 */
			DeleteMenu();
			break;
		case	3:	/* 更新 */
			UpdateMenu();
			break;
		case	4:	/* 終了 */
			if(CheckDialog("終了が選択されました。")==1){
				end(0);
			}
			break;
		default:
			break;
	}

	return(0);
}

int CheckDialog(char *str)
{
WINDOW	*check_win_waku;
WINDOW	*check_win;
int	c,len,lines;

	len=strlen(str);
	lines=len/(COLS-9)+6;
	check_win_waku=newwin(lines,COLS-17,(LINES-lines)/2,14);
	box(check_win_waku,0,0);
	CenterPrint(check_win_waku,0,COLS-17,"確認");
	wrefresh(check_win_waku);
	check_win=newwin(lines-2,COLS-19,(LINES-lines)/2+1,15);
	mvwprintw(check_win,1,0,str);
	noecho();
	keypad(check_win,TRUE);
	CenterPrint(check_win,lines-3,COLS-19,"実行しますか？ [Y]es / [N]o ");
	wrefresh(check_win);

	while(1){
		c=wgetch(MenuWin);
		c=toupper(c);
		if(c=='Y'||c=='N'){
			break;
		}
	}

	delwin(check_win);
	delwin(check_win_waku);
	touchwin(BaseWin);wrefresh(BaseWin);
	touchwin(TitleWin);wrefresh(TitleWin);
	touchwin(MenuWin);wrefresh(MenuWin);
	touchwin(WorkWin);wrefresh(WorkWin);
	touchwin(DebugWin);wrefresh(DebugWin);

	if(c=='Y'){
		return(1);
	}
	else{
		return(0);
	}
}

int InfoDialog(char *str)
{
WINDOW	*info_win_waku;
WINDOW	*info_win;
int	len,lines;

	len=strlen(str);
	lines=len/(COLS-9)+6;
	info_win_waku=newwin(lines,COLS-17,(LINES-lines)/2,14);
	box(info_win_waku,0,0);
	CenterPrint(info_win_waku,0,COLS-17,"情報");
	wrefresh(info_win_waku);
	info_win=newwin(lines-2,COLS-19,(LINES-lines)/2+1,15);
	mvwprintw(info_win,1,0,str);
	noecho();
	keypad(info_win,TRUE);
	CenterPrint(info_win,lines-3,COLS-19,"何かキーを押してください");
	wrefresh(info_win);

	wgetch(MenuWin);

	delwin(info_win);
	delwin(info_win_waku);
	touchwin(BaseWin);wrefresh(BaseWin);
	touchwin(TitleWin);wrefresh(TitleWin);
	touchwin(MenuWin);wrefresh(MenuWin);
	touchwin(WorkWin);wrefresh(WorkWin);
	touchwin(DebugWin);wrefresh(DebugWin);

	return(1);
}

int InsertMenu()
{
int	menu_pos,i,len;
char	buf[512];
char	str[7][512];

	wclear(WorkWin);
	box(WorkWin,0,0);
	CenterPrint(WorkWin,0,COLS-11,"登録");
	mvwprintw(WorkWin,2,2, "        氏名");
	mvwprintw(WorkWin,4,2, "    カナ氏名");
	mvwprintw(WorkWin,6,2, "    郵便番号");
	mvwprintw(WorkWin,8,2, "        住所");
	mvwprintw(WorkWin,10,2,"    電話番号");
	mvwprintw(WorkWin,12,2,"携帯電話番号");
	mvwprintw(WorkWin,14,2,"[S]ave,[E]nd");
	wrefresh(WorkWin);

	keypad(WorkWin,TRUE);
	echo();
	for(i=0;i<7;i++){
		strcpy(str[i],"");
	}

	menu_pos=0;
	while(1){
		len=strlen(str[menu_pos]);
		memset(buf,' ',len);
		buf[len]='\0';
		mvwprintw(WorkWin,menu_pos*2+2,16,buf);wrefresh(WorkWin);
		mvwgetstr(WorkWin,menu_pos*2+2,16,buf);
		DebugPrint("%s",buf);
		if(strlen(buf)>0){
			strcpy(str[menu_pos],buf);
		}
		mvwprintw(WorkWin,menu_pos*2+2,16,str[menu_pos]);wrefresh(WorkWin);
		if(menu_pos==6){	/* save , end */
			if(toupper(str[menu_pos][0])=='S'){
				DoInsert(str);
				break;
			}
			else if(toupper(str[menu_pos][0])=='E'){
				break;
			}
		}
		menu_pos++;
		if(menu_pos==7){
			menu_pos=0;
		}
	}

	noecho();
	wclear(WorkWin);
	box(WorkWin,0,0);
	wrefresh(WorkWin);

	return(0);
}

int DoInsert(char str[7][512])
{
char	buf[1024];

	sprintf(buf,"insert into address (\"氏名\",\"カナ氏名\",\"郵便番号\",\"住所\",\"電話番号\",\"携帯電話番号\") values (\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\");",
		str[0],str[1],str[2],str[3],str[4],str[5]);
	if(CheckDialog(buf)==1){
		SendAndRecv(buf,strlen(buf));
	}

	return(0);
}

int SelectMenu()
{
int	menu_pos,i,len;
char	buf[512];
char	str[8][512];

	wclear(WorkWin);
	box(WorkWin,0,0);
	CenterPrint(WorkWin,0,COLS-11,"検索");
	mvwprintw(WorkWin,2,2, "          氏名");
	mvwprintw(WorkWin,4,2, "      カナ氏名");
	mvwprintw(WorkWin,6,2, "      郵便番号");
	mvwprintw(WorkWin,8,2, "          住所");
	mvwprintw(WorkWin,10,2,"      電話番号");
	mvwprintw(WorkWin,12,2,"  携帯電話番号");
	mvwprintw(WorkWin,14,2,"出力先 [M/F/P]");
	mvwprintw(WorkWin,16,2,"[S]elect,[E]nd");
	wrefresh(WorkWin);

	keypad(WorkWin,TRUE);
	echo();
	for(i=0;i<8;i++){
		strcpy(str[i],"");
	}

	menu_pos=0;
	while(1){
		len=strlen(str[menu_pos]);
		memset(buf,' ',len);
		buf[len]='\0';
		mvwprintw(WorkWin,menu_pos*2+2,18,buf);wrefresh(WorkWin);
		mvwgetstr(WorkWin,menu_pos*2+2,18,buf);
		DebugPrint("%s",buf);
		if(strlen(buf)>0){
			strcpy(str[menu_pos],buf);
		}
		mvwprintw(WorkWin,menu_pos*2+2,18,str[menu_pos]);wrefresh(WorkWin);
		if(menu_pos==7){	/* select , end */
			if(toupper(str[menu_pos][0])=='S'){
				DoSelect(str);
				break;
			}
			else if(toupper(str[menu_pos][0])=='E'){
				break;
			}
		}
		menu_pos++;
		if(menu_pos==8){
			menu_pos=0;
		}
	}

	noecho();
	wclear(WorkWin);
	box(WorkWin,0,0);
	wrefresh(WorkWin);

	return(0);
}

int DoSelect(char str[8][512])
{
char	*column[6]={"氏名","カナ氏名","郵便番号","住所","電話番号","携帯電話番号"};
int	column_no,i;
char	buf[1024],cmd[1024];

	column_no=-1;
	for(i=0;i<6;i++){
		if(strlen(str[i])>0){
			column_no=i;
			break;
		}
	}
	if(column_no==-1){
		sprintf(cmd,"select * from address;");
	}
	else{
		sprintf(cmd,"select * from address where \"%s\" = \"%s\";",
			column[column_no],str[column_no]);
	}

	if(toupper(str[6][0])=='F'){
		sprintf(buf,"%s (ファイル出力)",cmd);
	}
	else if(toupper(str[6][0])=='P'){
		sprintf(buf,"%s (プリンター出力)",cmd);
	}
	else{
		sprintf(buf,"%s (画面出力)",cmd);
	}

	if(CheckDialog(buf)==1){
		if(toupper(str[6][0])=='F'){
			SelectToFile(cmd);
		}
		else if(toupper(str[6][0])=='P'){
			SelectToPrinter(cmd);
		}
		else{
			SelectToMonitor(cmd);
		}
	}

	return(0);
}

int SelectToMonitor(char *cmd)
{
char	*GetCurrentLine();
char	buf[1024];
char	*ptr;
int	line;
WINDOW	*result_win;
WINDOW	*result_win_waku;

	result_win_waku=newwin(LINES-5,COLS-2,1,1);
	box(result_win_waku,0,0);
	CenterPrint(result_win_waku,0,COLS-2,"検索結果");
	wrefresh(result_win_waku);
	result_win=newwin(LINES-7,COLS-4,2,2);

	line=0;
	SendAndRecv(cmd,strlen(cmd));
	ptr=GetCurrentLine();
	mvwprintw(result_win,line,0,ptr);line++;
	if(strstr(ptr,"Error:")==NULL){
		while(1){
			RecvData(Soc);
			ptr=GetCurrentLine();
			mvwprintw(result_win,line,0,ptr);line++;
			if(strstr(ptr,"selected.")!=NULL){
				break;
			}
			if(line==LINES-8){	/* 表示しきれない場合 */
				CenterPrint(result_win,LINES-8,COLS-4,"---- Hit Return to Next-Page ----");
				wgetstr(result_win,buf);
				wclear(result_win);
				line=0;
			}
		}
	}
	wrefresh(result_win);

	CenterPrint(result_win,LINES-8,COLS-4,"---- Hit Return to End ----");
	wgetstr(result_win,buf);

	delwin(result_win);
	delwin(result_win_waku);
	touchwin(BaseWin);wrefresh(BaseWin);
	touchwin(TitleWin);wrefresh(TitleWin);
	touchwin(MenuWin);wrefresh(MenuWin);
	touchwin(WorkWin);wrefresh(WorkWin);
	touchwin(DebugWin);wrefresh(DebugWin);

	return(0);
}

#define	RESULT_FILE	"select.csv"

int SelectToFile(char *cmd)
{
char	*GetCurrentLine();
char	*ptr;
int	line;
FILE	*fp;

	if((fp=fopen(RESULT_FILE,"w"))==NULL){
		InfoDialog("結果ファイルが書き込めません。");
		return(-1);
	}
		
	line=0;
	SendAndRecv(cmd,strlen(cmd));
	ptr=GetCurrentLine();
	fprintf(fp,"%s\n",ptr);line++;
	if(strstr(ptr,"Error:")==NULL){
		while(1){
			RecvData(Soc);
			ptr=GetCurrentLine();
			fprintf(fp,"%s\n",ptr);line++;
			if(strstr(ptr,"selected.")!=NULL){
				break;
			}
		}
	}

	fclose(fp);

	InfoDialog("結果ファイルに書き込みました。");

	return(0);
}

#define	TO_PRINTER	"lpr -Plp9n"

int SelectToPrinter(char *cmd)
{
char	*GetCurrentLine();
char	*ptr;
int	line;
FILE	*fp;

	if((fp=popen(TO_PRINTER,"w"))==NULL){
		InfoDialog("プリンターに出力できません。");
		return(-1);
	}
		
	line=0;
	SendAndRecv(cmd,strlen(cmd));
	ptr=GetCurrentLine();
	fprintf(fp,"%s\n",ptr);line++;
	if(strstr(ptr,"Error:")==NULL){
		while(1){
			RecvData(Soc);
			ptr=GetCurrentLine();
			fprintf(fp,"%s\n",ptr);line++;
			if(strstr(ptr,"selected.")!=NULL){
				break;
			}
		}
	}

	pclose(fp);

	InfoDialog("プリンターに出力しました。");

	return(0);
}

int DeleteMenu()
{
int	menu_pos,i,len;
char	buf[512];
char	str[7][512];

	wclear(WorkWin);
	box(WorkWin,0,0);
	CenterPrint(WorkWin,0,COLS-11,"削除");
	mvwprintw(WorkWin,2,2, "          氏名");
	mvwprintw(WorkWin,4,2, "      カナ氏名");
	mvwprintw(WorkWin,6,2, "      郵便番号");
	mvwprintw(WorkWin,8,2, "          住所");
	mvwprintw(WorkWin,10,2,"      電話番号");
	mvwprintw(WorkWin,12,2,"  携帯電話番号");
	mvwprintw(WorkWin,14,2,"[D]elete,[E]nd");
	wrefresh(WorkWin);

	keypad(WorkWin,TRUE);
	echo();
	for(i=0;i<7;i++){
		strcpy(str[i],"");
	}

	menu_pos=0;
	while(1){
		len=strlen(str[menu_pos]);
		memset(buf,' ',len);
		buf[len]='\0';
		mvwprintw(WorkWin,menu_pos*2+2,18,buf);wrefresh(WorkWin);
		mvwgetstr(WorkWin,menu_pos*2+2,18,buf);
		DebugPrint("%s",buf);
		if(strlen(buf)>0){
			strcpy(str[menu_pos],buf);
		}
		mvwprintw(WorkWin,menu_pos*2+2,18,str[menu_pos]);wrefresh(WorkWin);
		if(menu_pos==6){	/* delete , end */
			if(toupper(str[menu_pos][0])=='D'){
				DoDelete(str);
				break;
			}
			else if(toupper(str[menu_pos][0])=='E'){
				break;
			}
		}
		menu_pos++;
		if(menu_pos==7){
			menu_pos=0;
		}
	}

	noecho();
	wclear(WorkWin);
	box(WorkWin,0,0);
	wrefresh(WorkWin);

	return(0);
}

int DoDelete(char str[7][512])
{
char	buf[1024];
char	*column[6]={"氏名","カナ氏名","郵便番号","住所","電話番号","携帯電話番号"};
int	column_no,i;

	column_no=-1;
	for(i=0;i<6;i++){
		if(strlen(str[i])>0){
			column_no=i;
			break;
		}
	}
	if(column_no==-1){
		sprintf(buf,"delete from address;");
	}
	else{
		sprintf(buf,"delete from address where \"%s\" = \"%s\";",
			column[column_no],str[column_no]);
	}

	if(CheckDialog(buf)==1){
		SendAndRecv(buf,strlen(buf));
	}

	return(0);
}

int UpdateMenu()
{
int	menu_pos,i,len;
char	buf[512];
char	str[15][512];

	wclear(WorkWin);
	box(WorkWin,0,0);
	CenterPrint(WorkWin,0,COLS-11,"更新");
	mvwprintw(WorkWin,1,1, "[対象]----");
	mvwprintw(WorkWin,2,2, "          氏名");
	mvwprintw(WorkWin,3,2, "      カナ氏名");
	mvwprintw(WorkWin,4,2, "      郵便番号");
	mvwprintw(WorkWin,5,2, "          住所");
	mvwprintw(WorkWin,6,2, "      電話番号");
	mvwprintw(WorkWin,7,2, "  携帯電話番号");
	mvwprintw(WorkWin,8,1, "[内容]----");
	mvwprintw(WorkWin,9,2, "          氏名");
	mvwprintw(WorkWin,10,2,"      カナ氏名");
	mvwprintw(WorkWin,11,2,"      郵便番号");
	mvwprintw(WorkWin,12,2,"          住所");
	mvwprintw(WorkWin,13,2,"      電話番号");
	mvwprintw(WorkWin,14,2,"  携帯電話番号");
	mvwprintw(WorkWin,15,2,"[U]pdate,[E]nd");
	wrefresh(WorkWin);

	keypad(WorkWin,TRUE);
	echo();
	for(i=0;i<14;i++){
		strcpy(str[i],"");
	}

	menu_pos=0;
	while(1){
		len=strlen(str[menu_pos]);
		memset(buf,' ',len);
		buf[len]='\0';
		mvwprintw(WorkWin,menu_pos+2,18,buf);wrefresh(WorkWin);
		mvwgetstr(WorkWin,menu_pos+2,18,buf);
		DebugPrint("%s",buf);
		if(strlen(buf)>0){
			strcpy(str[menu_pos],buf);
		}
		mvwprintw(WorkWin,menu_pos+2,18,str[menu_pos]);wrefresh(WorkWin);
		if(menu_pos==13){	/* update , end */
			if(toupper(str[menu_pos][0])=='U'){
				DoUpdate(str);
				break;
			}
			else if(toupper(str[menu_pos][0])=='E'){
				break;
			}
		}
		menu_pos++;
		if(menu_pos==6){
			menu_pos++;
		}
		if(menu_pos==14){
			menu_pos=0;
		}
	}

	noecho();
	wclear(WorkWin);
	box(WorkWin,0,0);
	wrefresh(WorkWin);

	return(0);
}

int DoUpdate(char str[14][512])
{
char	buf[1024],buf2[256];
char	*column[6]={"氏名","カナ氏名","郵便番号","住所","電話番号","携帯電話番号"};
int	column_no,i,first_flag;

	strcpy(buf,"update address set ");
	first_flag=1;
	for(i=7;i<13;i++){
		if(strlen(str[i])!=0){
			if(first_flag!=1){
				strcat(buf,",");
			}
			sprintf(buf2,"\"%s\" = \"%s\" ",column[i-7],str[i]);
			strcat(buf,buf2);
			first_flag=0;
		}
	}

	column_no=-1;
	for(i=0;i<6;i++){
		if(strlen(str[i])>0){
			column_no=i;
			break;
		}
	}
	if(column_no==-1){
		strcat(buf,";");
	}
	else{
		sprintf(buf2,"where \"%s\" = \"%s\";",
			column[column_no],str[column_no]);
		strcat(buf,buf2);
	}

	if(CheckDialog(buf)==1){
		SendAndRecv(buf,strlen(buf));
	}

	return(0);
}
