#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>
#include    <mos.h>
#include    "defs.h"
#include    "event.h"
#include    "graphic.h"
#include    "msg.h"
#include    "buff.h"
#include    "coldef.h"
#include    "wind.h"

#define	MAX_KEY_TBL	100

char	*strdup(char *str);
void	DEL_line(void);
void	INS_line(void);
void	KEY_undo(void);
void	KEY_bs(void);
void	KEY_cr(void);
void	KEY_del(void);
void	KEY_del_left(void);
void	KEY_del_right(void);
void	KEY_tab(void);
void	KEY_ins_sw(void);
void	KEY_left(void);
void	KEY_right(void);
void	KEY_up(void);
void	KEY_down(void);
void	KEY_line_btm(void);
void	KEY_line_top(void);
void	KEY_page_up(void);
void	KEY_page_down(void);
void	KEY_for_scrool(void);
void	KEY_bak_scrool(void);
void	KEY_mike_open(void);
void	KEY_john_open(void);
void	KEY_kent_open(void);
void	KEY_jack_open(void);
void	KEY_term_mike(void);
void	KEY_term_john(void);
void	KEY_term_kent(void);
void	KEY_act_cheng(void);
void	KEY_memo(void);
void	TANGO_set(void);
void	LIN_onoff(void);
void	KEY_scl_dsp(void);
void	KEY_word_left(void);
void	KEY_word_right(void);
void	KEY_scr_up(void);
void	KEY_scr_down(void);
void	KEY_null(void);
void	WIN_scrn_event(EVENT *ep,int x,int y,int sw);
void	KEY_cut_and_copy(void);
void	KEY_cut(void);
void	KEY_copy(void);

void	RSB_putc(int ch);
void	TERM_pause_abort(void);
int	kan_pos(char *p,int n);
int	iskan(char *str);
int	kbhit();
int	getkey(unsigned *ec);
void	WIN_col_buf(int no,int pos,int end);

extern int	TERM;
extern int	act_wind;
extern int	lin_new_flg;
extern WIND	win[MAX_WIND];

static struct {
    char    *func;
    void    (*proc)();
} key_func_tbl[]={
	{ "DEL_LINE",	DEL_line },
	{ "INS_LINE",	INS_line },
	{ "UNDO",	KEY_undo },
	{ "BS",		KEY_bs },
	{ "CR",		KEY_cr },
	{ "DEL",	KEY_del },
	{ "LEFT_DEL",	KEY_del_left },
	{ "RIGHT_DEL",	KEY_del_right },
	{ "TAB",	KEY_tab },
	{ "INS_SW",	KEY_ins_sw },
	{ "LEFT_CUR",	KEY_left },
	{ "RIGHT_CUR",	KEY_right },
	{ "LEFT_WORD",	KEY_word_left },
	{ "RIGHT_WORD",	KEY_word_right },
	{ "LINE_BTM",	KEY_line_btm },
	{ "LINE_TOP",	KEY_line_top },
	{ "UP_CUR",	KEY_up },
	{ "DOWN_CUR",	KEY_down },
	{ "UP_LINE",	KEY_scr_up },
	{ "DOWN_LINE",	KEY_scr_down },
	{ "UP_SCR",	KEY_page_up },
	{ "DOWN_SCR",	KEY_page_down },
	{ "FOR_SCR",	KEY_for_scrool },
	{ "BACK_SCR",	KEY_bak_scrool },
	{ "MIKE",	KEY_mike_open },
	{ "JOHN",	KEY_john_open },
	{ "KENT",	KEY_kent_open },
	{ "JACK",	KEY_jack_open },
	{ "MIKE_TERM",	KEY_term_mike },
	{ "JOHN_TERM",	KEY_term_john },
	{ "KENT_TERM",	KEY_term_kent },
	{ "ACT_CHENG",	KEY_act_cheng },
	{ "MEMO",	KEY_memo },
	{ "TANGO",	TANGO_set },
	{ "LINE_SW",	LIN_onoff },
	{ "SCL_SW",	KEY_scl_dsp },
	{ "CUT_COPY",	KEY_cut_and_copy },
	{ "CUT_KEY",	KEY_cut },
	{ "COPY_KEY",	KEY_copy },
	{ "NULL",	KEY_null },
	{ NULL,		KEY_null }
    };

static struct {
    int     code;
    void    (*proc)();
    char    *msg;
} edit_key_tbl[MAX_KEY_TBL],term_key_tbl[MAX_KEY_TBL];

static int	edit_key_max=0;
static int	term_key_max=0;

