/*
 *      input.c from Access Point SNMP Utils for Linux
 *	program input & screen related functions
 *
 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
 * Copyright (c) 2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 from
 * June 1991 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <menu.h>
#include <errno.h>
#include "ap-utils.h"

extern WINDOW *main_sub, *win_for_help;

#define GEN_IV_MSG _("Invalid value. Press any key to continue.")
#define OOR_IV_MSG _("Value must be in range %u - %u. Press any key to continue.")

#define MAC_LEN 12
#define MAC_BYTES 6

void
get_mac(char *mac, int row, int col)
{
    char message[MAC_LEN+1], mess[MAC_LEN/MAC_BYTES+1];
    int i;

    message[0] = '\0';
    while (strlen(message) != 12)
	get_value(message, row, col, MAC_LEN+1, HEX_STRING, 0, 0, NULL);

    for (i = 0; i < MAC_BYTES; i++) {
	mess[0] = message[2 * i];
	mess[1] = message[2 * i + 1];
	mess[2] = '\0';
	mac[i] = strtol(mess, NULL, 16);
    }
}

#define IP_LEN 15

void
get_ip(struct in_addr *ip, int row, int col, char *helpmsg)
{
    char message[IP_LEN+1], *cp;
    int dotc;

get_ip_again:
    get_value(message, row, col, IP_LEN+1, ANY_STRING, 0, 0, NULL);
    for (cp = message, dotc = 0; *cp && (cp = index(cp, '.')); cp++, dotc++);
    if (dotc < 3 || !(inet_aton(message, ip))) {
	if (helpmsg) {
	    print_helperr(GEN_IV_MSG);
	    getch();
	    print_help(helpmsg);
	}
	goto get_ip_again;
    }
}

void
get_mask(struct in_addr *ip, int row, int col, char *helpmsg)
{
    int i, bit, hmask, trans_count;

get_mask_again:
    trans_count = 0;
    get_ip(ip, row, col, helpmsg);
    hmask = ntohl(ip->s_addr);
    bit = hmask & 0x00000001;
    for (i = 1; i < 32; i++)
	if (((hmask >> i) & 0x00000001) != bit) {
	    bit ^= 0x00000001;
	    trans_count++;
	}

    if (trans_count > 1) {
	print_helperr(GEN_IV_MSG);
	getch();
	print_help(helpmsg);
	goto get_mask_again;
    }
}

/*
 * This is the main input function for all cases when user needs to enter
 * a visible string. It also performs syntax correctness checks
 * as well as bounds checks if required.
 *
 * value ....... this should keep the value, entered by the user,
 *		 in ascii string notation
 * row
 * col ......... the begin coordinates, relative to the current window,
 *		 at which the string entered by the user will appear
 * len ......... maximum length of the entered string, including the
 *		 tailing '\0' character. Negative value along with
 *		 vt=ANY_STRING means that zero-length string is allowed.
 * vt .......... desired input value type (types INT_STRING, HEX_STRING,
 *		 ANY_STRING)
 * minv, maxv .. min/max bounds in case of integer value input. Each can be
 *		 of value 0-0xFFFFFFFF. If both are 0, it means no value bounds
 *		 checking will be performed. Used only with vt=INT_STRING.
 * helpmsg ..... pointer to string that will be printed after OOR_IV_MSG
 *		 message if the entered integer value is out of range.
 *		 If NULL, then no 'invalid value' and subsequent status line
 *		 message will be printed, and whole input process will be
 *		 repeated. Used only with vt=INT_STRING.
 */
