/*    /daemon/chat.c
 *    from the Nightmare 3.2 Mudlib
 *    daemon to handle all mud chat channels
 *    created by Descartes of Borg 931220
 */

#include <lib.h>
#include <config.h>
#include <pov.h>
#include <daemons.h>
#include <origin.h>
#include <message_class.h>
#include "include/chat.h"

inherit LIB_DAEMON;

string suspect,site,plainmsg,chan,foo,bar,baz;
static private mapping Channels;
static private mapping chanlast;

static private string *local_chans = ({"newbie","cre","gossip","admin","error",
  "priest", "mage", "explorer", "thief", "fighter", "death", "connections" });
static string *syschans = ({ "death", "connections" });


static void create() {
    object pl;
    string *tmp_arr = ({});
    daemon::create();
    SetNoClean(1);
    Channels = ([]);
    foreach(string kanal in local_chans + syschans){
	if( !Channels[kanal] ) Channels[kanal] = ({});
    }
    foreach(pl in users()) {
	string *chans;
	string channel;

	if( pl && !(chans = (string *)pl->GetChannels()) ) continue;
	foreach(channel in chans) {
	    if( !Channels[channel] ) Channels[channel] = ({});
	    Channels[channel] = distinct_array(Channels[channel] + ({ pl }));
	}
    }
    foreach( string channel in local_chans ){
	tmp_arr += ({ channel + "emote" });
    }

    local_chans += tmp_arr;
}

string decolor(string str){
    string s1 = "", s2, s3, test;
    int tmp = 2;
    if(sscanf(str,"%s<%s>%s",s1,s2,s3) != 3) 
	tmp = sscanf(str,"<%s>%s",s2,s3);
    if(tmp != 2) return str;
    else {
	test = s1+"<"+s2+">%^RESET%^"+strip_colours(s3);
	return test;
    }
}

varargs int CanListen(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
} 

varargs int CanTalk(object who, string canal){
    if(!RESTRICTED_INTERMUD) return 1;
    if(canal && member_array(canal, local_chans) != -1) return 1;
    else return imud_privp(who);
}

string *eventRegisterMember(string *chans) {
    string *tmp;
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return ({});
    tmp = ({});
    foreach(channel in chans) {
	/* just check out for secure channels */
	switch(channel) {
	case "admin":
	    if( !archp(ob) ) break;
	case "cre": case "intercre": case "intergossip":
	    if( !creatorp(ob) ) break;
	default:
	    if( !Channels[channel]) Channels[channel] = ({});
	    Channels[channel] = distinct_array(Channels[channel] + ({ ob }));
	    tmp += ({ channel });
	}
    }
    return tmp;
}

string *eventRemoveMember(string *chans) {
    object ob;
    string channel;

    if( !living(ob = previous_object()) ) return({});
    foreach(channel in chans) {
	if( !Channels[channel] ) continue;
	else Channels[channel] -= ({ ob });
	if( !sizeof(Channels[channel]) ) map_delete(Channels, channel);
    }
    return chans;
}

int cmdLast(string feep){

    if(!chanlast||!Channels[feep]||member_array(this_player(), Channels[feep])==-1){

	this_player()->eventPrint(mud_name() + " does not have that channel on record.", MSG_ERROR);
	return 1;
    }
    if(!sizeof(chanlast[feep]))
    {
	this_player()->eventPrint("That channel has no backlog.", MSG_ERROR);
	return 1;
    }
    if(!CanListen(this_player(),feep)){
	write("You lack privileges to that channel.");
	return 1;
    }
    this_player()->eventPrint(implode(chanlast[feep], "\n"));
    return 1;
}

