/*****************************************************************************/
/* Waku Edit SYSTEM for X68000 V1.00 Copyright (C) 1994 by AIG-SOFT */
/*****************************************************************************/

#include <iocslib.h>
#include "defines.h"
#include "abuff.h"
#include "amacro.h"
#include "akey.h"

/*****************************************************************************/
/* 内部変数 */

static	unsint	p,pe;		/* バッファー内ポインター */
static	unsint	MLEN;		/* 入力最大長さ */
static	unsint	pmode;		/* 動作モード */
static	unsint	fset;		/* Set(1)/Move(0) */
static	unsint	fover;		/* Overwrite(1)/Plus(0) */
static	unsint	foff;		/* Off(1)/On(0) */
static	int	stx,sty;	/* 表示開始座標 */
static	int	bx;		/* 前行ｘ座標 */
static	int	yy;		/* 行ｙ相対座標 */
static	unsint	cc;		/* 前回の方向 */
static	unsint	*buff;		/* 内部文字バッファー */
static	unchar	*bbuff;		/* 内部属性バッファー */
static	unchar	bit;		/* 枠属性 */
#define	CHMAX	16		/* 枠線は１セット１６キャラクター必要 */
static	unsint	ch[CHMAX];	/* 枠キャラクター番号（属性も兼ねる）*/
				/*	  2│	 ch番号
					0─┼─1 bit対応
					   │3		*/
static	void	(*disp)();	/* 内部状態表示関数 */
static	short	fdpset=0;	/* 内部状態表示関数設定フラグ */

/*****************************************************************************/
/* チェック */
/*****************************************************************************/

static short BLen(unsint c)
/* バッファー内バイト数＋この文字のサイズ */
{
unsint i;
short len;
	len=pe+1+(c>=256); /* 基本長さ＋この文字のサイズ */
	for (i=0;i<pe;i++) {
		if (buff[i]>=256) len++;
	}
	return(len);
}

static unsint CheckSize(unsint c)
/* 文字表示幅 */
/* 半角,1/2角,1/4角文字=1、全角文字=2 */
{
	return(2-((c<256 && c>=' ')||is24(c>>8)));
}

#define BCheckSize(pp)	CheckSize(buff[pp])

static short BKlen2(unsint ps)
/* ポイント以降バッファー内容の表示長さを計算する */
{
unsint i;
short len=0;
	for (i=ps;i<pe;i++) len+=BCheckSize(i);
	return(len);
}

/*****************************************************************************/
/* カーソル移動 */
/*****************************************************************************/

static unsint PRight()
/* point right */
{
	if (p==pe) return(1); /* 移動不可：ポイントが行末にある */
	p++;
	return(0);
}

static unsint PLeft()
/* point left */
{
	if (p==0) return(1); /* 移動不可：ポイントが行頭にある */
	p--;
	return(0);
}

static void CsrRight(unsint pp)
/* pp=移動前のポイント */
{
unsint l;
	l=BCheckSize(pp);
	curright();
	if (l==2) curright();
}

static void CsrLeft(unsint pp)
/* pp=移動後のポイント */
{
	curleft();
	if (BCheckSize(pp)==2) curleft();
}

/*****************************************************************************/
/* 初期化 */
/*****************************************************************************/

static unsint OpenBuffer(unchar **line0)
/* 先行入力文字列を内部バッファーに展開する */
/* 多行展開のため、改行文字でも終了する */
/* klen()文字数を返す */
{
unsint c,len,pe; /* ローカルpeを使う */
unchar *line;
short i;
	line=*line0;
	len=pe=0;
	while (c=*line++,c!=0 && c!='\n') {
		if (chkknj1(c)) c=(c<<8)|*line++;
		len+=(1+c>=256);
		if (len>MLEN) {
			while (c!=0 && c!='\n') c=*line++;
			break;
		}
		/* 枠線キャラクターの時はそれに対応した属性を持つ */
		/* キャラクターから判断するので若干の誤差が出るがしかたない */
		for (i=CHMAX-1;i>=0 && c!=ch[i];i--);
		bbuff[pe]=(i<0? 0:i);
		buff[pe++]=c;
	}
	if (c==0) line--; /* 0x00の位置 */
	*line0=line;
	return(pe);
}

