#include <lib.h>
#include <cfg.h>
#include <save.h>
#include <daemons.h>
#include <network.h>
void help();

inherit LIB_DAEMON;

string ret_string = "";
string array config, config2, keywords;
string array bools = ({ "enable","disable","on","off","1","0" });
string array yesbools = ({ "enable","on","1","yes" });
string array nobools = ({ "disable","off","0","no" });
string array restrict_tokens = ({ "restrict","unrestrict" });
string array nonmodals = ({ "liveupgrade", "prompt","status","email",
  "debugger", "access", "pinging" });
string array modals = ({ "matchcommand", "matchobject", "autowiz", "locked","localtime", 
  "justenglish", "justhumans", "encumbrance", "pk", "compat", "exitsbare",
  "retain", "defaultparse", "disablereboot" });
string array inet_services = ({ "oob", "hftp", "ftp", "http", "rcp", "inet" });

static int NotImplemented(string which);
varargs static int TestFun(string which, string arg);
varargs static int ModIntermud(string which, string arg);
varargs static int ModRouter(string which, string arg);
static int ProcessModal(string which, string arg);
varargs static int ModStartRoom(string which, string arg);
static int ProcessOther(string which, string arg);
static int ProcessString(string which, string arg);
int ProcessInet(string which, string arg);

static private void validate() {
    if(!this_player()) return 0;
    if( !((int)master()->valid_apply(({ "ASSIST" }))) )
	error("Illegal attempt to access mudconfig: "+get_stack()+" "+identify(previous_object(-1)));
}

mixed cmd(string str) {
    string which, arg;

    validate();

    if(!str || sscanf(str, "%s %s", which, arg) != 2){
	help();
	return 1;
    }
    cp("/secure/include/config.h","/secure/save/backup/config."+time());
    config = explode( read_file("/secure/include/config.h"),"\n" );
    config2 = ({});
    keywords = ({});
    foreach(string element in config){
	string s1, s2;
	if(sscanf(element,"#define %s %s",s1,s2) == 2) keywords += ({ s1 });
    }

    if(member_array(which,modals) != -1){
	ProcessModal(which, arg);
    }

    if(member_array(which,inet_services) != -1){
	ProcessInet(which, arg);
    }

    else switch(which){
    case "test" : TestFun(which, arg);break;
    case "intermud" : ModIntermud(which, arg);break;
    case "router" : ModRouter(which, arg);break;
    case "startroom" : ModStartRoom(which, arg);break;
    case "resets" : which = "TIME_TO_RESET";ProcessOther(which,arg);break;
    case "offset" : which = "GMT_OFFSET";ProcessOther(which,arg);break;
    case "extraoffset" : which = "EXTRA_TIME_OFFSET";ProcessOther(which,arg);break;
    case "newbielevel" : which = "MAX_NEWBIE_LEVEL";ProcessOther(which,arg);break;
    case "monitor" : which = "GLOBAL_MONITOR";ProcessOther(which,arg);break;
    case "maxip" : which = "SAME_IP_MAX";ProcessOther(which,arg);break;
    case "maxcommands" : which = "MAX_COMMANDS_PER_SECOND";ProcessOther(which,arg);break;
    case "maxidle" : which = "IDLE_TIMEOUT";ProcessOther(which,arg);break;
    case "hostip" : which = "HOST_IP";ProcessString(which,arg);break;
    case "email" : which = "ADMIN_EMAIL";ProcessString(which,arg);break;
    case "liveupgrade" : which = "LIVEUPGRADE_SERVER";ProcessString(which,arg);break;
    case "mudstatus" : which = "MUD_STATUS";ProcessString(which,arg);break;
    case "debugger" : which = "DEBUGGER";ProcessString(which,arg);break;
    default : NotImplemented(which);break;
    }
    return 1;
}

varargs static int CompleteConfig(string file){
    string ret = implode(config2,"\n")+"\n";
    if(!file) file = "/secure/include/config.h";
    validate();
    ret = replace_string(ret,"\n\n","\n");
    write_file(file,ret,1);
    reload(MASTER_D,0,1);
    reload(LIB_CONNECT,0,1);
    write("Command complete.");
    return 1;
}

