/* 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 <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ncurses.h>
#include <panel.h>
#include "mudix.h"

USER    *settings;
TRIGGER *global_trigger;
NAMING  *global_naming;
PATH    *global_path;
VAR     *global_variable;
int      global_alias;

char *smash_tilde(char *buf)
{
    char *pBuf = buf;

    while (*pBuf) {
	if (*pBuf == '~')
	    *pBuf = '-';
	pBuf++;
    }
    return buf;
}

void init_settings(int argc, char **argv) 
{
    int i;

    if (!(settings = (USER *)malloc(sizeof(USER)))) {
	fprintf(stdout, "No memory for settings?!\n");
	exit(1);
    }

    fEchoIn = TRUE;
    fEchoOut = TRUE;

    settings->trigger_list = NULL;
    settings->naming_list  = NULL;
    settings->path_list    = NULL;
    settings->tabs_list    = NULL;
    settings->vars_list    = NULL;
    settings->site         = NULL;
    settings->port         = 0;
    settings->state        = STATE_READY;
    settings->sock 	   = 0;
    
    for (i=0; i<ALIAS_MAX; i++)
	settings->alias[i] = NULL;

    if (argc != 2) {
	settings->site = argv[1];
	settings->port = atoi(argv[2]);
    }
    else if (!load_settings(argv[1])) {
	printf("Error in userfile!\n");
	exit(1);
    }
}

SSTATE set_settings(char *input) 
{
    char *pIn = input;

    if (*input == '\0') {
	if (global_trigger)
	    free_trigger(global_trigger);
	if (global_naming)
	    free_naming(global_naming);
	if (global_path)
	    free_path(global_path);
	if (global_variable)
	    free_var(global_variable);
	global_trigger = NULL;
	global_naming = NULL;
	global_path = NULL;
	global_variable = NULL;
	settings->state = STATE_READY;
	return STATE_ILLPARM;
    }

    do {
	if (!isprint(*pIn))
	    *pIn = '\0';
    }
    while (*pIn++ != '\0');
	
    switch(settings->state) {
	default:
	    break;
	case STATE_CREATE_1:
            if (!(global_trigger = new_trigger(TRG_LOGIN, FALSE))) {
                do_status("No memory for trigger?", 3);
                settings->state = STATE_READY;
                return STATE_NOMEM;
            }

            global_trigger->in  = (char *)malloc(strlen(input)+1);
            global_trigger->inp = global_trigger->in;
            strcpy(global_trigger->in, input);
	    dialog("What name do you want to login with?");
            settings->state = STATE_CREATE_2;
            break;
        case STATE_CREATE_2:
            global_trigger->out = (char *)malloc(strlen(input)+1);
            strcpy(global_trigger->out, input);
	    global_trigger->out[0]  = toupper(global_trigger->out[0]);
            global_trigger->enabled = TRUE;
            dialog("What text should trigger the password trigger?");
            settings->state = STATE_CREATE_3;
            break;
        case STATE_CREATE_3:
            if (!(global_trigger = new_trigger(TRG_PASSWORD, FALSE))) {
                do_status("No memory for trigger?", 3);
                settings->state = STATE_READY;
               	return STATE_NOMEM; 
            }

            global_trigger->in  = (char *)malloc(strlen(input)+1);
            global_trigger->inp = global_trigger->in;
            strcpy(global_trigger->in, input);
            dialog("What password do you want to use to login with?");
            settings->state = STATE_CREATE_4;
            break;
        case STATE_CREATE_4:
            global_trigger->out = (char *)malloc(strlen(input)+1);
            strcpy(global_trigger->out, input);
            global_trigger->enabled = TRUE;
            settings->state = STATE_READY;
            break;
	case STATE_TABSDELETE:
	  {
	    TABS *tabs;
 
	    for (tabs = settings->tabs_list; tabs; tabs = tabs->next) {
		if (!tabs->name)
		    continue;

		if (strcmp(tabs->name, input))
		    continue;

		free_tabs(tabs);
                do_status("Tab deleted.", 3);            
		break;
	    }  

 	    settings->state = STATE_READY;
	    break;
	  }
	case STATE_VDELETE:
	  {
	    VAR *pVar;
 
	    for (pVar = settings->vars_list; pVar; pVar = pVar->next) {
		if (!pVar->name || !pVar->value)
		    continue;

		if (strcmp(pVar->name, input))
		    continue;

		free_var(pVar);
                do_status("Variable deleted.", 3);            
		break;
	    }  

 	    settings->state = STATE_READY;
	    break;
	  }
	case STATE_NDELETE:
	  {
	    NAMING *pNm;
 
	    for (pNm = settings->naming_list; pNm; pNm = pNm->next) {
		if (!pNm->name || !pNm->string)
		    continue;

		if (strcmp(pNm->name, input))
		    continue;

		free_naming(pNm);
                do_status("Naming alias deleted.", 3);            
		break;
	    }  

 	    settings->state = STATE_READY;
	    break;
	  }
	case STATE_PDELETE:
	  {
	    PATH *path;
 
	    for (path = settings->path_list; path; path = path->next) {
		if (!path->name || !path->path)
		    continue;

		if (strcmp(path->name, input))
		    continue;

		free_path(path);
                do_status("Path deleted.", 3);            
		break;
	    }  

 	    settings->state = STATE_READY;
	    break;
	  }
 	case STATE_TDELETE:
          {
            TRIGGER *trig;
	    int      count = atoi(input);

    	    for (trig = settings->trigger_list; trig; trig = trig->next) {
        	if (!trig->in || !trig->out)
            	    continue;
		
		if (--count == 0) {
		    free_trigger(trig); 
                    do_status("Trigger deleted.", 3);            
		    break;
		}
	    }

 	    settings->state = STATE_READY;
	    break;
	  }
 	case STATE_TTOGGLE:
	  {
            TRIGGER *trig;
	    int      count = atoi(input);

    	    for (trig = settings->trigger_list; trig; trig = trig->next) {
        	if (!trig->in || !trig->out)
            	    continue;
		
		if (--count == 0) {
		    trig->enabled ^= TRUE; 
		    if (trig->enabled)
                    	do_status("Trigger enabled.", 3);            
		    else
                    	do_status("Trigger disabled.", 3);            
			
		    break;
		}
	    }

 	    settings->state = STATE_READY;
	    break;
	  }
 	case STATE_TCREATE_1:
	  {
	    int level = atoi(input);

	    if (level < 0) {
		settings->state = STATE_READY;
		return STATE_ILLPARM;
	    }

	    if (!(global_trigger = new_trigger(level, FALSE))) {
		do_status("No memory for trigger?", 3);
 	        settings->state = STATE_READY;
		return STATE_NOMEM;
	    }

 	    settings->state = STATE_TCREATE_2;
	    break;
	  }
	case STATE_TCREATE_2:
	    global_trigger->in  = (char *)malloc(strlen(input)+1);
	    global_trigger->inp = global_trigger->in;
            strcpy(global_trigger->in, input);
 	    settings->state = STATE_TCREATE_3;
            break;
	case STATE_TCREATE_3:
	    global_trigger->out = (char *)malloc(strlen(input)+1);
            strcpy(global_trigger->out, input);
            do_status("Trigger created and enabled.", 3);            
	    global_trigger->enabled = TRUE;
 	    settings->state = STATE_READY;
            break;
	case STATE_TABSCREATE:
	  {
	    TABS *tabs;

	    if (!(tabs = new_tabs(FALSE))) {
		do_status("No memory for tab?", 3);
 	        settings->state = STATE_READY;
		return STATE_NOMEM;
	    }

	    tabs->name = (char *)malloc(strlen(input)+1);
            strcpy(tabs->name, input);
            do_status("Tab created.", 3);            
 	    settings->state = STATE_READY;
            break;
	  }
	case STATE_PCREATE_1:
	    if (!(global_path = new_path(FALSE))) {
		do_status("No memory for path?", 3);
 	        settings->state = STATE_READY;
		return STATE_NOMEM;
	    }

	    global_path->name = (char *)malloc(strlen(input)+1);
            strcpy(global_path->name, input);
 	    settings->state = STATE_PCREATE_2;
            break;
	case STATE_PCREATE_2:
	    global_path->path = (char *)malloc(strlen(input)+1);
            strcpy(global_path->path, input);
            do_status("Path created.", 3);            
 	    settings->state = STATE_READY;
            break;
	case STATE_VCREATE_1:
	    if (!(global_variable = var_lookup(input)) &&
		!(global_variable = new_var(FALSE))) {
		do_status("No memory for variable?", 3);
 	        settings->state = STATE_READY;
		return STATE_NOMEM;
	    }

	    if (!global_variable->name) {
	        global_variable->name = (char *)malloc(strlen(input)+1);
                strcpy(global_variable->name, input);
	    }
 	    settings->state = STATE_VCREATE_2;
            break;
	case STATE_VCREATE_2:
	    if (global_variable->value)
		free(global_variable->value);
	    global_variable->value = (char *)malloc(strlen(input)+1);
            strcpy(global_variable->value, input);
            do_status("Variable set.", 3);            
 	    settings->state = STATE_READY;
            break;
	case STATE_ALIAS_1:
	    global_alias = isdigit(*input)? atoi(input)-1: atoi(input+1)-1;

            if (global_alias < 0 || global_alias > ALIAS_MAX) {
		do_status("Illegal alias slot.", 3);
 	        settings->state = STATE_READY;
                return STATE_ILLPARM;
	    }

 	    settings->state = STATE_ALIAS_2;
            break;
	case STATE_ALIAS_2:
	    if (settings->alias[global_alias])
		free(settings->alias[global_alias]);
	    settings->alias[global_alias] = (char *)malloc(strlen(input)+1);
	    strcpy(settings->alias[global_alias], input);
            do_status("Alias created.", 3);            
 	    settings->state = STATE_READY;
            break;
	case STATE_NAMING_1:
	  {
	    NAMING *pNm;
 
	    for (pNm = settings->naming_list; pNm; pNm = pNm->next) {
		if (!pNm->name || !pNm->string)
		    continue;

		if (!strcmp(pNm->name, input)) {
		    do_status("Name alias already exists.", 3);
 	            settings->state = STATE_READY;
		    return STATE_READY;
		}
	    }

	    if (!(global_naming = new_naming(FALSE))) {
		do_status("No memory for name alias?", 3);
 	        settings->state = STATE_READY;
		return STATE_NOMEM;
	    }

	    global_naming->name = (char *)malloc(strlen(input)+1);
            strcpy(global_naming->name, input);
 	    settings->state = STATE_NAMING_2;
	    break;
	  }
	case STATE_NAMING_2:
	    global_naming->string = (char *)malloc(strlen(input)+1);
            strcpy(global_naming->string, input);
            do_status("Naming alias created.", 3);            
 	    settings->state = STATE_READY;
            break;
	case STATE_QUIT:
	    if (*input == 'y' || *input == 'Y')
		fDown = TRUE;
 	    settings->state = STATE_READY;
	    break;
	case STATE_SAVE:
	  {
            TRIGGER *trig;
            NAMING  *pNm;
            PATH    *path;
            TABS    *tabs;
            VAR     *var;
	    FILE    *fp;
	    char     filename[MAX_STRING];
	    int      i;

	    if (*input == '\0') {
		do_status("Illegal filename.", 3);
 	        settings->state = STATE_READY;
                return STATE_ILLPARM;
	    }
		
            sprintf(filename, "%s.usr", input);
	    if (!(fp = fopen(filename, "w"))) { 
		do_status("Failed to write to file.", 3);
 	        settings->state = STATE_READY;
                return STATE_ERRFILE;
	    }

	    fprintf(fp, "#USER\nsite %s~\nport %d~\n", 
				settings->site, settings->port);

	    for (i=0; i<ALIAS_MAX; i++) {
		if (!settings->alias[i] || settings->alias[i][0] == '\0')
		    continue;
		fprintf(fp, "al %d %s~\n", i, smash_tilde(settings->alias[i]));
	    }

    	    for (trig = settings->trigger_list; trig; trig = trig->next) {
        	if (!trig->in || !trig->out)
            	    continue;

        	fprintf(fp, "trig %d\n%s~\n%s~\n", trig->level, 
				smash_tilde(trig->in), smash_tilde(trig->out));
    	    }
	    
    	    for (pNm = settings->naming_list; pNm; pNm = pNm->next) {
        	if (!pNm->name || !pNm->string)
            	    continue;

        	fprintf(fp, "nmal\n%s~\n%s~\n", smash_tilde(pNm->name), 
						smash_tilde(pNm->string));
    	    }
	    
    	    for (path = settings->path_list; path; path = path->next) {
        	if (!path->name || !path->path)
            	    continue;

        	fprintf(fp, "path\n%s~\n%s~\n", smash_tilde(path->name), 
						smash_tilde(path->path));
    	    }
	    
    	    for (tabs = settings->tabs_list; tabs; tabs = tabs->next) {
        	if (!tabs->name)
            	    continue;

        	fprintf(fp, "tab\n%s~\n", smash_tilde(tabs->name));
    	    }
	    
    	    for (var = settings->vars_list; var; var = var->next) {
        	if (!var->name || !var->value)
            	    continue;

        	fprintf(fp, "var\n%s~\n%s~\n", smash_tilde(var->name), 
					       smash_tilde(var->value));
    	    }
	    
            fprintf(fp, "#END");
	    fclose(fp);
	    do_status("Userfile created.", 3);
 	    settings->state = STATE_READY;
            break;
	  }
	case STATE_WRITE:
          /* 
	   * Write settings in a format that can be read with #read 
	   * In most cases this will serve as an example macro file...
	   * Edit the created file to your likings (E)
	   */
          {
            TRIGGER *trig;
            NAMING  *pNm;
            PATH    *path;
            TABS    *tabs;
	    VAR     *var;
            FILE    *fp;
            char     filename[MAX_STRING];
            int      i;

            if (*input == '\0') {
                do_status("Illegal filename.", 3);
                settings->state = STATE_READY;
                return STATE_ILLPARM;
            }

            sprintf(filename, "%s.cmd", input);
            if (!(fp = fopen(filename, "w"))) {
                do_status("Failed to write to file.", 3);
                settings->state = STATE_READY;
                return STATE_ERRFILE;
            }

            for (i=0; i<ALIAS_MAX; i++) {
                if (!settings->alias[i] || settings->alias[i][0] == '\0')
                    continue;

                fprintf(fp, "#alias %d {%s}\n", i+1, 
					smash_tilde(settings->alias[i]));
            }

            for (trig = settings->trigger_list; trig; trig = trig->next) {
                if (!trig->in || !trig->out)
                    continue;

                fprintf(fp, "#trigger %d {%s} {%s}\n", trig->level,
                                smash_tilde(trig->in), smash_tilde(trig->out));
            }

            for (pNm = settings->naming_list; pNm; pNm = pNm->next) {
                if (!pNm->name || !pNm->string)
                    continue;

                fprintf(fp, "#nmalias {%s} {%s}\n", smash_tilde(pNm->name),
                                                smash_tilde(pNm->string));
            }

    	    for (path = settings->path_list; path; path = path->next) {
        	if (!path->name || !path->path)
            	    continue;

        	fprintf(fp, "#path {%s} {%s}\n", smash_tilde(path->name), 
						 smash_tilde(path->path));
    	    }
	    
    	    for (tabs = settings->tabs_list; tabs; tabs = tabs->next) {
        	if (!tabs->name)
            	    continue;

        	fprintf(fp, "#tab {%s}\n", smash_tilde(tabs->name));
    	    }

    	    for (var = settings->vars_list; var; var = var->next) {
        	if (!var->name || !var->value)
            	    continue;

        	fprintf(fp, "#variable {%s} {%s}\n", smash_tilde(var->name), 
						     smash_tilde(var->value));
    	    }

            fclose(fp);
            do_status("Commandfile created.", 3);
            settings->state = STATE_READY;
            break;
          }
    }

    if (settings->state == STATE_READY) {
	hide_panel(pDialog);
        global_trigger = NULL;
	global_naming = NULL;
	global_path = NULL;
	global_variable = NULL;
    }

    return settings->state;
}