void
get_value(char *value, int row, int col, int len,
	  char vt, unsigned int minv, unsigned int maxv, char *helpmsg)
{
    int c, i, x, y;
    unsigned char acs;
    char iv_msg[128], zerolen_perm = 0;
    enum { INSERT, OVERWRITE } mode;
    WINDOW *gv_win;

    if (vt == ANY_STRING && len < 0) {
	zerolen_perm = 1;
	len = -len;
    }

    gv_win = derwin(main_sub, 1, len - 1, row, col);
    wbkgdset(gv_win, A_REVERSE);

get_value_again:
    mode = INSERT;
    curs_set(1);
    werase(gv_win);
    wrefresh(gv_win);

    i = 0; /* means 'character count' here */
    while (1) {
	c = getch();
	acs = 0;
	switch (vt) {
	    case INT_STRING:
		if (c >= '0' && c <= '9') acs = 1;
		break;
	    case HEX_STRING:
		if ((c >= '0' && c <= '9') ||
		    (c >= 'a' && c <= 'f') ||
		    (c >= 'A' && c <= 'F')) acs = 1;
		break;
	    case ANY_STRING:
		acs = 1;
	}
	/* rather do not ask, how this works... */
	getyx(gv_win, y, x);
	switch (c) {
	    case KEY_BACKSPACE:
		/* lefthandside DELETE */
		if (x > 0) {
		    /*
		     * do not step cursor back only if window is filled AND
		     * cursor is totally on RHS
		     */
		    if ((i < len - 1) || (x < len - 2))
			x--;
		    mvwdelch(gv_win, 0, x);
		    wrefresh(gv_win);
		    i--;
		}
		break;
	    case KEY_DC:
		/* righthandside DELETE */
		if (i > 0) {
		    if (x == i)
			x--;
		    mvwdelch(gv_win, 0, x);
		    wrefresh(gv_win);
		    i--;
		}
		break;
	    case KEY_LEFT:
		if (x > 0) {
		    wmove(gv_win, 0, --x);
		    wrefresh(gv_win);
		}
		break;
	    case KEY_RIGHT:
		if (x < i) {
		    wmove(gv_win, 0, ++x);
		    wrefresh(gv_win);
		}
		break;
	    case KEY_HOME:
		wmove(gv_win, 0, 0);
		wrefresh(gv_win);
		break;
	    case KEY_END:
		wmove(gv_win, 0, (i == len - 1 ? i - 1 : i));
		wrefresh(gv_win);
		break;
	    case KEY_IC:
		mode = (mode == INSERT ? OVERWRITE : INSERT);
		curs_set(mode == INSERT ? 1 : 2);
		break;
	    case 0x0A:
		/* ENTER KEY */
		if (i > 0 || zerolen_perm)
		    goto away;

		break;
	    default:
		if (acs && (c < 0x100)) {
		    if (mode == INSERT) {
			if (i < len - 1) {
			    winsch(gv_win, c);
			    i++;
			    wmove(gv_win, 0, ++x);
			    wrefresh(gv_win);
			}
		    } else {
			if (i <= len - 1) {
//			    wechochar(gv_win, c);
			    waddch(gv_win, c);
			    if (x == i)
				i++;
			    wmove(gv_win, 0, ++x);
			    wrefresh(gv_win);
			}
		    }
		}
	}
    }

away:
    wmove(gv_win, 0, 0);
    winnstr(gv_win, value, i);
    value[i] = 0;
    curs_set(0);

    if (vt == INT_STRING) {
	i = strtoul(value, (char **)NULL, 10);
	sprintf(value, "%i", i); /* eat leading zeros */
	if (minv | maxv) {
	    errno = 0;
	    if ((unsigned)i < minv || (unsigned)i > maxv || errno == ERANGE) {
		if (helpmsg) {
		    snprintf(iv_msg, sizeof(iv_msg) -1, OOR_IV_MSG, minv, maxv);
		    print_helperr(iv_msg);
		    getch();
		    print_help(helpmsg);
		}
		goto get_value_again;
	    }
	}
    }

    wbkgdset(gv_win, A_BOLD);
    werase(gv_win);
    waddstr(gv_win, value);
    wrefresh(gv_win);
//    mvwprintw(main_sub, 18, 1, "length: %i", i);
//    wrefresh(main_sub);
    delwin(gv_win);
}

/*
 * Note: indeed we should use get_value() for this action, but for the moment,
 * we'll settle with this one.
 */

void
get_pass(char *value, int row, int col, int len)
{
    int i, j = 0;

    wattrset(main_sub, COLOR_PAIR(12));
    wmove(main_sub, row, col);
    for (i = 0; i < len - 1; i++)
	waddch(main_sub, ' ');
    curs_set(1);

    while (1) {
	value[j] = mvwgetch(main_sub, row, col + j);
	if (value[j] == 0x0A) /* NEWLINE */
	    break;
	if (value[j] == 0x1B) /* ESCAPE */
	    continue;
	if (value[j] != 0x7F) { /* BACKSPACE */
	    if (j < len - 1)
		mvwaddch(main_sub, row, col + j++, '*');
	} else {
	    if (j)
		mvwaddch(main_sub, row, col + --j, ' ');
	}
    };
    value[j] = '\0';

    curs_set(0);
    wattrset(main_sub, A_BOLD);
    wmove(main_sub, row, col);
    for (i = 0; i < len - 1; i++)
	waddch(main_sub, ' ');
    wmove(main_sub, row, col);
    for (i = 0; i < j; i++)
	waddch(main_sub, '*');
    wattrset(main_sub, A_NORMAL);
    wrefresh(main_sub);
}