static int NotImplemented(string which){
    validate();
    write("The keyword \""+which+"\" is not yet implemented in mudconfig.");
    return 1;
}

varargs static int TestFun(string which, string arg){
    validate();
    write("Which: "+which+" arg: "+arg);
    return 1;
}

varargs static int ModStartRoom(string which, string arg){
    if(!arg){
	write("Please specify the absolute path to the room's file.");
	return 1;
    }
    if(first(arg,1) != "/"){
	write("You've entered a relative path. Please try again, using an absolute path.");
	return 1;
    }
    if(last(arg,2) == ".c") arg = truncate(arg,2);
    if(!file_exists(arg+".c")){
	write("That file does not exist.");
	return 1;
    }
    if(catch(load_object(arg))){
	write("/n/nThat room file is broken. Please fix it and try again.");
	return 1;
    }
    cp("/secure/include/rooms.h","/secure/save/backup/room."+time());
    config = explode( read_file("/secure/include/rooms.h"),"\n" );
    config2 = ({});
    foreach(string line in config){
	string s1,s2,s3;
	if(sscanf(line,"%s %s %s",s1,s2,s3) == 3){
	    if(s1 == "#define" && s2 == "ROOM_START") 
		line = "#define ROOM_START       \""+arg+"\"";
	}
	config2 += ({ line });
    }
    CompleteConfig("/secure/include/rooms.h");
    reload(LIB_CREATOR,1,1);
    write("\nNote: Some objects, like verbs and workrooms, still contain the old "+
      "START_ROOM value. This will not change until they are reloaded or "+
      "the mud reboots.");
    return 1;
}


varargs static int ModRouter(string which, string arg){
    string preloads = read_file(CFG_PRELOAD);
    string *load_lines = explode(preloads,"\n");
    string *ret_arr = ({});
    string yesline = "/secure/daemon/i3router/server";
    string noline = "#/secure/daemon/i3router/server";
    validate();
    if(member_array(arg,bools) == -1){
	write("Please try again, indicating whether you want to enable or disable the intermud router. Note that this setting will persist across reboots.");
	return 1;
    }

    if(member_array(arg,yesbools) != -1){
	if(member_array(yesline,load_lines) != -1) write("Persistent router activation is already enabled.");
	else {
	    write("Enabling persistent router activation.");
	    foreach(string line in load_lines){
		if(line == noline) line = yesline;
		ret_arr += ({ line });
	    }
	    if(member_array(yesline,ret_arr) == -1){
		ret_arr += ({ yesline });
	    }
	    unguarded( (: cp(CFG_PRELOAD,"/secure/save/backup/preload."+time()) :) );
	    ret_string = implode(ret_arr,"\n")+"\n";
	    ret_string = replace_string(ret_string,"\n\n","\n");
	    unguarded( (: write_file(CFG_PRELOAD,ret_string,1) :) );
	}
	if(find_object(ROUTER_D)) write("The intermud router is already running.");
	else {
	    write("Starting the intermud router.");
	    load_object(ROUTER_D);
	}

	ret_string = "";
	write("The intermud router is enabled.");
	return 1;
    }

    if(member_array(arg,nobools) != -1){

	if(member_array(noline,load_lines) != -1) write("Persistent router activation is already disabled.");
	else {
	    write("Disabling persistent router activation.");
	    foreach(string line in load_lines){

		if(line == yesline) line = noline;
		ret_arr += ({ line });
	    }
	    if(member_array(noline,ret_arr) == -1){
		ret_arr += ({ noline });
	    }
	    unguarded( (: cp(CFG_PRELOAD,"/secure/save/backup/preload."+time()) :) );
	    ret_string = implode(ret_arr,"\n")+"\n";
	    ret_string = replace_string(ret_string,"\n\n","\n");
	    unguarded( (: write_file(CFG_PRELOAD,ret_string,1) :) );

	}
	if(!find_object(ROUTER_D)) write("The intermud router is not running.");
	else {
	    write("Stopping the intermud router.");
	    find_object(ROUTER_D)->eventDestruct();
	}
	ret_string = "";
	write("The intermud router is disabled.");
	return 1;
    }
}