static unsint CloseBuffer(unchar **line0)
/* 内部バッファーを文字列にする */
{
unsint c,i=0,j=0;
unchar *line;
	line=*line0;
	while (i<pe) {
		c=buff[i++];
		if (c>=256) line[j++]=(c>>8);
		line[j++]=(unchar)(c);
	}
	*line0=&line[j];
	return(j); /* 文字数を返す */
}

static void disp_mode()
/* 内部状態表示 */
{
int x,y;
	if (fdpset) {
		cursor(&x,&y);
		disp(fset,fover,foff,pmode,ch);
		B_LOCATE(x,y);
	}
}

static void Init(unsint mode)
{
	foff=fover=fset=cc=0;
	if (mode) disp_mode();
}

static unchar pkeys[712];	/* 全再定義可能キー待避エリア */

static void KeySet()
/* 再定義可能キー待避＆セット */
{
	/* 全キー定義内容待避 */
	FNCKEYGT(0,pkeys);
	/* キー定義 */
	keysetc(K_INS,  0x09);	/* INS   */
	keysetc(K_HOME, 0x16);	/* HOME  */
	keysetc(K_RIGHT,0x1c);	/* RIGHT */
	keysetc(K_LEFT, 0x1d); 	/* LEFT  */
	keysetc(K_UP,   0x1e);	/* UP    */
	keysetc(K_DOWN, 0x1f);	/* DOWN  */
}

#define	KeyReset()	FNCKEYST(0,pkeys)	/* 全再定義可能キー復帰 */

/*****************************************************************************/
/* 表示 */
/*****************************************************************************/

static void LinePrint(unsint ps,short olen)
/* ポイント以降表示 */
{
short i;
	/* とりあえず表示できる分表示する */
	for (i=ps;i<pe;i++) {
		B_PUTC(buff[i]);
		olen-=BCheckSize(i); /* 前表示した分と今表示した分の差 */
	}
	/* 前の表示が今表示したものより長かった時、長い部分＝上書きされない部分を消す */
	if (olen>0) { /* olen<0なら消さない */
	    unchar line[96*2+1];
	    int x,y;
		string(line,' ',(int)olen);
		cursor(&x,&y);
		B_PRINT(line);
		B_LOCATE(x,y);
	}
	/* カーソルをポイントに戻す */
	for (i=pe-1;i>=p;i--) CsrLeft(i);
}

/*****************************************************************************/
/* ストア */
/*****************************************************************************/

static unsint WakuChar(unsint c)
/* 枠属性設定 */
/* return : 1=枠描画可能 , 0=移動のみ */
{
short i;
unsint ret=0;
	if (!fset) return(0);
	
	/* 下の枠キャラクターから属性を得る */
	if (fover||p==pe) bit=0;
	else bit=bbuff[p]; /* p<pe : 上書き */
	switch (c) { /* 今回の移動に伴う枠設定 */
	    default:
		return(0); /* 移動以外のキーは無効 */
	    case 0x1d:
		if (cc==0x1c) goto STP; /* 前回と正反対の時は方向変換のみ */
		if (foff) bit&=~BIT0; else bit|=BIT0;
		break;
	    case 0x1c:
		if (cc==0x1d) goto STP;
		if (foff) bit&=~BIT1; else bit|=BIT1;
		break;
	    case 0x1f:
		if (cc==0x1e) goto STP;
		if (foff) bit&=~BIT3; else bit|=BIT3;
		break;
	    case 0x1e:
		if (cc==0x1f) goto STP;
		if (foff) bit&=~BIT2; else bit|=BIT2;
		break;
	}
	switch (cc) { /* 前回の移動に伴う枠設定 */
	    case 0x1d:
		if (foff) bit&=~BIT1; else bit|=BIT1;
		break;
	    case 0x1c:
		if (foff) bit&=~BIT0; else bit|=BIT0;
		break;
	    case 0x1f:
		if (foff) bit&=~BIT2; else bit|=BIT2;
		break;
	    case 0x1e:
		if (foff) bit&=~BIT3; else bit|=BIT3;
		break;
	}
	ret=1;
    STP:;
	cc=c;
	return(ret);
}

