/* Copyright (c) 2002
 *	Marko Boomstra (m.boomstra@chello.nl).  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <conf.h>
#include <arpa/telnet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <ncurses.h>
#include <panel.h>
#include "mudix.h"

//#define CSI_DEBUG

/*
 * Defines
 */
#define MAX_HISTORY	100

/*
 * Locals
 */
int    check_esc(char *pBuf, int *attrib);

char   history	[MAX_HISTORY][LINE_MAXIM];
char  *pHistory [MAX_HISTORY];
char **pCurHist	= pHistory;	
char **pGetHist	= pHistory;
bool   fCharacterMode = FALSE;


void init_history(void)
{
    int	index;

    for (index = 0; index < MAX_HISTORY; index++) 
    {
	pHistory[index] = &history[index][0];
	history[index][0] = '\0';
    }

    return;
}


void read_input(void)
{
    static int  input[LINE_MAXIM];
           int *in = input, i = 0, value = 0;

    while((*in = fgetc(stdin)) != EOF) {
#if KEY_DEBUG
        wprintw(wInput, "<I%d>", *in); 
#endif
    	in++;
    	if ((in-input) >= INPUT_MAX)
    	    break;
    }

#if KEY_DEBUG
    wprintw(wInput, "<%d-%d>", *in, in-input);
#endif

    if (in == input)
	return; 

    if (input[0] == ESC)
	while (i < (in-input))
	    value += input[i++]; 

#if KEY_DEBUG
    wprintw(wInput, "<V%d>", value); 
#endif

    switch (value) {
	case 0:
          {
	    int length = in-input;

    	    in = input;
	    while (length-- > 0) {
    	    	if (isprint(*in)) {
    	    	    if (pEndIn >= (inbuf+INPUT_MAX)) {
		    	beep();
		    	break;
    	    	    }

                    if (fCharacterMode) {
			char k[2];

			k[0] = (char)*in;
			k[1] = '\0';
			process_input(k, NULL, FALSE);
			break;
		    }

        	    pCursor++;
        	    if (fEchoIn) {
            	    	waddch(wInput, *in);
        	    }
        	    else {
            	    	waddch(wInput, '*');
        	    }
    	       	}

	    	switch (*in) {
		    case TAB:
			check_tab(inbuf, pEndIn);
			break;
		    case BS:
		    case BACKSPACE:
                	if (pCursor == pEndIn) {
                    	    if (pEndIn > inbuf) {
                        	pEndIn--;
                        	pCursor--;
                    	    }
                    	    else
                            	break;

                    	    if (pCursor >= (LINE_LENGTH + inbuf))
                            	print_cmd_line();
                    	    else
                        	backspace();
                	}
                	else {
                    	    char *pTemp;

                    	    if (pCursor > inbuf) {
                        	pCursor--;
                        	move_cursor(wInput, LEFT, 1);
                    	    }
                    	    else
                        	break;

                    	    pTemp = pCursor;
                    	    while (pTemp != pEndIn) {
                        	*pTemp = *(pTemp+1);
                        	pTemp++;
                    	    }
                    	    pEndIn--;

                    	    print_cmd_line();
                            if (pCursor < (LINE_LENGTH + inbuf))
                            	wmove(wInput, 0, pCursor-inbuf);
                	}
                	break;
                    case CR:
                    	*pEndIn++ = '\n';
                    	*pEndIn   = '\0';

                    	/* put command in history */
                    	if (pEndIn - inbuf >= 4 && fEchoIn) {
                    	    char *pTempHis = *pCurHist;
                    	    char *pTempBuf = inbuf;

                    	    while(*pTempBuf != '\n')
                            	*pTempHis++ = *pTempBuf++;
                    	    *pTempHis = '\0';

                    	    if (pCurHist == &pHistory[MAX_HISTORY-1])
                            	pCurHist = pGetHist = &pHistory[0];
                    	    else
                            	pGetHist = ++pCurHist;
                        }

                    	process_input(inbuf, NULL, FALSE);
                    	werase(wInput);
                    	pEndIn = inbuf;
                   	pCursor = inbuf;
                    	break;
            	    default:
                    	*pEndIn++ = *in;
                    	if (pCursor == pEndIn) {
                            if (pCursor >= (LINE_LENGTH + inbuf))
                            	print_cmd_line();
                    	}
                    	else {
                    	    char *pTemp;

                    	    pCursor--;
                    	    pTemp = pEndIn-1;
                    	    while (pTemp != pCursor) {
                            	*pTemp = *(pTemp-1);
                            	pTemp--;
                    	    }
                    	    *pCursor++ = *in;

                    	    print_cmd_line();
                            if (pCursor < (LINE_LENGTH + inbuf))
                            	wmove(wInput, 0, pCursor-inbuf);
                    	}
                    	break;
            	}
		in++;
	    }
	    break;
          }
        case ESC:
	    if (panel_hidden(pMsg) == PANEL_VISIBLE)
	    	hide_panel(pMsg);
	    else if (panel_hidden(pScroll) == PANEL_VISIBLE)
	    	hide_panel(pScroll);
	    else if (panel_hidden(pStatus) == PANEL_VISIBLE)
	    	hide_panel(pStatus);
	    break;
	case F1:
	case F2:
	case F3:
	case F4:
	case F5:
	case F6:
	case F7:
	case F8:
	case F9:
	case F10:
	case F11:
	case F12:
            process_alias(value);
            break;
	case HOME:
	    if (fKeypadWalk)
		process_input("u", NULL, TRUE);
	    else {
	        pCursor = inbuf;
	        print_cmd_line();
		wmove(wInput, 0, 0);
	    }
	    break;
	case END:
	    if (fKeypadWalk)
		process_input("d", NULL, TRUE);
	    else {
		pCursor = pEndIn;
	    	print_cmd_line();
	    }
	    break;
        case PGUP:
            show_scroll(UP, SCROLL_SIZE);
            break;
        case PGDWN:
            show_scroll(DOWN, SCROLL_SIZE);
            break;
	case L_ALT('1'):
	    show_triggers();
	    break;
	case L_ALT('2'):
	    show_aliases();
	    break;
	case L_ALT('3'):
	    show_paths();
	    break;
	case L_ALT('4'):
	    show_tabs();
	    break;
	case L_ALT('5'):
	    show_vars();
	    break;
	case L_ALT('m'):
            fCharacterMode ^= TRUE;
	    if (fCharacterMode) {
	    	do_status("Character mode turned ON.", 3);
		hide_panel(pBanner);
		hide_panel(pInput);
		wresize(wMain, LEN_ROW, LEN_COL);
	    }
	    else {
	    	do_status("Character mode turned OFF.", 3);
		show_panel(pBanner);
		show_panel(pInput);
		wresize(wMain, LEN_ROW-2, LEN_COL);
	    }
            break;
	case L_ALT('h'):
	    do_help();
	    break;
	case L_ALT('q'):
	    dialog("Are you sure you want to quit? (Y/N)");
	    settings->state = STATE_QUIT;
	    break;
	case L_ALT('r'):
	    fReconnect = TRUE;
	    do_status("Reconnect requested.", 3);
	    break;
	case L_ALT('t'):
	    dialog("Toggle which trigger? (#)");
	    settings->state = STATE_TTOGGLE;
	    break;
	case L_ALT('s'):
	    show_settings();
	    break;
	case L_ALT('c'):
	    dialog("What text should initiate the login procedure?");
	    settings->state = STATE_CREATE_1;
	    break;
	case L_ALT('i'):
            fEchoIn ^= TRUE;
	    if (fEchoIn)
	    	do_status("Input echo turned ON.", 3);
	    else
	 	do_status("Input echo turned OFF.", 3);
            break;
	case L_ALT('o'):
            fEchoOut ^= TRUE;
	    if (fEchoOut)
	    	do_status("Command echo turned ON.", 3);
	    else
	 	do_status("Command echo turned OFF.", 3);
            break;
	case L_ALT('p'):
	    fPrintTime ^= TRUE;
	    if (fPrintTime)
	    	do_status("Time displayed.", 3);
	    else
	 	do_status("Time display disabled.", 3);
	    break;
	case L_ALT('k'):
	    fKeypadWalk ^= TRUE;
	    if (fKeypadWalk)
	    	do_status("Keypad walking enabled.", 3);
	    else
	 	do_status("Keypad walking disabled.", 3);
	    break;
	case L_ALT('a'):
	    if (!fStatusReport) {
	    	fStatusReport = TRUE;
	    	do_status("Status reports enabled.", 3);
	    }
	    else {
	    	do_status("Status reports disabled.", 3);
	    	fStatusReport = FALSE;
	    }
	    break;
        case ARROW_U:
        case XARROW_U:
	    if (fKeypadWalk) {
		process_input("n", NULL, TRUE);
		break;
	    }

	    if (panel_hidden(pScroll) == PANEL_VISIBLE) {
		show_scroll(UP, 1);
		break;
	    }

	    if (pGetHist == &pHistory[0])
		pGetHist = &pHistory[MAX_HISTORY-1];
	    else 
		pGetHist--;

	    werase(wInput);
	    if (**pGetHist != '\0') {
		char *pTemp = inbuf;
		char *pHtmp = *pGetHist;

		while(*pHtmp != '\0')
		    *pTemp++ = *pHtmp++;
		*pTemp = '\0';

	        pEndIn = pCursor = pTemp;
               	if (pCursor >= (LINE_LENGTH + inbuf))
                    print_cmd_line();
	        else {
		    waddnstr(wInput, inbuf, pTemp - inbuf);
		}
	    }
	    else
		pEndIn = pCursor = inbuf;
            break;
        case ARROW_D:
        case XARROW_D:
	    if (fKeypadWalk) {
		process_input("s", NULL, TRUE);
		break;
	    }

	    if (panel_hidden(pScroll) == PANEL_VISIBLE) {
		show_scroll(DOWN, 1);
		break;
	    }

	    if (pGetHist == &pHistory[MAX_HISTORY-1])
		pGetHist = &pHistory[0];
	    else 
		pGetHist++;

	    werase(wInput);
	    if (**pGetHist != '\0') {
		char *pTemp = inbuf;
		char *pHtmp = *pGetHist;

		while(*pHtmp != '\0')
		    *pTemp++ = *pHtmp++;
		*pTemp = '\0';

	        pEndIn = pCursor = pTemp;
                if (pCursor >= (LINE_LENGTH + inbuf))
                    print_cmd_line();
	        else {
		    waddnstr(wInput, inbuf, pTemp - inbuf);
		}
	    }
	    else
		pEndIn = pCursor = inbuf;
            break;
        case ARROW_R:
        case XARROW_R:
	    if (fKeypadWalk) {
		process_input("e", NULL, TRUE);
		break;
	    }

            if (pCursor < pEndIn) {
            	pCursor++;

            	if (pCursor >= (LINE_LENGTH + inbuf))
                    print_cmd_line();
		else
                    move_cursor(wInput, RIGHT, 1);
            }
            break;
        case ARROW_L:
        case XARROW_L:
	    if (fKeypadWalk) {
		process_input("w", NULL, TRUE);
		break;
	    }

            if (pCursor > inbuf) {
                pCursor--;
            
	      	if (pCursor >= (LINE_LENGTH + inbuf))
                    print_cmd_line();
	     	else
                    move_cursor(wInput, LEFT, 1);
            }
            break;
	default:
	    break;
    }

    return;
}