varargs static int ModIntermud(string which, string arg){
    validate();
    write("Which: "+which+" arg: "+arg);
    if(arg == "restrict"){
	write("Restricting intermud...");
	flush_messages(this_player());
	foreach(string element in config){
	    if(grepp(element, "RESTRICTED_INTERMUD")) element = "#define RESTRICTED_INTERMUD      1";
	    config2 += ({ element });
	}
	CompleteConfig();
	reload(INTERMUD_D,0,1);
	return 1;
    }

    if(arg == "reset"){
	object ob = find_object(INTERMUD_D);
	write("Purging all intermud data (including router password!). Previous data file saved to /secure/save/backup/ .");
	if(!ob){
	    if(file_exists(SAVE_INTERMUD+".o")) rename(SAVE_INTERMUD+".o", "/secure/save/backup/intermud."+time());
	}
	else {
	    if(file_exists(SAVE_INTERMUD+".o")) rename(SAVE_INTERMUD+".o", "/secure/save/backup/intermud."+time());
	    reload(INTERMUD_D,0,1);
	}
	config2 = config;
	CompleteConfig();
	return 1;
    }

    if(arg == "unrestrict"){
	write("Unrestricting intermud...");
	flush_messages(this_player());
	foreach(string element in config){
	    if(grepp(element, "RESTRICTED_INTERMUD")) element = "#define RESTRICTED_INTERMUD      0";
	    config2 += ({ element });
	}
	CompleteConfig();
	reload(INTERMUD_D,0,1);
	return 1;
    }

    if(member_array(arg,bools) == -1){
	write("Please try again, indicating whether you want to enable, disable, restrict, unrestrict, or reset intermud.");
	return 1;
    }

    if(member_array(arg,yesbools) != -1){
	write("Enabling intermud...");
	flush_messages(this_player());
	foreach(string element in config){
	    if(grepp(element, "DISABLE_INTERMUD")) element = "#define DISABLE_INTERMUD         0";
	    config2 += ({ element });
	}
	CompleteConfig();
	reload(INTERMUD_D,0,1);
	return 1;
    }

    if(member_array(arg,nobools) != -1){
	write("Disabling intermud...");
	flush_messages(this_player());
	foreach(string element in config){
	    if(grepp(element, "DISABLE_INTERMUD")) element = "#define DISABLE_INTERMUD         1";
	    config2 += ({ element });
	}
	CompleteConfig();
	reload(INTERMUD_D,0,1);
	return 1;
    }

    return 1;
}

static int ProcessOther(string which, string arg){
    int junk;
    validate();

    if(sscanf(arg,"%d",junk) != 1){
	write("This parameter requires an integer as an argument.");
	return 1;
    }

    foreach(string element in config){
	if(grepp(element, which)){
	    string s1, s2, s3;
	    if(sscanf(element,"#define %s %s",s1,s2) != 2){
		write("Major problem. You should revert to a backup of config.h immediately.");
		return 1;
	    }
	    s3 = trim(s2);
	    s2 = replace_string(s2,s3,arg);
	    element = "#define "+s1+" "+s2;
	}
	config2 += ({ element });
    }
    CompleteConfig();
    if(which == "LOCAL_TIME" || which == "EXTRA_TIME_OFFSET" || which == "GMT_OFFSET"){
	reload("/secure/sefun/local_time",0,1);
	reload("/secure/sefun/time",0,1);
	reload("/secure/sefun/timestamp",0,1);
	reload("/secure/sefun/sefun",0,1);
	reload("/daemon/time",0,1);
	reload("/cmds/creators/people",0,1);
	reload("/cmds/players/date",0,1);
	reload("/cmds/players/nextreboot",0,1);
	reload("/cmds/players/version",0,1);
	write("This configuration change will require a few minutes to take effect completely.");
    }
    if(which == "GLOBAL_MONITOR") reload(SNOOP_D,0,1);
    if(which == "IDLE_TIMEOUT" || which == "MAX_NEWBIE_LEVEL"){ 
	reload(LIB_CREATOR,1,1);
	write("This configuration will take effect for each user the next time they log in.");
	return 1;
    }
    return 1;
}