void show_settings(void) 
{
    TRIGGER *trig;
    NAMING  *alias;
    PATH    *path;
    TABS    *tabs;
    VAR     *var;
    char    *str;
    int      lines, i, width, height, centre;

    width  = LEN_COL-4;
    height = 21;
    centre = ((width-2)/2)-1;

    wresize(wMsg, height, width);
    werase(wMsg);
    move_panel(pMsg, 1, 2);

    trig = trig_lookup(NULL, TRG_LOGIN);

    lines = 1;
    str = "MUDix v"VERSION" by M. Boomstra (enigma@dwizardry.dhs.org)";
    mvwprintw(wMsg, lines++, centre-strlen(str)/2, str);
    mvwhline(wMsg, lines++, 0, ACS_HLINE, LEN_COL-4);
    mvwprintw(wMsg, lines++, 1, "Username  : %s", trig? trig->out: "Unknown");
    mvwprintw(wMsg, lines-1, centre, " Host      : %s", settings->site);
    mvwprintw(wMsg, lines++, 1, "Port      : %d", settings->port);
    mvwprintw(wMsg, lines-1, centre, " Socket    : %d", settings->sock);
    mvwprintw(wMsg, lines++, 1, "Sent      : %d Bytes", total_sent);
    mvwprintw(wMsg, lines-1, centre, " Received  : %d Bytes", total_recv);
    mvwprintw(wMsg, lines++, 1, "Echo input: %s", fEchoIn? "On": "Off");
    mvwprintw(wMsg, lines-1, centre, " Echo outp.: %s", fEchoOut? "On": "Off");
    mvwprintw(wMsg, lines++, 1, "Keypadwalk: %s", fKeypadWalk? "On": "Off");
    mvwprintw(wMsg, lines-1, centre, " Status    : %s", 
				fStatusReport? "On": "Off");
    mvwprintw(wMsg, lines++, 1, "Char. mode: %s", fCharacterMode? "On": "Off");

    mvwhline(wMsg, lines++, 0, ACS_HLINE, LEN_COL-4);
    for (i=0, trig = settings->trigger_list; trig; trig = trig->next, i++);
    mvwprintw(wMsg, lines++, 1, "Triggers  : %d", i);
    for (i=0, alias = settings->naming_list; alias; alias = alias->next, i++);
    mvwprintw(wMsg, lines++, 1, "Aliases   : %d", i);
    for (i=0, path = settings->path_list; path; path = path->next, i++);
    mvwprintw(wMsg, lines++, 1, "Paths     : %d", i);
    for (i=0, tabs = settings->tabs_list; tabs; tabs = tabs->next, i++);
    mvwprintw(wMsg, lines-3, centre, " Tabs      : %d", i);
    for (i=0, var = settings->vars_list; var; var = var->next, i++);
    mvwprintw(wMsg, lines-2, centre, " Variables : %d", i);

    mvwhline(wMsg, lines++, 0, ACS_HLINE, LEN_COL-4);
    for(i=0; i<ALIAS_MAX/2; i++)
        mvwprintw(wMsg, lines++, 1, "F%-2d: %s", 
			i+1, settings->alias[i] ? settings->alias[i] : "");
    lines-=ALIAS_MAX/2;
    for( ; i<ALIAS_MAX; i++)
        mvwprintw(wMsg, lines++, centre, " F%-2d: %s", 
		i+1, settings->alias[i] ? settings->alias[i] : "");

    draw_border(wMsg);
    str = "MUDix Settings";
    mvwprintw(wMsg, 0, centre-strlen(str)/2, str);
    show_panel(pMsg);
    return;
}