void	KEY_cut(void){}
void	KEY_copy(void){}
void	KEY_null(void){}		/* Nothing Func */

void	KEY_cut_and_copy(void)
{
    int     i,n,j,k,fg=0,rc=FALSE;
    int     ch;
    unsigned ec;
    int     old_x,old_y;
    int     old_ptr,old_pos;
    int     x1=0,y1=0,x2=0,y2=0;
    WIND    *wp;
    CHI_PTR *cp;
    LIN_PTR *lp;
    LIN_PTR *tp;
    static void (*move_proc[])()={
	KEY_left,KEY_right,KEY_word_left,KEY_word_right,
	KEY_line_btm,KEY_line_top,KEY_up,KEY_down,
	KEY_scr_up,KEY_scr_down,KEY_page_up,KEY_page_down
    };

    wp = &(win[act_wind]);
    cp = &(wp->child[wp->now]);
    if ( cp->now_ptr == ERR )
	return;

    lp = get_lin(cp->now_ptr);
    if ( cp->cur_x < strlen(lp->lin) )
	cp->cur_x = kan_pos(lp->lin,cp->cur_x);

    old_ptr = cp->now_ptr;
    old_pos = cp->lin_pos;
    old_x = cp->cur_x;
    old_y = cp->cur_y;

    MOS_disp(FALSE);
    DSP_string("CUT&COPY",
	296,wp->wind_y + 4,COL_RED,wp->color);
    WIN_dsp_buf(act_wind);

    for ( ; ; ) {
	if ( old_ptr == cp->now_ptr ) {
	    old_pos = cp->lin_pos;
	    if ( fg == 2 )
		WIN_dsp_buf(act_wind);
	    else if ( fg == 1 ) 
	        DSP_xline(x1,y1,x2,y2,7,4);
	    fg = 1;

	    if ( old_x < cp->cur_x ) {
		x1 = old_x * 8;
		x2 = cp->cur_x * 8 - 1;
	    } else {
		x1 = cp->cur_x * 8;
		x2 = old_x * 8 + 7;
	    }

	    y1 = wp->wind_y + 16 + cp->cur_y * 16;	    
	    y2 = y1 + 15;
	    DSP_xline(x1,y1,x2,y2,7,4);

	} else {
	    fg = 2;
	    if ( old_pos <= cp->lin_pos )
		WIN_col_buf(act_wind,old_pos,cp->lin_pos);
	    else
		WIN_col_buf(act_wind,cp->lin_pos,old_pos);
	}

	while ( kbhit() == 0 );
	ch = getkey(&ec);
	ec &= 0xFF14;
	
	for ( i = 0 ; i < edit_key_max ; i++ ) {
	    if ( edit_key_tbl[i].code == ec ) {
		if ( edit_key_tbl[i].proc == KEY_cut ) {
		    rc = TRUE;
		    goto CUT_TRY;
		} else if ( edit_key_tbl[i].proc == KEY_copy )
		    goto CUT_TRY;
		for ( n = 0 ; n < 12 ; n++ ) {
		    if ( edit_key_tbl[i].proc == move_proc[n] ) {
			(*edit_key_tbl[i].proc)();
			break;
		    }
		}
		break;
	    }
	}

	if ( ch == 0x1B )
	    goto ENDOF;
    }

CUT_TRY:
    if ( fg == 1 ) {
	if ( cp->cur_x > old_x ) {
	    i = old_x;
	    n = cp->cur_x - old_x;
	} else {
	    i = cp->cur_x;
	    n = old_x - cp->cur_x + 1;
	}
	MEMO_word_cut(act_wind,cp->lin_pos,i,n);

	if ( rc != FALSE && 
		(j = get_lin_pos(act_wind,cp->lin_pos)) != ERR ) {
	    lp = get_lin(j);
	    if ( strlen(lp->lin) > i ) {
		j = kan_pos(lp->lin,i);
		k = kan_pos(lp->lin,i + n);
		strcpy(&(lp->lin[j]),&(lp->lin[k]));
		cp->cur_x = j;
		cp->wrt_flg = TRUE;
	    }
	}


    } else if ( fg == 2 ) {
	if ( old_pos <= cp->lin_pos ) {
	    i = old_pos;
	    n = cp->lin_pos;
	} else {
	    i = cp->lin_pos;
	    n = old_pos;
	}
	MEMO_line_cut(act_wind,i,n);

	if ( rc != FALSE && 
		(j = get_lin_pos(act_wind,i)) != ERR ) {
	    cp->now_ptr = j;
	    cp->lin_pos = i;
	    while ( i++ <= n )
		DEL_line();
	    old_ptr = cp->now_ptr;
	    old_pos = cp->lin_pos;
	}
    }

ENDOF:
    cp->now_ptr = old_ptr;
    cp->lin_pos = old_pos;
    cp->cur_x = old_x;
    cp->cur_y = old_y;
    WIN_dsp_buf(act_wind);
    DSP_string("        ",
	296,wp->wind_y + 4,COL_RED,wp->color);
    MOS_disp(TRUE);
}
int	EDIT_KEY_chk(int code)
{
    int     i;

    for ( i = 0 ; i < edit_key_max ; i++ ) {
	if ( edit_key_tbl[i].code == code )
	    return i;
    }
    return ERR;
}
int	EDIT_KEY_exec(int code,int i)
{
    for ( ; i < edit_key_max ; i++ ) {
	if ( edit_key_tbl[i].code == code ) {
	    if ( edit_key_tbl[i].msg != NULL )
		MSG_wind(edit_key_tbl[i].msg);
	    else
		(*edit_key_tbl[i].proc)();
	    return TRUE;
	}
    }
    return FALSE;
}
int	TERM_KEY_exec(int code)
{
    int     i;

    for ( i = 0 ; i < term_key_max ; i++ ) {
	if ( term_key_tbl[i].code == code ) {
	    if ( term_key_tbl[i].msg != NULL )
		MSG_wind(term_key_tbl[i].msg);
	    else
		(*term_key_tbl[i].proc)();
	    return TRUE;
	}
    }
    return FALSE;
}
void	EDIT_KEY_set(int code,char *func)
{
    int     i;

    if ( *func == '_' ) {
	if ( edit_key_max < MAX_KEY_TBL ) {
	    edit_key_tbl[edit_key_max].code = code;
	    edit_key_tbl[edit_key_max].msg = strdup(func);
	    edit_key_max++;
	}
	return;
    }

    for ( i = 0 ; key_func_tbl[i].func != NULL ; i++ ) {
	if ( strcmp(key_func_tbl[i].func,func) == 0 ) {
	    if ( edit_key_max < MAX_KEY_TBL ) {
	        edit_key_tbl[edit_key_max].code = code;
	        edit_key_tbl[edit_key_max].proc = key_func_tbl[i].proc;
	        edit_key_tbl[edit_key_max].msg = NULL;
		edit_key_max++;
	    }
	    return;
	}
    }
}
void	TERM_KEY_set(int code,char *func)
{
    int     i;

    if ( *func == '_' ) {
	if ( term_key_max < MAX_KEY_TBL ) {
	    term_key_tbl[term_key_max].code = code;
	    term_key_tbl[term_key_max].msg = strdup(func);
	    term_key_max++;
	}
	return;
    }

    for ( i = 0 ; key_func_tbl[i].func != NULL ; i++ ) {
	if ( strcmp(key_func_tbl[i].func,func) == 0 ) {
	    if ( term_key_max < MAX_KEY_TBL ) {
	        term_key_tbl[term_key_max].code = code;
	        term_key_tbl[term_key_max].proc = key_func_tbl[i].proc;
	        term_key_tbl[term_key_max].msg = NULL;
		term_key_max++;
	    }
	    return;
	}
    }
}
int	KEY_func_exec(char *func)
{
    int     i;

    for ( i = 0 ; key_func_tbl[i].func != NULL ; i++ ) {
	if ( strcmp(key_func_tbl[i].func,func) == 0 ) {
	    (*key_func_tbl[i].proc)();
	    return FALSE;
	}
    }
    return ERR;
}