static int ProcessString(string which, string arg){
    validate();

    foreach(string element in config){
	if(grepp(element, which)){
	    string s1, s2, s3;
	    if(sscanf(element,"#define %s %s",s1,s2) != 2){
		write("Major problem. You should revert to a backup of config.h immediately.");
		return 1;
	    }
	    s3 = trim(s2);
	    s2 = replace_string(s2,s3,"\""+arg+"\"");
	    element = "#define "+s1+" "+s2;
	}
	config2 += ({ element });
    }
    CompleteConfig();
    return 1;
}

static int ProcessModal(string which, string arg){
    int junk;
    validate();
    if(member_array(arg,yesbools) != -1) arg = "1";
    if(member_array(arg,nobools) != -1) arg = "0";
    if(sscanf(arg,"%d",junk) != 1){
	write("This parameter is a modal or quasi-modal. I have a hunch it requires an integer as an argument, or at least a \"yes\" or \"no\".");
	return 1;
    }
    if(member_array(upper_case(which),keywords) != -1){
	which = upper_case(which);
    }
    else switch(lower_case(which)){
    case "autowiz" : which = "AUTO_WIZ";break;
    case "locked" : which = "MUD_IS_LOCKED";break;
    case "localtime" : which = "LOCAL_TIME";break;
    case "justenglish" : which = "ENGLISH_ONLY";break;
    case "justhumans" : which = "HUMANS_ONLY";break;
    case "encumbrance" : which = "ENABLE_ENCUMBRANCE";break;
    case "pk" : which = "PLAYER_KILL";break;
    case "compat"  : which = "COMPAT_MODE";break;
    case "retain" : which = "RETAIN_ON_QUIT";break;
    case "defaultparse" : which = "DEFAULT_PARSING";break;
    case "disablereboot" : which = "DISABLE_REBOOTS";break;
    case "exitsbare" : which = "BARE_EXITS";break;
    case "matchcommand" : which = "COMMAND_MATCHING";break;
    case "matchobject" : which = "OBJECT_MATCHING";break;
    default : break;
    }
    foreach(string element in config){
	if(grepp(element, which)){
	    string s1, s2, s3;
	    if(sscanf(element,"#define %s %s",s1,s2) != 2){
		write("Major problem. You should revert to a backup of config.h immediately.");
		return 1;
	    }
	    s3 = trim(s2);
	    s2 = replace_string(s2,s3,arg);
	    element = "#define "+s1+" "+s2;
	}
	config2 += ({ element });
    }
    CompleteConfig();
    if(which == "DEFAULT_PARSING" || which == "ENABLE_ENCUMBRANCE" ||
      which == "BARE_EXITS" || which == "COMMAND_MATCHING"){ 
	reload(LIB_CREATOR,1,1);
	write("This configuration will take effect for each user the next time they log in.");
	return 1;
    }
    if(which == "RETAIN_ON_QUIT" || which == "OBJECT_MATCHING") 
	write("To make this configuration take effect, reboot the mud.");
    return 1;
}

int ProcessService(string which, string what){
    int port_offset, type;
    string sclass;
    switch(which){
    case "hftp": port_offset=OFFSET_HFTP;sclass="/secure/lib/net/h_ftpd";type=1;break;
    case "ftp": port_offset=OFFSET_FTP;sclass="/secure/lib/net/ftp";type=1;break;
    case "http": port_offset=OFFSET_HTTP;sclass="/secure/lib/net/http";type=3;break;
    case "rcp": port_offset=OFFSET_RCP;sclass="/secure/lib/net/remote";type=1;break;
    case "oob": port_offset=OFFSET_OOB;sclass="/secure/lib/net/oob";type=0;break;
    }
    switch(what){
    case "add": INET_D->AddService(which,port_offset, sclass, type);break;
    case "remove": INET_D->RemoveService(which);break;
    case "start": INET_D->eventStartServer(which);break;
    case "restart": INET_D->eventRestartServer(which,1);break;
    case "stop": INET_D->eventStopServer(which);break;
    }
    if(which == "oob"){
	if( what == "start" || what == "restart")
	    reload(OOB_D);
	if( what == "stop" )
	    OOB_D->eventDestruct();
    }
    write("Done.");
    return 1;
}