varargs int eventAddLast(string feep, string str, string pchan, string pmsg, string pwho)
{
    chan=feep;
    if(!chanlast)
	chanlast=([]);
    if(!sizeof(chanlast[chan]))
	chanlast[chan] = ({});
    if(sizeof(chanlast[chan]) == 50)
	chanlast[chan] = chanlast[chan][1..sizeof(chanlast[chan])];
    chanlast[chan] += ({ str });
    if(!pchan || pchan == "") pchan = "foo";
    plainmsg = "bar";
    if(pchan) plainmsg = "<" + pchan + "> ";
    if(pmsg) plainmsg += pmsg;
    if(pwho && pwho !="") plainmsg = pwho+" "+plainmsg;
    if(pchan && pchan != "admin"){
	chan = GetLocalChannel(chan);
	unguarded( (: write_file("/log/chan/"+chan,"["+timestamp()+"] "+plainmsg+"\n") :) );
	if( file_size("/log/chan/"+chan) > 200000) {
	    unguarded( (: rename("/log/chan/"+chan,"/log/chan/"+chan+"."+timestamp() ) :) );
	}
    }
    else {
	unguarded( (: write_file("/secure/log/"+chan,"["+timestamp()+"] "+plainmsg+"\n") :) );
	if( file_size("/secure/log/"+chan) > 200000) {
	    unguarded( (: rename("/secure/log/"+chan,"/secure/log/"+chan+"."+timestamp() ) :) );
	}
    }
    return 1;
}

