#include <lib.h>
#include <privs.h>
#include <save.h>
#include <rooms.h>
#include <config.h>

inherit LIB_DAEMON;

int SnoopClean();
int RemoveWatcher(string watcher, mixed target);

string *snooped = ({});
object *snoopers = ({});
string *monitored  = ({}); 
mapping Watchers = ([]);
int count = 0;
int just_loaded = 1;
object *prevusers;

void eventLoadRogues();

static void create() {
    daemon::create();
    debug("SNOOP_D restarted.","red");
    if( file_size( SAVE_SNOOP __SAVE_EXTENSION__ ) > 0 )
	unguarded( (: restore_object, SAVE_SNOOP, 1 :) );
    SetNoClean(1);
    SnoopClean();
    set_heart_beat(1);
    if(GLOBAL_MONITOR == 0 && sizeof(monitored)){
	call_out( (: eventLoadRogues :), 1);
    }
    snoopers = filter(objects(), (: base_name($1) == "/secure/obj/snooper" :) );
    prevusers = users();
}

void RegisterSnooper(){
    object registrant = previous_object();
    if(base_name(registrant) == "/secure/obj/snooper" &&
      member_array(registrant,snoopers) == -1) snoopers += ({ registrant });
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
}

void UnregisterSnooper(){
    object registrant = previous_object();
    if(base_name(registrant) == "/secure/obj/snooper" &&
      member_array(registrant,snoopers) != -1) snoopers -= ({ registrant });
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
}

void eventLoadRogues(){
    if( !((int)master()->valid_apply(({ PRIV_SECURE }))) ) return;
    foreach(string rogue in monitored) this_object()->CheckBot(rogue);
    snoopers = filter(objects(), (: base_name($1) == "/secure/obj/snooper" :) );
}

int CheckBot(string str){
    object cloan, foo;
    int allset, err, already_watched = 0;
    string *immune;
    string name;

    if(!str) str = "foo";
    str = lower_case(str);
    foo = find_player(str);
    if(sizeof(snoopers)){
	foreach(object snoopbox in snoopers){
	    if(clonep(snoopbox) ) {
	    }
	    else snoopers -= ({snoopbox});
	    if(snoopbox && snoopbox->GetSnooped() && snoopbox->GetSnooped() == str) {
		already_watched = 1;
	    }
	    if(snoopbox->GetSnooped() && (!find_player(snoopbox->GetSnooped()) ||!(snoopbox->GetSnooped()))) {
	    }
	}
    }
    if(!already_watched && foo && (GLOBAL_MONITOR > 0 || member_array(str, monitored) != -1 || member_array(str, snooped) != -1 )){
	if(archp(find_player(str)) && GLOBAL_MONITOR == 2) return 0;
	cloan=new("/secure/obj/snooper");
	cloan->eventStartSnoop(str);
    }
    err = catch(unguarded( (: save_object, SAVE_SNOOP, 1 :) ));
    return 1;
}

void CheckSnooped(){
    object *lusers = users();
    just_loaded = 0;
    foreach(object user in lusers){
	CheckBot(user->GetKeyName());
    }
    prevusers = lusers;
}

int SnoopClean(){
    object *voyeurs = users();
    snoopers = filter(objects(), (: base_name($1) == "/secure/obj/snooper" :) );
    if(sizeof(voyeurs)){
	foreach(object perv in voyeurs){
	    if(!snooperp(perv) && !archp(perv)){
		RemoveWatcher(perv->GetKeyName(), "all");
	    }
	}
    }
    if(sizeof(snoopers)){
	foreach(object snoopbox in snoopers){
	    if(snoopbox && !clonep(snoopbox) ) {
		snoopers -= ({snoopbox});
	    }
	    else {
		object subject;
		string dude = snoopbox->GetSnooped();
		if(dude) subject = find_player(dude);
		if(!dude || !subject || !query_snooping(snoopbox) || 
		  (member_array(dude,snooped) == -1 && member_array(dude,monitored) == -1 && GLOBAL_MONITOR < 1 )){
		    snoopbox->eventDestruct();
		    snoopers -= ({snoopbox});
		}
	    }
	}
    }
    return 1;
}

int eventDestruct(){
    if( !((int)master()->valid_apply(({ "SECURE" }))) )
	error("Illegal attempt to destruct snoop: "+get_stack()+" "+identify(previous_object(-1)));
    return ::eventDestruct();
}


void heart_beat(){
    count++;

    if( !(count % 5) ) CheckSnooped();

    if( !(count % 10)) {
	foreach(object snoopbox in snoopers){
	    if(!snoopbox) snoopers -= ({ snoopbox });
	}
	SnoopClean();
    }
    if(count > 100) count = 0;
}