bool load_settings(char *file) 
{
    FILE *fp;
    bool  found;

    if (!(fp = fopen(file, "r")))
	return FALSE;

    found = FALSE;
    while (1) {
        char *word, *string;

        word = fread_word(fp);
	if (*word == '\0')
	    break;

        if (!strcmp(word, "#USER")) {
	    if (feof(fp))
	        break;
        }
        else if (!strcmp(word, "#END")) 
	{
	    found = TRUE;
	    break;
        }
	else if (!strcmp(word, "site")) {
	    string = fread_string(fp);
	    settings->site = (char *)malloc(strlen(string)+1);
	    strcpy(settings->site, string);
	}   
	else if (!strcmp(word, "al")) {
	    int nr = atoi(fread_word(fp));

	    string = fread_string(fp);
	    settings->alias[nr] = (char *)malloc(strlen(string)+1);
	    strcpy(settings->alias[nr], string);
	}   
	else if (!strcmp(word, "nmal")) {
	    NAMING *pNm;

	    if (!(pNm = new_naming(TRUE)))
		break;

	    string = fread_string(fp);
	    pNm->name = (char *)malloc(strlen(string)+1);
	    strcpy(pNm->name, string);
	    string = fread_string(fp);
	    pNm->string = (char *)malloc(strlen(string)+1);
	    strcpy(pNm->string, string);
	}
	else if (!strcmp(word, "tab")) {
	    TABS *tabs;

	    if (!(tabs = new_tabs(TRUE)))
		break;

	    string = fread_string(fp);
	    tabs->name = (char *)malloc(strlen(string)+1);
	    strcpy(tabs->name, string);
	}
	else if (!strcmp(word, "path")) {
	    PATH *path;

	    if (!(path = new_path(TRUE)))
		break;

	    string = fread_string(fp);
	    path->name = (char *)malloc(strlen(string)+1);
	    strcpy(path->name, string);
	    string = fread_string(fp);
	    path->path = (char *)malloc(strlen(string)+1);
	    strcpy(path->path, string);
	}
	else if (!strcmp(word, "var")) {
	    VAR *var;

	    if (!(var = new_var(TRUE)))
		break;

	    string = fread_string(fp);
	    var->name = (char *)malloc(strlen(string)+1);
	    strcpy(var->name, string);
	    string = fread_string(fp);
	    var->value = (char *)malloc(strlen(string)+1);
	    strcpy(var->value, string);
	}
	else if (!strcmp(word, "trig")) {
            TRIGGER *trigger;
	    int      level = atoi(fread_word(fp));

            if (!(trigger = new_trigger(level, TRUE)))
		break;

	    string = fread_string(fp);
	    trigger->in  = (char *)malloc(strlen(string)+1);
	    trigger->inp = trigger->in;
	    strcpy(trigger->in, string);
	    string = fread_string(fp);
	    trigger->out = (char *)malloc(strlen(string)+1);
	    strcpy(trigger->out, string);
	    trigger->enabled = TRUE;
	}   
	else if (!strcmp(word, "port")) {
	    string = fread_string(fp);
	    settings->port = atoi(string);
	}   
        else
            continue;
    }
  
    fclose(fp);

    return found;
}


syntax highlighted by Code2HTML, v. 0.9.1