int ProcessInet(string which, string arg){
    int sub;
    string preloads = read_file(CFG_PRELOAD);
    string *load_lines = explode(preloads,"\n");
    string *ret_arr = ({});
    string yesline = "/secure/daemon/inet";
    string noline = "#/secure/daemon/inet";
    validate();
    if(!arg) arg = "status";
    if(which != "inet"){
	sub = 1;
	if(!find_object(INET_D)){
	    write("The inet service is not running. Please type: mudconfig inet start");
	    write("Or: mudconfig inet enable");
	    write("Then retry your command.");
	    return 1;
	}
    }
    else {
	if(member_array(arg,yesbools) != -1){
	    if(member_array(yesline,load_lines) != -1) {
		write("Persistent inet activation is already enabled.");
		return 1;
	    }
	    else {
		write("Enabling persistent inet activation.");
		foreach(string line in load_lines){
		    if(line == noline) line = yesline;
		    ret_arr += ({ line });
		}
		if(member_array(yesline,ret_arr) == -1){
		    ret_arr += ({ yesline });
		}
		unguarded( (: cp(CFG_PRELOAD,"/secure/save/backup/preload."+time()) :) );
		ret_string = implode(ret_arr,"\n")+"\n";
		ret_string = replace_string(ret_string,"\n\n","\n");
		unguarded( (: write_file(CFG_PRELOAD,ret_string,1) :) );
		write("The inet service is enabled.");
		arg = "start";
	    }
	}

	if(member_array(arg,nobools) != -1){
	    if(member_array(noline,load_lines) != -1){
		write("Persistent inet activation is already disabled.");
		return 1;
	    }
	    else {
		write("Disabling persistent inet activation.");
		foreach(string line in load_lines){

		    if(line == yesline) line = noline;
		    ret_arr += ({ line });
		}
		if(member_array(noline,ret_arr) == -1){
		    ret_arr += ({ noline });
		}
		unguarded( (: cp(CFG_PRELOAD,"/secure/save/backup/preload."+time()) :) );
		ret_string = implode(ret_arr,"\n")+"\n";
		ret_string = replace_string(ret_string,"\n\n","\n");
		unguarded( (: write_file(CFG_PRELOAD,ret_string,1) :) );
		write("The inet service is disabled.");
		arg = "stop";
	    }
	}

	if(arg == "start"){
	    if(find_object(INET_D)) write("The inet service is already running.");
	    else {
		write("Starting the inet service.");
		load_object(INET_D);
	    }
	}
	else if(arg == "stop"){
	    if(!find_object(INET_D)) write("The inet service is already stopped.");
	    else {
		write("Stopping the inet service.");
		find_object(INET_D)->eventDestruct();
	    }
	}
	else if(arg == "restart"){
	    if(find_object(INET_D)) {
		write("The inet service is running. Stopping it now...");
		find_object(INET_D)->eventDestruct();
	    }
	    write("Starting the inet service.");
	    load_object(INET_D);
	}
	else if(arg == "status"){
	    if(member_array(yesline,load_lines) != -1){
		write("Persistent inet activation is enabled.");
	    }
	    else {
		write("Persistent inet activation is disabled.");
	    }

	    if(!find_object(INET_D)){
		write("The inet service is not running");
	    }
	    else{
		string *servkeys, *servkeys2;
		string subret = "The following services are available: "; 
		string subret2 = "The following services are running: "; 
		write("The inet service is running");
		if(sizeof(servkeys = keys(INET_D->GetServices()))){
		    subret += implode(servkeys,", ")+".\n";
		}
		else subret = "";
		if(sizeof(servkeys2 = keys(INET_D->GetServers()))){
		    subret2 += implode(servkeys2,", ")+".\n";
		}
		else subret2 = "";
		write(subret+subret2);
		return 1;
	    }
	    ret_string = "";
	    write("Done.");
	    return 1;
	}
	else write("Unsupported inet subcommand.");
	return 1;
    }
    if(arg == "enable"){
	if(INET_D->GetService(which)){
	    write("The "+which+" service is already enabled. Perhaps you mean to start or restart it?");
	    return 1;
	}
	ProcessService(which,"add");
	return 1;
    }
    if(arg == "disable"){
	if(!(INET_D->GetService(which))){
	    write("The "+which+" service is already disabled.");
	    return 1;
	}
	ProcessService(which,"remove");
	return 1;
    }

    if(arg == "start"){
	if(INET_D->GetServer(which)){
	    write("The "+which+" service is already started. Perhaps you mean to restart it?");
	    return 1;
	}
	if(!(INET_D->GetService(which))){
	    write("The "+which+" service has been disabled or is not available.");
	    return 1;
	}
	ProcessService(which,"start");
	return 1;
    }
    if(arg == "stop"){
	if(!(INET_D->GetService(which))){
	    write("The "+which+" service is already disabled and therefore not running.");
	    return 1;
	}
	if(!(INET_D->GetServer(which))){
	    write("The "+which+" service is already stopped.");
	    return 1;
	}
	ProcessService(which,"stop");
	return 1;
    }

    if(arg == "restart"){
	ProcessService(which,"restart");
	return 1;
    }
    if(arg == "status"){
	write("The "+which+" service is "+(INET_D->GetService(which) ? "enabled." : "disabled."));
	if(INET_D->GetService(which))
	    write("The "+which+" service is "+(INET_D->GetServer(which) ? "running." : "stopped."));
	return 1;
    }
    write("Unsupported mudconfig inet service subcommand.");
    return 1;
}