void reset(){
    if(query_heart_beat(this_object()) < 1) set_heart_beat(60);
    CheckSnooped();
}

int GetSnoop(string target, string msg){
    if(target != "cratylus") {
    }
    if(base_name(previous_object()) != "/secure/obj/snooper") return 0;
    else if(sizeof(Watchers[target])) {
	foreach(string watcher in Watchers[target]){
	    tell_player(watcher, target+"% "+msg);
	}
    }
    else Watchers[target] = ({});
    return 1;
}

int AddWatcher(string watcher, string target){
    if(!valid_snoop(find_player(watcher), find_player(target))) {
	return 0;
    }
    if(Watchers[target] && member_array(watcher,Watchers[target]) != -1){
	return 0;
    }
    if(!Watchers[target] || !sizeof(Watchers[target])) {
	Watchers[target] = ({ watcher });
    }
    else {
	Watchers[target] += ({ watcher });
    }
    if(member_array(target,snooped) == -1) snooped += ({target});
    CheckBot(target);
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
    return 1;
}

int RemoveWatcher(string watcher, mixed target){
    string *targs = ({});
    if(this_player() && interactive(this_player())){
	if(this_player()->GetKeyName() != watcher && !archp(this_player())) return 0;
    }
    else return 0;
    if(stringp(target)){
	if(target == "all"){
	    foreach(string key, string *sap in Watchers){ 
		if(member_array(watcher,sap) != -1) targs += ({ key });
	    }
	}
	else targs = ({ target });
    }
    else if(arrayp(target)) targs = target;
    else return 0;
    foreach(string subtarg in targs){
	if(!Watchers[subtarg] || !sizeof(Watchers[subtarg])) return 0;
	else if(member_array(watcher, Watchers[subtarg]) != -1) 
	    Watchers[subtarg] -= ({ watcher });
	if((!Watchers[subtarg] || !sizeof(Watchers[subtarg])) &&
	  member_array(subtarg, monitored) == -1) {
	    foreach(object snoopbox in snoopers){
		if(snoopbox->GetSnooped() == subtarg) snoopbox->eventDestruct(); 
	    }
	}
	if(!Watchers[subtarg] || !sizeof(Watchers[subtarg])) {
	    snooped -= ({subtarg});
	    if(Watchers[subtarg]) map_delete(Watchers, subtarg);
	}
    }
    CheckBot("adsfgrertgrsgnfxmy");
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
    return 1;
}

int AddMonitor(string requestor, string target){
    if(member_array(target, monitored) == -1) monitored += ({ target });
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
    CheckBot(target);
    return 1;
}

int RemoveMonitor(object requestor, string target){
    if(!archp(requestor)) return 0;
    monitored -= ({ target });
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
    if(Watchers[target] && sizeof(Watchers[target])) return 1;
    foreach(object snoopbox in snoopers){
	if(snoopbox->GetSnooped() == target) snoopbox->eventDestruct(); 
    }
    if(Watchers[target] && !sizeof(Watchers[target])) map_delete(Watchers, target);
    CheckBot("asreg54eqwhtrbsf");
    unguarded( (: save_object, SAVE_SNOOP, 1 :) );
    return 1;
}

int ReportLinkDeath(string str){
    if(!find_player(str)){
	foreach(object snoopbox in snoopers){
	    if(snoopbox->GetSnooped() == str) snoopbox->eventDestruct(); 
	}
	map_delete(Watchers, str);
	unguarded( (: save_object, SAVE_SNOOP, 1 :) );
	return 1;
    }
    return 0;
}

int ReportReconnect(string str){
    if(base_name(previous_object()) == LIB_CONNECT){
	foreach(object snoopbox in snoopers){
	    if(snoopbox->GetSnooped() == str) snoopbox->eventDestruct(); 
	}
	map_delete(Watchers, str);
	CheckBot(str);
	unguarded( (: save_object, SAVE_SNOOP, 1 :) );
	return 1;
    }
    return 0;
}

string Report(){
    string ret = "";
    mapping TmpWatchers = ([]);
    if( !((int)master()->valid_apply(({ PRIV_SECURE }))) ){
	return 0;
    }
    foreach(mixed key, mixed val in Watchers){
	if(sizeof(val))
	    TmpWatchers[key] = val;
    }
    ret+="Watchers: "+identify(TmpWatchers)+"\n";
    ret+="snoopers: "+identify(snoopers)+"\n";
    ret+="snooped: "+identify(snooped)+"\n";
    ret+="monitored: "+identify(monitored)+"\n";
    return ret;
}