void process_input(char *input, char *args, bool fReturn)
{
    char  cmd[MAX_STRING];

    while (*input != '\0') {
	input = parse_input(input, args, cmd, fReturn);

        if (cmd[0] == '#') {		/* it's a MUDix command */
	    do_command(&cmd[1]);	/* do not pass the '#' */
	    continue;
    	}
	
    	if (process_naming(cmd))
	    continue;

    	if (!settings->state) {
    	    if (fEchoOut) {
	        wattrset(wMain, COLOR_PAIR(COL_GREEN)|A_NORMAL);
	    	waddstr(wMain, cmd);
	    	put_in_scroll(cmd, NULL, COLOR_PAIR(COL_GREEN)|A_NORMAL);    
	    	wattrset(wMain, COLOR_PAIR(0)|A_NORMAL);
	    }   
       	    strcat(send_buffer, cmd);
    	}
    	else
	    set_settings(cmd);
    }

    return;
}

int fill_param(int *npar, char *pBuf, int *params)
{
    char  local[24];
    char *pLoc = local;
    
    if (*npar >= CSIPARAMS)
	return 0;

    while (pBuf < pOut) {
	if (*pBuf == ';' || (*pBuf != ' ' && !isdigit(*pBuf))) {
	    *pLoc = '\0';
	    params[(*npar)++] = local[0]? atoi(local): 0; 
#ifdef CSI_DEBUG
	{
	    FILE *fp = fopen("log", "a+");
	    fprintf(fp, "<%d-%s-%c>\n", *npar, local, *pBuf);
	    fclose(fp);
	}
#endif
	    return pLoc-local;
	}
	*pLoc++ = *pBuf++;
    }     
    return 0;
}