void help() {
    message("help",
      "Syntax: mudconfig PARAMETER VALUE \n\n"
      "Modifies various system settings.\n"
      "Examples: \n"
      "\nmudconfig autowiz [ yes | no ]"
      "\nmudconfig locked [ yes | no ]"
      "\nmudconfig justenglish [ yes | no ]"
      "\nmudconfig justhumans [ yes | no ]"
      "\nmudconfig encumbrance [ yes | no ]"
      "\nmudconfig pk [ yes | no ]"
      "\nmudconfig compat [ yes | no ]"
      "\nmudconfig retain [ yes | no ]"
      "\nmudconfig defaultparse [ yes | no ]"
      "\nmudconfig disablereboot [ yes | no ]"
      "\nmudconfig matchcommand [ yes | no ]"
      "\nmudconfig matchobject [ yes | no ]"
      "\nmudconfig exitsbare [ yes | no ]"
      "\nmudconfig localtime [ yes | no ]"
      "\nmudconfig offset <offset from gmt in seconds>"
      "\nmudconfig extraoffset <offset from GMT in hours>"
      "\nmudconfig maxcommands <max number of commands per second>"
      "\nmudconfig maxip <max connections per IP>"
      "\nmudconfig monitor <monitoring level, 0 to 2>"
      "\nmudconfig newbielevel <max newbie level>"
      "\nmudconfig resets <interval between resets>"
      "\nmudconfig router [ enable | disable ]"
      "\nmudconfig startroom <filename of start room>"
      "\nmudconfig email <the admin's email address>"
      "\nmudconfig liveupgrade <the default liveupgrade mud's name>"
      "\nmudconfig hostip <the computer's ip address (eg 111.222.333.444)>"
      "\nmudconfig intermud [ enable | disable | restrict | unrestrict | reset ]"
      "\nmudconfig inet [ enable | disable | start | stop | restart | status ]"
      "\nmudconfig ftp [ enable | disable | start | stop | restart | status ]"
      "\nmudconfig hftp [ enable | disable | start | stop | restart | status ]"
      "\nmudconfig http [ enable | disable | start | stop | restart | status ]"
      "\nmudconfig rcp [ enable | disable | start | stop | restart | status ]"
      "\nmudconfig oob [ enable | disable | start | stop | restart | status ]"
      "\n\nSee also: admintool", this_player()
    );
}