int cmdChannel(string verb, string str) {
    string varb, msg, name, rc, target, targetkey, target_msg;
    object ob = 0;
    int emote;

    if(first(str,1) == ":" && 
      (member_array(str[1..1], ({ "Q", "O", "P", "D", "I", "X" })) == -1 &&
	alphap(str[1..1]) && sizeof(str) > 3)){
	if(!grepp(verb,"emote")) verb += "emote";
	str = trim(replace_string(str,":","",1));
    }

    if(grepp(verb, "emote")) varb = replace_string(verb,"emote","");
    else if(last(verb, 1) == ":") varb = replace_string(verb,":","");
    else varb = verb;

    if( verb == "list" ) {
	string *who;
	string ch, mud;

	if( !str ) {
	    return 0;
	}
	if( sscanf(str, "%s@%s", ch, mud) == 2 ) {
	    mud = trim(mud);
	    if(!alphap(last(mud,1))) mud = truncate(mud,1);
	    if( !Channels[ch] ) {
		return 0;
	    }
	    if( member_array(this_player(), Channels[ch]) == -1 ) {
		return 0;
	    }
	    if( ch == (ch = GetRemoteChannel(ch)) ) {
		if(!creatorp(this_player())){
		    write("Remote channel information is not available to players.");
		    return 1;
		}
	    }
	    if( !(mud = (string)INTERMUD_D->GetMudName(mud)) ) {
		this_player()->eventPrint(mud_name() + " is not aware of " 
		  "such a place.", MSG_ERROR);
		return 1;
	    }
	    if(!CanTalk(this_player(),verb)){
		write("You lack privileges to that channel.");
		return 1;
	    }
	    SERVICES_D->eventSendChannelWhoRequest(ch, mud);
	    this_player()->eventPrint("Remote listing request sent.",
	      MSG_SYSTEM);
	    return 1;
	}
	else ch = str;
	if( !Channels[ch] ) {
	    return 0;
	}
	if( member_array(this_player(), Channels[str]) == -1 ) {
	    return 0;
	}
	who = GetChannelList(str);
	msg = "Online: " + implode(who, "   ");
	this_player()->eventPrint(msg, MSG_SYSTEM);
	return 1;
    }
    if(!CanTalk(this_player(),verb)){
	write("You lack privileges to that channel.");
	return 1;
    }
    if( !str || str == "" ) {
	this_player()->SetBlocked(verb);
	return 1;
    }
    if(member_array(verb, syschans) != -1) {
	write("This is not a channel for chatting.");
	return 1;
    }
    if(this_player()->GetProperty("gag")){
	write("You have gag mode enabled. Type: 'gag off' to talk on channels.");
	return 1;
    }

    if( !Channels[verb] ) {
	if( sscanf(verb, "%semote", verb) || sscanf(verb, "%s:", verb) ) {
	    string emote_cmd, remains;
	    mixed array msg_data;
	    int i;
	    if( !Channels[verb] ) {
		return 0;
	    }
	    rc = GetRemoteChannel(verb);
	    sscanf(str, "%s %s", emote_cmd, remains);
	    if( !emote_cmd ) {
		emote_cmd = str;
		remains = 0;
	    }
	    if( !remains ) {
		msg_data = SOUL_D->GetChannelEmote(emote_cmd, "");
		if( !msg_data ) {
		    str = "$N " + str;
		}
	    }

	    else {
		if( ob = find_living(target = convert_name(remains)) ) {
		    msg_data = SOUL_D->GetChannelEmote(emote_cmd, "LIV");
		    if( !msg_data ) {
			str = "$N " + str;
			target = 0;
		    }
		}
		else if( strsrch(target, "@") == -1 || rc == verb ) {
		    string array words = explode(remains, " ");
		    target = "";
		    for(i=0; i<sizeof(words); i++) {
			target += lower_case(words[i]);
			if( ob = find_living(target) ) {
			    if( i < sizeof(words)-1 ) {
				remains = implode(words[(i+1)..], " ");
			    }
			    else {
				remains = 0;
			    }
			    msg_data = SOUL_D->GetChannelEmote(emote_cmd,
			      "LIV STR",
			      remains);
			    break;
			}
		    }
		    if( !ob ) {
			msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR",
			  remains);
			target = 0;
		    }
		    if( !msg_data ) {
			str = "$N " + str;
			target = 0;
		    }
		}
		else if( rc != verb ) {
		    string array words;
		    i = strsrch(remains, "@", -1);
		    if( i >= strlen(remains)-1 ) {
			msg_data = SOUL_D->GetChannelEmote(emote_cmd, "STR",
			  remains);
			target = 0;
		    }
		    else {
			string mud;

			words = explode(remains[(i+1)..], " ");
			target = remains[0..i];
			remains = "";
			while(sizeof(words)) {
			    mud = implode(words, " ");
			    mud = trim(mud);
			    if(!alphap(last(mud,1))) mud = truncate(mud,1);
			    mud = INTERMUD_D->GetMudName(lower_case(mud));
			    if( mud ) {
				target += mud;
				break;
			    }
			    if( remains == "" ) {
				remains = words[<1];
			    }
			    else {
				remains = words[<1] + " " + remains;
			    }
			    words = words[0..<2];
			}
			if( !mud ) {
			    msg_data = SOUL_D->GetChannelEmote(emote_cmd,
			      "STR", remains);
			    target = 0;
			}
			else {
			    if( trim(remains) == "" ) {
				msg_data = SOUL_D->GetChannelEmote(emote_cmd,
				  "LIV");
			    }
			    else {
				msg_data = SOUL_D->GetChannelEmote(emote_cmd,
				  "LIV STR",
				  remains);
			    }
			    if( !msg_data ) {
				str = "$N " + str;
				target = 0;
			    }
			}
		    }
		}
	    }
	    if( msg_data ) {
		string sgen = this_player()->GetGender();
		string tgen = 0;

		if( ob ) {
		    target = ob->GetName();
		    tgen = ob->GetGender();
		}
		else if( target ) {
		    string user, mud;

		    sscanf(target, "%s@%s", user, mud);
		    targetkey = target;
		    tgen = SERVICES_D->GetRemoteGender(user, mud);
		    target = SERVICES_D->GetRemoteDisplayName(user, mud);
		    if( !target ) {
			target = capitalize(targetkey);
		    }
		}
		str = create_message(POV_OBSERVER, msg_data[0][0],
		  msg_data[0][1], "$N", sgen, "$O", tgen,
		  msg_data[1]);
		if( target ) {
		    target_msg = create_message(POV_TARGET, msg_data[0][0],
		      msg_data[0][1], "$N", sgen,
		      "$O", tgen, msg_data[1]);
		}
	    }
	    emote = 1;
	}
	else {
	    return 0;
	}
    }
    else {
	rc = GetRemoteChannel(verb);
    }
    if( member_array(this_player(), Channels[verb]) == -1 ) return 0;

    if( (int)this_player()->GetBlocked(verb) ) {
	if( (int)this_player()->GetBlocked("all") ) {
	    this_player()->eventPrint("You cannot chat while totally blocked.",
	      MSG_ERROR);
	    return 1;
	}
	this_player()->eventPrint("Turn this channel on to talk on it.", MSG_ERROR);
	return 1;
    }
    if( verb == "admin"  || verb == "cre") {
	if( !(name = (string)this_player()->GetCapName()) )
	    name = capitalize((string)this_player()->GetKeyName());
    }
    else name = (string)this_player()->GetName();
    if( target_msg ) {
	target_msg = replace_string(target_msg, "$O's", "your");
    }
    if(!grepp(str,"$N") && emote) str = "$N "+str;

    eventSendChannel(name, verb, str, emote, target, target_msg);
    if(member_array(GetRemoteChannel(verb),INTERMUD_D->GetChannels()) != -1){
	if( ob ) {
	    SERVICES_D->eventSendChannel(name, rc, str, emote, target,
	      target_msg);
	}
	else {
	    SERVICES_D->eventSendChannel(name, rc, str, emote, targetkey,
	      target_msg);          
	}
    }
    return 1;
}