bool iscsiseq(char c)
{
    if ((c>='A' && c<='J') 
     || (c>='K' && c<='M')
     || (c>='c' && c<='h')
     || (c>='l' && c<='n')
     || (c>='q' && c<='s')
     ||  c=='P' || c=='X' || c=='a' || c=='u' || c=='`')
	return TRUE;

    return FALSE;
}

int check_esc(char *pBuf, int *attrib)
{
    char *pBegin = pBuf;
    int   params[CSIPARAMS], npar, i;

    if (*pBuf++ != ESC || pBuf >= pOut)		/* push character back */
	return 0;

    if (*pBuf++ != '[')				/* only accept "ESC [" */
	return 1;

    npar = 0;
    while (pBuf < pOut) {
	if (isdigit(*pBuf))
    	    pBuf += fill_param(&npar, pBuf, params);
	if (iscsiseq(*pBuf)) {
	    static int x_cur, y_cur;	/* cursor position for store/restore */
	           int x, y;
	    
            switch (*pBuf++) {
		case '@':
		    for (i=0; i<params[0]; i++)
			winsch(wMain, ' ');
		    break;
		case 'A':
		    move_cursor(wMain, UP, params[0]);
		    break;
		case 'B':
		    move_cursor(wMain, DOWN, params[0]);
		    break;
		case 'C':
		    move_cursor(wMain, RIGHT, params[0]);
		    break;
		case 'D':
		    move_cursor(wMain, LEFT, params[0]);
		    break;
		case 'E':
		    move_cursor(wMain, DOWN, params[0]);
		    getyx(wMain, y, x);
		    wmove(wMain, y, 0);
		    break;
		case 'F':
		    move_cursor(wMain, UP, params[0]);
		    getyx(wMain, y, x);
		    wmove(wMain, y, 0);
		    break;
		case 'G':
		    getyx(wMain, y, x);
		    wmove(wMain, y, params[0]-1);
		    break;
		case 'H':
		    wmove(wMain, params[0]-1, params[1]-1);
		    break;
		case 'J':
		    if (params[0] == 0)
			wclrtobot(wMain);
		    else if (params[0] == 2)
			wclear(wMain);
		    break;
		case 'K':
		    if (params[0] == 0)
			wclrtoeol(wMain);
		    else if (params[0] == 2) {
			getyx(wMain, y, x);
			wmove(wMain, y, 0);
			wclrtoeol(wMain);
		    }
		    break;
		case 'm':
		    for (i=0; i<npar; i++) {
			switch (params[i]) {
			    case ANSI_DEFAULT:
			        *attrib = 0;
				break;
			    case ANSI_BOLD:
			        *attrib |= A_BOLD;
				break;
			    case ANSI_REVERSE:
			        *attrib |= A_REVERSE;
				break;
			    case ANSI_UNDERLINE:
			        *attrib |= A_UNDERLINE;
				break;
			    case ANSI_BLINK:
			        *attrib |= A_BLINK;
				break;
			    case ANSI_BOLD_OFF:
			        *attrib &= ~A_BOLD;
				break;
			    case ANSI_REVERSE_OFF:
			        *attrib &= ~A_REVERSE;
				break;
			    case ANSI_UNDERLINE_OFF:
			        *attrib &= ~A_UNDERLINE;
				break;
			    case ANSI_BLINK_OFF:
			        *attrib &= ~A_BLINK;
				break;
			    case ANSI_FG_DEFAULT:
			        *attrib &= ~A_COLOR;
				break;
			    case ANSI_FG_BLACK:
			    case ANSI_FG_RED:
			    case ANSI_FG_GREEN:
			    case ANSI_FG_BROWN:
			    case ANSI_FG_BLUE:
			    case ANSI_FG_MAGENTA:
			    case ANSI_FG_CYAN:
			    case ANSI_FG_WHITE:
				*attrib = (*attrib & ~A_COLOR) |
			        	   COLOR_PAIR(params[i]-29);
				break;
			    default:
				break;
			}
		    }

		    wattrset(wMain, *attrib);
		    break;
		case 's':
		    getyx(wMain, y_cur, x_cur);
		    break; 
		case 'u':
		    wmove(wMain, y_cur, x_cur);
		    break; 
                default:
		    break;
            }
	    return pBuf-pBegin;
        }
	pBuf++;
    }
 
    return 0;	/* push the current escape sequence back for later check */
}