static unsint CPrint(unsint c)
/* １文字表示 */
{
int x,y;
	buff[p]=c;
	bbuff[p]=bit;
	cursor(&x,&y);
	B_PUTC(c);
	B_LOCATE(x,y);
	return(0);
}

static unsint WriteWaku()
/* 枠書き込み */
/* return : 0=ok , 2=line over */
{
unsint cb,c;
short len,i;
	/* １文字書き込み */
	c=ch[bit];
	if (p==pe) { /* 新規書き込み */
		if (BLen(c)>MLEN) return(2); /*１行が一杯になる:書き込まない*/
		pe++; /* エンドポインターも進める */
		return(CPrint(c));
	}
	/* 前の文字と今の文字の表示長さの差を求める */
	cb=buff[p]; /* 書込む前の文字 */
	len=CheckSize(cb)-CheckSize(c);
	if (len==0) return(CPrint(c)); /* 差がない時はそのまま */
	
	if (len>=0) { /* 表示長さが短くなる時 */
		/* ポイント以降再表示のみ */
		CPrint(c);
		LinePrint(p,BKlen2(p)+len); /*+lenが重要(後の残り文字を消す)*/
		return(0);
	}
	
	/* 表示長さが長くなる時 */
	if (p+1==pe) { /* となりが行末の時 */
		if (BLen(c)-1-(cb>=256)>MLEN) return(2); /* １行が一杯になる */
		return(CPrint(c));
	}
	/* 行真ん中での上書き */
	/* となりの１文字もつぶして入れる */
	/* 今は文字の長さが１／２しかないのでこれでいい */
	len=BKlen2(p);
	for (i=p+2;i<=pe;i++) { /* Del */
		buff[i-1]=buff[i];
		bbuff[i-1]=bbuff[i];
	}
	pe--;
	CPrint(c);
	LinePrint(p,len);
	return(0);
}

/*****************************************************************************/
/* メイン */
/*****************************************************************************/

SetWKDisp(void (*func)())
/* 内部状態表示関数を設定する */
{
	disp=func;
	fdpset=1;
}

GetWKStatus(int *cp,int *cy,int *xx)
/* waku_edit()の内部情報を取得する */
/* 継続処理のために利用する（外部インターフェース）*/
{
	*cp=(int)p;
	*cy=(int)yy;
	*xx=bx-stx;
}

SetWKStatus(int cp,int cy,int xx)
/* waku_edit()の内部情報を与える */
/* 継続処理のために利用する（外部インターフェース）*/
{
	p=cp;
	yy=cy;
	bx=xx+stx;
}

/*****************************************************************************/