int
yes_no(int brow, int bcol)
{
    char *names[2] = { YES, NO };

    return menu_choose(brow, bcol, names, 2) + 1;
}

int
on_off(int brow, int bcol)
{
    char *names[2] = { ON, OFF };

    return menu_choose(brow, bcol, names, 2) + 1;
}

int menu_choose(int brow, int bcol, char **names, unsigned int num)
{
    unsigned short int c;
    ITEM **menu_item = calloc(num, sizeof(ITEM)), **ip = menu_item;
    MENU *menu1;
    unsigned int ncol = 2, i, nrow;
    WINDOW *choose_menu, *sub_choose_menu;
    extern WINDOW *main_sub;

    for (i = 0; i < num; i++) {
	*ip++ = new_item(names[i], "");
	if (ncol < strlen(names[i]) + 3)
	    ncol = strlen(names[i]) + 3;
    }
    *ip = (ITEM *) 0;


    nrow = num + 2;
    if (brow + 2 + nrow <= (unsigned int) LINES - 2)
	choose_menu = newwin(nrow, ncol, brow + 2, bcol + MCOLS);
    else
	choose_menu = newwin(nrow, ncol, brow - nrow + 3, bcol + MCOLS);
    sub_choose_menu = derwin(choose_menu, nrow - 2, ncol - 2, 1, 1);
    attrset(COLOR_PAIR(11));

    mvwaddch(choose_menu, 0, 0, ACS_ULCORNER);
    mvwaddch(choose_menu, 0, ncol - 1, ACS_URCORNER);
    mvwaddch(choose_menu, nrow - 1, 0, ACS_LLCORNER);
    mvwaddch(choose_menu, nrow - 1, ncol - 1, ACS_LRCORNER);
    for (i = 1; i < ncol - 1; i++) {
	mvwaddch(choose_menu, 0, i, ACS_HLINE);
	mvwaddch(choose_menu, nrow - 1, i, ACS_HLINE);
    }
    for (i = 1; i < nrow - 1; i++) {
	mvwaddch(choose_menu, i, 0, ACS_VLINE);
	mvwaddch(choose_menu, i, ncol - 1, ACS_VLINE);
    }
    wrefresh(choose_menu);


    menu1 = new_menu(menu_item);
    set_menu_win(menu1, choose_menu);
    set_menu_sub(menu1, sub_choose_menu);

    set_menu_opts(menu1, O_ONEVALUE);

    curs_set(0);
    post_menu(menu1);
    wrefresh(sub_choose_menu);

    while (1) {
	switch (getch()) {
	case KEY_DOWN:
	case 'j':
	case 'J':
	    menu_driver(menu1, REQ_NEXT_ITEM);
	    wrefresh(sub_choose_menu);
	    break;
	case KEY_RIGHT:
	case 'l':
	case 'L':
	    menu_driver(menu1, REQ_LAST_ITEM);
	    wrefresh(sub_choose_menu);
	    break;
	case KEY_LEFT:
	case 'h':
	case 'H':
	    menu_driver(menu1, REQ_FIRST_ITEM);
	    wrefresh(sub_choose_menu);
	    break;
	case KEY_UP:
	case 'k':
	case 'K':
	    menu_driver(menu1, REQ_PREV_ITEM);
	    wrefresh(sub_choose_menu);
	    break;
	case 10:
	    i = item_index(current_item(menu1));
	    unpost_menu(menu1);
	    free_menu(menu1);
	    for (c = 0; c < nrow - 2; c++)
		free_item(menu_item[c]);
	    delwin(sub_choose_menu);
	    delwin(choose_menu);
	    redrawwin(main_sub);
	    free(menu_item);
	    return i;

	}
    }
}

/*
 * Reads key by either no-delay getch() (WAIT_FOREVER mode) or using
 * getch() with tval tenths of second timeout (WAIT_TIMEOUT mode).
 * Returns 0 for timeout, or pressed key code.
 */
int wait_key(int tval)
{
    int i;
    extern int wait_mode;

    if (wait_mode == WAIT_TIMEOUT)
	timeout(tval * 100);

    i = getch();

    if (wait_mode == WAIT_TIMEOUT) {
	timeout(-1);
//	nocbreak();
//	cbreak();
    }

    if (i == ERR)
	i = 0;

    return i;
}

int help_ysn()
{
    char c;

    print_help (_("Y - Yes; Any other key - No (it's safer to answer No)"));
    c = getch();
    clear_main(0);
    if (c == 'y' || c == 'Y')
	return 0;

    return 1;
}