void process_buffer(void) 
{
    static char   buffer[OUTBUF_LENGTH];
    static char   trigbuf[OUTBUF_LENGTH];
    	   char  *pObuf = outbuf;
    	   char  *pBuf = buffer;
           char  *pTrg = trigbuf;
    	   bool   EscSeq = FALSE;
    static int    attrib;
  	   int	  len;

    while (pObuf < pOut) {
        if (!isprint(*pObuf)) {
            switch(*pObuf) {
                case ESC:
		    if (pBuf != buffer) {
	    	    	waddnstr(wMain, buffer, pBuf-buffer);
    		    	put_in_scroll(buffer, pBuf, attrib);
		        pBuf = buffer;
		    }
		    if ((len = check_esc(pObuf, &attrib)) <= 0) {
			EscSeq = TRUE;
			break;
		    }
		    pObuf += len;
                    continue;
                case (char)IAC:
                    pObuf += check_iac((unsigned char *)pObuf);
                    continue;
                case BEEP:
		    beep();
                    pObuf++;
                    continue;
                case CR:
                    pObuf++;
                    continue;
                default:
                    break;
            }
        }

        if (EscSeq) {
	    char *pEsc = outbuf;

	    while (pObuf < pOut)
	        *pEsc++ = *pObuf++;
	    pOut = pEsc;
	    break;
        }

        *pTrg++ = *pBuf++ = *pObuf++;
    }

    if (!EscSeq)
        pOut = outbuf;

    if (pBuf != buffer) {
	waddnstr(wMain, buffer, pBuf-buffer);
    	put_in_scroll(buffer, pBuf, attrib);
    }

    if (pTrg != trigbuf) {
        *pTrg = '\0';
	trigger_check(trigbuf);
    }

    fProcessData = FALSE;
    return;
}



syntax highlighted by Code2HTML, v. 0.9.1