int waku_edit(lines,wx,wy,mode,waku,plines,kexit,getchs)
unchar *lines,*waku,*plines;
int wx,wy,mode,kexit,(*getchs)();
/*	lines : 入力バッファー
		\n毎に行分割する。
	plines: 先行入力文字列
	   	もし、文字列がwxより長かった場合はwx以下になる。
      mode
	bit 1 : SetWKStatus()の内部情報による処理の継続 0=off , 1=on
	bit 3 : カーソル上下のロール 0=STOP , 1=ROLL
	bit 4 : カーソル左右のロール 0=STOP , 1=ROLL
	常時全スクリーン、上書きモード
*/
{
unsint *buff0;
unchar *bbuff0;
unsint c,pp,fs;
int i,xx;
	/* 内部バッファー＋内部属性バッファー確保（全行分）*/
	/* +1:pe , +1:EOS */
	bbuff0=newmalloc(wx*wy+(1+wx+1)*sizeof(unsint)*wy);
	if (bbuff0==NULL) return(-1); /* error */
	buff0=(unsint *)(bbuff0+wx*wy);
	
	/* 初期設定 */
	KeySet();
	cursor(&stx,&sty);
	if (wx==0) stx=0; /* 幅指定なし */
	pmode=mode;
	MLEN=wx;
	/* 枠設定 */
	for (i=0;i<CHMAX;i++) {
		c=*waku++;
		if (c==0) {free(bbuff0); return(-2);} /* 枠線が足りない */
		if (chkknj1(c)) c=(c<<8)|*waku++;
		ch[i]=c;
	} /* 枠は１セット１６キャラクター必要 */
	Init(1);
	
	/* 全行pe初期化 */
	buff=buff0;
	for (i=0;i<wy;i++) {
		*buff=0;
		buff+=(wx+2); /* next line */
	}
	
	/* 先行入力：入力文字制限処理なし */
	i=0;
	buff=buff0;
	bbuff=bbuff0;
	while (*plines!=0) {
		*buff++; /* buff[0]=peだから */
		pe=OpenBuffer(&plines);
		*(buff-1)=pe; /* 行毎のpeを記録 */
		B_LOCATE(stx,sty+i);
		if (pe>0) LinePrint(0,0); /* 行表示 */
		buff+=(wx+1); /* next line */
		bbuff+=wx;
		if (++i>=wy) break; /* 行が多すぎるときはカットする */
	}
	
	/* カーソルをホームに戻す */
	if (!(pmode&BIT1)) bx=yy=p=0;
	buff=buff0+(wx+2)*yy;
	bbuff=bbuff0+wx*yy;
	
	do {
		/* ポイント移動 */
		pe=*buff++;
		xx=stx;
		if (p>0) {
			i=0;
			while (i<=MLEN && xx<bx) {
				if (i>=pe) {buff[i]=' '; bbuff[i]=0;}
				xx+=CheckSize(buff[i++]);
			}
			if (xx>bx) xx-=CheckSize(buff[--i]);
			if (i>=pe) *(buff-1)=pe=i;
			p=i;
		}
		B_LOCATE(xx,sty+yy);
		
		/* 行処理 */
		while (c=getchs(0,kexit),
		    strchr("\x1b\x03\x16\x17",c)==NULL && c!=kexit) {
		    
		    /* 枠線属性決定 */
		    fs=WakuChar(c);
		    if (fs) { /* 枠線描画を伴う時 */
			if (WriteWaku()) { /* 書き込めない */
				beep();
				continue;
			}
		    }
		    if (c==0x1e||c==0x1f) break; /* カーソル上下 */
		    
		    /* キー処理 */
		    switch (c) {
			/*** 制御系コマンド ***/
			case ' ': /* SPACE : Set/Move */
				fset^=1;
				cc=0;
				disp_mode();
				continue;
			case 0x08: /* BS : Off/On */
				foff^=1;
				disp_mode();
				continue;
			case 0x09: /* TAB/INS : Overwrite/Plus */
				fover^=1;
				disp_mode();
				continue;
			
			/*** 移動系コマンド ***/
			case 0x1c: /* cursor right */
				pp=p;
				if (!PRight()) CsrRight(pp);
				else { /* p=pe */
					if (BLen(' ')<=MLEN) {
						B_PUTC(' ');
						bbuff[p]=0;
						buff[p++]=' ';
						pe++;
						continue;
					}
					if (pmode&BIT4) {c=0x0d; goto LN;}
				}
				continue;
			
			case 0x1d: /* cursor left */
				if (fs && ch[bit]>=256) {
					/* 全角枠線の時はx=2分戻そうとする */
					if (!PLeft()) CsrLeft(p);
					else continue; /* その行内で戻れない時はやめる */
					/* 戻した所が全角ならすでに２戻っているので終わり */
					if (buff[p]>=256) continue;
				}
				if (!PLeft()) CsrLeft(p);
				else {
					if (pmode&BIT4) {c=0x0a; goto LN;}
				}
				continue;
		    }
		} /* end of 行処理 */
	    LN:;
		*(buff-1)=pe;
		bx=pos();
		
		/* 行またぎ処理 */
		switch (c) {
			default:continue;
			
			case 0x1e:/* cursor up */
			case 0x0a:/* cursor up & cursor line end */
				if (yy==0) {
					if (pmode&BIT3) yy=wy-1;
					else {
						beep();
						kflush();
						break;
					}
				} else yy--;
				break;
			
			case 0x0d:/* [RET] */
				p=0;
			case 0x1f:/* cursor down */
				if (yy==wy-1) {
					if (pmode&BIT3) yy=0;
					else {beep(); kflush();}
				} else yy++;
				break;
			
			case 0x17:/* END */
				cc=0;
				yy=wy-1;
				break;
			
			case 0x16:/* HOME */
				cc=p=yy=0;
				break;
		}
		buff=buff0+(wx+2)*yy;
		bbuff=bbuff0+wx*yy;
	} while (c!=BREAK && c!=ESC && c!=kexit);
	
	/* 内部バッファーを文字列に換える */
	buff=buff0;
	bbuff=bbuff0;
	for (i=0;i<wy;i++) {
		pe=*buff++;
		CloseBuffer(&lines);
		*lines++='\n';
		buff+=(wx+1); /* next line */
		bbuff+=wx;
	}
	*(lines-1)=0;
	
	free(bbuff0);
	KeyReset();
	Init(0);
	return(c);
}