static char	*mos_mac[10]={ NULL,NULL,NULL,NULL,NULL,
			       NULL,NULL,NULL,NULL,NULL };

void	MAC_event(register EVENT *ep)
{
    switch(ep->now) {
    case EVT_CLIP_MOS:
	EVT_clip_on(ep);
	if ( *mos_mac[ep->no] == '_' )
	    ep->now = EVT_REP_MOS;
    case EVT_ON_MOS:
	DSP_mos(1);
	break;

    case EVT_SELECT_MOS:
	EVT_clip_off(ep);
	DSP_mos(0);
    case EVT_REP_MOS:
	MSG_wind(mos_mac[ep->no]);
	break;

    case EVT_DOLACK_MOS:
	ep->now = EVT_NON;
    case EVT_MOVE_MOS:
	EVT_clip_off(ep);
    case EVT_OFF_MOS:
	DSP_mos(0);
	break;
    }
}
int	MAC_init(char *argv[])
{
    int     i;
    char    tmp[16];

    DSP_box(0,464,560,479,COL_LINE,COL_WHIS);
    EVT_level_free(250);
    for ( i = 0 ; *argv != NULL && i < 10 ; i++ ) {
	if ( mos_mac[i] != NULL ) {
	    free(mos_mac[i]);
	    mos_mac[i] = NULL;
	}
	if ( *argv[0] != '\0' ) {
	    sprintf(tmp,"%-6.6s",*(argv++));
	    EVT_sw(i*56+2,466,tmp,COL_WHIS2,COL_WHIS,250,MAC_event,i);
	    mos_mac[i] = strdup(*(argv++));
	} else
	    argv += 2;
    }
    return i;
}