varargs void eventSendChannel(string who, string ch, string msg, int emote,
  string target, string targmsg) {
    object channeler = find_player(lower_case(who));
    string pchan,pmsg;
    pchan=ch;
    if(!channeler) channeler = this_player();

    if(this_player() && this_player() != channeler) channeler = this_player();

    if(!strsrch(base_name(previous_object()), "/realms/") ||
      !strsrch(base_name(previous_object()), "/open/")) {
	return 0;
    }

    if(member_array(ch, syschans) != -1) {
	emote = 0;
    } 
    if(channeler){
	if(!CanTalk(channeler, ch) && member_array(ch, syschans) == -1){

	    return;
	}
    }

    if( file_name(previous_object()) == SERVICES_D) {
	ch = GetLocalChannel(ch);
	if( emote && sizeof(who)) msg = replace_string(msg, "$N", who);
    }
    else if( origin() != ORIGIN_LOCAL && previous_object() != master() &&
      file_name(previous_object()) != PARTY_D && member_array(ch, syschans) == -1){
	return;
    }
    if(!Channels[ch] && file_name(previous_object()) != SERVICES_D){
	return;
    }
    if( emote ) {
	object *obs;
	object ob;
	string this_msg, tmp;
	if( target && (ob = find_player(convert_name(target))) ) {
	    target = (string)ob->GetName();
	}
	switch(ch)
	{
	case "connections":
	    this_msg = "%^WHITE%^";
	    break;
	case "death":
	    this_msg = "%^RED%^";
	    break;
	case "cre":
	    this_msg = "%^GREEN%^";
	    break;
	case "admin":
	    this_msg = "%^MAGENTA%^";
	    break;
	case "dchat":
	    this_msg = "%^CYAN%^";
	    break;
	case "intergossip":
	    this_msg = "%^GREEN%^";
	    break;
	case "intercre":
	    this_msg = "%^YELLOW%^";
	    break;
	case "ds":
	    this_msg = "%^YELLOW%^";
	    break;
	case "newbie":
	    this_msg = "%^B_YELLOW%^";
	    break;
	case "gossip":
	    this_msg = "%^B_BLUE%^";
	    break;
	default:
	    this_msg = "%^BLUE%^";
	}
	this_msg += "<" + ch + ">%^RESET%^ ";
	msg = replace_string(msg, "$N", who);
	if( target ) {
	    msg = replace_string(msg, "$O", target);
	    targmsg = replace_string(targmsg, "$N", who);
	    targmsg = capitalize(replace_string(targmsg, "$O", "you"));
	}
	tmp = this_msg + msg;
	eventAddLast(ch, tmp, pchan, msg);
	if(Channels[ch]){
	    obs = filter(Channels[ch], (: $1 && !((int)$1->GetBlocked($(ch))) :));
	    foreach(object listener in obs) {
		int ignore;
		if(sscanf(who,"%s@%s",suspect,site) < 2) {
		    suspect = who;
		    site = "@"+mud_name();
		}
		else site = "@"+site;
		if( listener == ob ) continue;
		if(sizeof(listener->GetMuffed()))
		    foreach(string jerk in listener->GetMuffed()){
		    if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
		    if(jerk && lower_case(site) == lower_case(jerk)) ignore = 1;
		}
		if(listener->GetNoChanColors()) tmp = decolor(tmp);
		if(!ignore && CanListen(listener,ch) && !(listener->GetProperty("mute"))) 
		    listener->eventPrint(tmp, MSG_CHAN);
		ignore = 0;
	    }
	    if( member_array(ob, obs) != -1 ) {
		if( ob && !((int)ob->GetBlocked(ch)) ) {
		    int ignore;
		    tmp = this_msg + targmsg;
		    if(sizeof(ob->GetMuffed()))
			foreach(string jerk in ob->GetMuffed()){
			if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
			if(jerk && lower_case(site) == lower_case(jerk)) ignore = 1;
		    }
		    if(ob->GetNoChanColors()) tmp = decolor(tmp);
		    if(!ignore && CanListen(ob,ch)&& !(ob->GetProperty("mute"))) 
			ob->eventPrint(tmp, MSG_CHAN);
		    ignore = 0;
		}
	    }
	}
	suspect = "";
	site = "";
    }
    else {
	object *obs;
	string tmsg;

	tmsg = who + " ";

	switch(ch)
	{
	case "cre":
	    tmsg += "%^GREEN%^";
	    break;
	case "connections":
	    tmsg += "%^WHITE%^";
	    break;
	case "death":
	    tmsg += "%^RED%^";
	    break;
	case "admin":
	    tmsg += "%^MAGENTA%^";
	    break;
	case "dchat":
	    tmsg += "%^CYAN%^";
	    break;
	case "intergossip":
	    tmsg += "%^GREEN%^";
	    break;
	case "intercre":
	    tmsg += "%^YELLOW%^";
	    break;
	case "ds":
	    tmsg += "%^YELLOW%^";
	    break;
	case "newbie":
	    tmsg += "%^B_YELLOW%^";
	    break;
	case "gossip":
	    tmsg += "%^B_BLUE%^";
	    break;
	default:
	    tmsg += "%^BLUE%^";
	}
	tmsg += "<"+ch+">%^RESET%^ " + msg;
	pmsg = msg;
	msg = tmsg;
	eventAddLast(ch, msg, pchan, pmsg, who);
	if(Channels[ch]) {
	    obs = filter(Channels[ch], (: $1 && !((int)$1->GetBlocked($(ch))) :));
	    foreach(object ob in obs){
		int ignore;
		if(sscanf(who,"%s@%s",suspect,site) < 2) {
		    suspect = who;
		    site = "@"+mud_name();
		}
		else site = "@"+site;

		if(sizeof(ob->GetMuffed()))
		    foreach(string jerk in ob->GetMuffed()){
		    if(jerk && lower_case(suspect) == lower_case(jerk)) ignore = 1;
		    if(jerk && lower_case(site) == lower_case(jerk)) ignore = 1;
		}
		if(ob->GetNoChanColors()) msg = decolor(msg);
		if(!ignore && CanListen(ob,ch)&& !(ob->GetProperty("mute"))) 
		    ob->eventPrint(msg, MSG_CHAN);

		ignore = 0;
		suspect ="";
		site = "";
	    }
	}
    }
}