/*****************************************************************************/
#if 0
unchar *wk ="　━━━┃┛┗┻┃┓┏┳┃┫┣╋";
unchar *wk2=" ---|+++|+++|+++";

static void DispMode(int fset,int fover,int foff,int pmode,unsint ch[])
{
int i;
	B_LOCATE(0,11);
	/*       0         1         2         3         4      */
	/*       0123456789012345678901234567890123456789012345 */
	B_PRINT("[*][ON/OFF:---][Over/Plus:----][枠:");
	B_LOCATE( 1,11); B_PRINT(fset?  " ":"*");
	B_LOCATE(11,11); B_PRINT(foff?  "Off":"On ");
	B_LOCATE(26,11); B_PRINT(fover? "Over":"Plus");
	/* LEDも点滅させる */
	LEDMOD(0,foff);  /* かな */
	LEDMOD(4,fover); /* INS */
	LEDMOD(3,fset);  /* CAPS */
	/* 枠線種類 */
	B_LOCATE(35,11);
	for (i=0;i<CHMAX;i++) B_PUTC(ch[i]);
	B_PUTC(']');
}

main()
{
#define LWD	80	/* １行幅 */
#define LN	10	/* 行数 */
int ret,cp,cy,xx;
char line[(LWD+1)*LN]; /* +1 for EOS */
	cls();
	SetWKDisp(DispMode);
	B_LOCATE(0,LN);
	for (ret=0;ret<LWD;ret++) B_PUTC('-');
	B_LOCATE(0,0);
	ret=waku_edit(line,LWD,LN,0,wk2,"先行\n入力です\n\nOk?",0,getch3);
	GetWKStatus(&cp,&cy,&xx);
	B_LOCATE(0,11);
	printf("p=%d,yy=%d,bx=%d,ret=%02x\n%s\nHit Any Key...",cp,cy,xx,ret,line);
	getch();
	
	cls();
	B_LOCATE(0,LN);
	for (ret=0;ret<LWD;ret++) B_PUTC('-');
	B_LOCATE(0,0);
	SetWKStatus(cp,cy,xx);
	ret=waku_edit(line,LWD,LN,BIT1,wk,line,0,getch3);
	B_LOCATE(0,11);
	printf("ret=%02x\n%s\n",ret,line);
}
#endif
/*****************************************************************************/