#define	MAX_HIS	8
#define	MAX_LIN	160

static struct _LB {
    int     len;
    int     pos;
    char    buf[MAX_LIN+4];
} lin_buf[MAX_HIS];

int	lin_his=0;

int	LIN_buf_pos(void)
{
    int     ofs;
    struct _LB *lp;

    lp = &(lin_buf[lin_his]);

    if      ( lp->pos >= 110 ) ofs = 90;
    else if ( lp->pos >= 70  ) ofs = 50;
    else 		       ofs = 0;
    ofs = kan_pos(lp->buf,ofs);

    return (lp->pos - ofs);
}
void	LIN_buf_dsp(void)
{
    int     ofs,n,y;
    struct _LB *lp;
    char    tmp[84];

    lp = &(lin_buf[lin_his]);

    if      ( lp->pos >= 110 ) ofs = 90;
    else if ( lp->pos >= 70  ) ofs = 50;
    else 		       ofs = 0;
    ofs = kan_pos(lp->buf,ofs);
    
    lp->buf[lp->len] = '\0';
    if ( (lp->len - ofs) >= 80 ) {
	n = kan_pos(lp->buf,ofs + 80) - ofs;
	strncpy(tmp,&(lp->buf[ofs]),n);
	tmp[n] = '\0';
    } else
        strcpy(tmp,&(lp->buf[ofs]));

    y = win[TERM].wind_y + win[TERM].size_y - 16;
    n = (lp->pos - ofs) * 8;

    cputstr(y*512,tmp,col_cnv[COL_BAK],col_cnv[COL_LINE]);
    DSP_xline(n,y+14,n+7,y+15,15,4);
}
void	LIN_buf_flush(void)
{
    int     i;
    struct _LB *lp;

    lp = &(lin_buf[lin_his]);

    TERM_pause_abort();
    for ( i = 0 ; i < lp->len ; i++ )
	RSB_putc(lp->buf[i]);

    if ( ++lin_his >= MAX_HIS )
	lin_his = 0;

    lp = &(lin_buf[lin_his]);
    lp->len = lp->pos = 0;
}
void	LIN_input(int ch)
{
    int     i,n;
    char    *p;
    struct _LB *lp;

    lp = &(lin_buf[lin_his]);
    lp->buf[lp->len] = '\0';

    if ( ch == '\x08' && lp->pos > 0 ) {
	lp->pos = kan_pos(lp->buf,lp->pos-1);
	p = &(lp->buf[lp->pos]);
	n = (iskan(p) ? 2:1);
	strcpy(p,p+n);
	lp->len -= n;

    } else if ( ch == 0x1B && lp->len > 0 ) {
	lp->pos = lp->len = 0;

    } else if ( ch == 0x7F ) {
	if ( lp->pos < lp->len ) {
	    p = &(lp->buf[lp->pos]);
	    n = (iskan(p) ? 2:1);
	    strcpy(p,p+n);
	    lp->len -= n;
	}

    } else if ( ch == 0x1C ) {
	if ( lp->pos < lp->len )
	    lp->pos += (iskan(&(lp->buf[lp->pos])) ? 2:1);

    } else if ( ch == 0x1D ) {
	if ( lp->pos > 0 )
	    lp->pos = kan_pos(lp->buf,lp->pos-1);

    } else if ( ch == 0x1E ) {
	if ( --lin_his < 0 )
	    lin_his = (MAX_HIS - 1);

    } else if ( ch == 0x1F ) {
	if ( ++lin_his >= MAX_HIS )
	    lin_his = 0;

    } else if ( ch < ' ' ) {
	LIN_buf_flush();
	RSB_putc(ch);

    } else if ( lp->len < MAX_LIN ) {
	if ( lp->pos < lp->len ) {
	    p = &(lp->buf[lp->len]);
	    for ( i = lp->len - lp->pos ; i > 0 ; i--,p-- )
		*p = *(p-1);
	}
	lp->buf[lp->pos++] = ch;
	lp->len++;
    }
    lin_new_flg = TRUE;
}