string *GetChannelList(string ch) {
    string *ret;
    object who;

    if( file_name(previous_object()) == SERVICES_D ) ch = GetLocalChannel(ch);
    else if( origin() != ORIGIN_LOCAL ) return ({});
    if( !Channels[ch] ) return ({});
    ret = ({});
    foreach(who in Channels[ch]) {
	if( !who || (int)who->GetInvis() || (int)who->GetBlocked(ch) )
	    continue;
	ret += ({ (string)who->GetName() });
    }
    return ret;
}

string *GetLocalChannels(){
    return copy(local_chans);
}

string GetLocalChannel(string ch) {
    switch(ch) {
    case "imud_code":
	return "intercre";

    case "imud_gossip":
	return "intergossip";

    case "dutch":
	return "dutch";

    case "dead_test4":
	return "ds_test";

    case "dead_souls":
	return "ds";

    }
    return ch;
}

string GetRemoteChannel(string ch) {
    switch(ch) {
    case "intercre": 
	return "imud_code";

    case "intergossip":
	return "imud_gossip";

    case "foundation":
	return "ie_flibcode";

    case "dutch":
	return "dutch";

    case "ds_test":
	return "dead_test4";

    case "ds":
	return "dead_souls";

    }
    return ch;
}

string *GetChannels() { return copy(keys(Channels)); }
string *GetSystemChannels() { return copy(syschans); }
