# Note: This is *not* a class ...

package uxmon;

use strict;
use Requester::Requester;

init();

sub init {
    (@uxmon::MOD = ()) if( ! @uxmon::MOD );
    (@uxmon::checkers = ()) if( ! @uxmon::checkers );
    (@uxmon::reporters = ()) if( ! @uxmon::reporters );
    (@uxmon::schedule = ()) if( ! @uxmon::schedule );
    (%uxmon::modules = ()) if( ! %uxmon::modules );
    ($uxmon::rule_file = "") if( ! $uxmon::rule_file );
    ($uxmon::old_rule_file_stat = 0) if( ! $uxmon::old_rule_file_stat );
    ($uxmon::hostname = $BigSister::common::hostname) if( ! $uxmon::hostname );
    %uxmon::joined_groups = ();
    $uxmon::leave_on_exit = 0;
    $uxmon::FQDN = 1;
    $uxmon::override_time = 0;
    %uxmon::report = ();
    %uxmon::comment = ();
    %uxmon::sort_comments = ();
    %uxmon::monitor_variables = ();
}



sub module_dir {
    my( $dir ) = @_;

    unshift( @uxmon::MOD, $dir );
}


sub rule_file {
    my( $file ) = @_;

    $uxmon::rule_file = $file;
}


sub leave_on_exit {
    my( $leave ) = @_;

    $uxmon::leave_on_exit = $leave;
}

sub drop_all_groups {
    my( $key, $nkey );

    foreach $key ( keys %uxmon::joined_groups ) {
	if( $key =~ /^\+/ ) {
	    $nkey = $key;
	    $nkey =~ s/^\+/\-/;
	    $uxmon::joined_groups{$nkey} = $uxmon::joined_groups{$key};
	    delete $uxmon::joined_groups{$key};
	}
    }
}

sub quit_gracefully {
    my( $key, $nkey, $reporter );

    print "quit_gracefully()\n" if( $BigSister::common::dl );
    Requester::Requester::preexit();
    if( $uxmon::leave_on_exit ) {
	print "dropping groups\n" if( $BigSister::common::dl > 2 );
	drop_all_groups;
	print "sending out leave messages\n" if( $BigSister::common::dl > 3 );
	foreach $reporter (@uxmon::reporters) {
	    $reporter->report( {}, {}, \%uxmon::joined_groups );
	}
	print "  back we are\n" if( $BigSister::common::dl > 3 );
    }
}



sub join_group {
    my( $name, @groups ) = @_;
    my( $joined );
    my $group;

    if( defined $uxmon::joined_groups{"-".$name} ) {
	$joined = $uxmon::joined_groups{"-".$name};
	foreach $group (@groups) {
	    @$joined = (grep( $group ne $_, @$joined ));
	}
    }
    unless( defined $uxmon::joined_groups{"+".$name} ) {
	$uxmon::joined_groups{"+".$name} = [];
    }
    $joined = $uxmon::joined_groups{"+".$name};
    foreach $group (@groups) {
	@$joined = (grep( $group ne $_, @$joined ), $group);
    }
    print "$name is in groups: ".join(" ",@$joined)."\n" if( $BigSister::common::dl>4 );
}

sub drop_group {
    my( $name, @groups ) = @_;
    my( $joined );
    my $group;

    if( defined $uxmon::joined_groups{"+".$name} ) {
	$joined = $uxmon::joined_groups{"+".$name};
	foreach $group (@groups) {
	    @$joined = (grep( $group ne $_, @$joined ));
	}
    }
    unless( defined $uxmon::joined_groups{"-".$name} ) {
	$uxmon::joined_groups{"-".$name} = [];
    }
    $joined = $uxmon::joined_groups{"-".$name};
    foreach $group (@groups) {
	@$joined = (grep( $group ne $_, @$joined ), $group);
    }
    print "$name is in groups: ".join(" ",@$joined)."\n" if( $BigSister::common::dl>4 );
}


sub displayname {
    my( $name, $realname ) = @_;

    $uxmon::joined_groups{"D".$name} = [ $realname ];
}


sub run_once {
    my( $time ) = @_;
    my( $checker, $reporter, $item, $comment, $ref, @comments );

    &load_rules;
    print "uxmon: next pass ...\n" if( $BigSister::common::dl );
    Schedule::schedule( \@uxmon::checkers, $time, "discover" );
    CWorker::do_them();
    Schedule::schedule( \@uxmon::checkers, $time, "check" );
    CWorker::do_them();
    Schedule::schedule( \@uxmon::checkers, $time, "late" );
    CWorker::do_them();

    foreach $item (keys %uxmon::report) {
	$ref = $uxmon::sort_comments{$item};
	next unless( $ref );
	$comment = "";
	foreach $ref (@$ref) {
	    my $i = rindex( $comment, "\n" );
	    ($i=-40) unless( $i>0 );
	    if( length($comment) + length($$ref[1]) - $i > 70 ) {
		$comment .= "\n".$$ref[1];
	    }
	    else { 
		($comment .= ", ") if( $comment );
		$comment .= $$ref[1];
	    }
	}
	$uxmon::comment{$item} = $comment;
    }

    Schedule::schedule( \@uxmon::reporters, $time, \%uxmon::report, \%uxmon::comment, \%uxmon::joined_groups );
    CWorker::do_them();

    if( $BigSister::common::dl>1 ) {
	foreach $item (keys %uxmon::report) {
	    print "to collector: $item: $uxmon::report{$item}: $uxmon::comment{$item}\n" if( $BigSister::common::dl>1 );
	}
    }

    Schedule::schedule( \@uxmon::checkers, $time, "cycle_done" );
    CWorker::do_them();
}


sub logmode {
    my( $checker, $reporter, $item, $comment, $ref, @comments, $continue, %old_report, $key, $cont, $changed );

    &load_rules;
    print "uxmon: logging ...\n" if( $BigSister::common::dl );
    foreach $checker (@uxmon::checkers) {
	next unless( defined $checker->log );
	$continue = 1;
	$uxmon::override_time = 0;
	while( $continue ) {
	    %uxmon::report = ();
	    %uxmon::comment = ();
	    %uxmon::sort_comments = ();
	    $continue = $checker->log;
	    foreach $item (keys %uxmon::report) {
		$ref = $uxmon::sort_comments{$item};
		next unless( $ref );
		@comments = sort { $$a[0] <=> $$b[0] } @$ref;
		$comment = "";
		foreach $ref (@comments) {
		    my $i = rindex( $comment, "\n" );
		    ($i=-40) unless( $i>0 );
		    if( length($comment) + length($$ref[1]) - $i > 70 ) {
			$comment .= "\n".$$ref[1];
		    }
		    else { 
			($comment .= ", ") if( $comment );
			$comment .= $$ref[1];
		    }
		}
		$uxmon::comment{$item} = $comment;
	    }
	    $changed = 0;
	    while( ($key,$cont) = each %uxmon::report ) {
		if( $cont ne $old_report{$key} ) {
		    print "changed: $key\n" if( $BigSister::common::dl>1 );
		    $changed = 1;
		    last;
		}
	    }
	    unless( $changed ) {
		while( ($key,$cont) = each %old_report ) {
		    if( $cont ne $uxmon::report{$key} ) {
			print "changed: $key\n" if( $BigSister::common::dl>1 );
			$changed = 1;
			last;
		    }
		}
	    }

	    if( $changed ) {
		%old_report = %uxmon::report;
		foreach $reporter (@uxmon::reporters) {
		    $reporter->set_time( $uxmon::override_time ) if( $uxmon::override_time );
		    $reporter->report( \%uxmon::report, \%uxmon::comment, \%uxmon::joined_groups );
		}
		if( $BigSister::common::dl>1 ) {
		    foreach $item (keys %uxmon::report) {
			print "to collector: $item: $uxmon::report{$item}: $uxmon::comment{$item}\n" if( $BigSister::common::dl>1 );
		    }
		}
		sleep 1;
	    }
	}
	$uxmon::override_time = 0;
    }
}



sub force_rule_reload {
    $uxmon::old_rule_file_stat = 0;
    &load_rules;
}


sub load_rules {
    my( $rule_file_stat );

    return( 1 ) if( $uxmon::old_rule_file_stat );
    $uxmon::old_rule_file_stat = time;
    print "loading rule file $uxmon::rule_file\n" if( $BigSister::common::dl>1 );
    @uxmon::checkers = @uxmon::reporters = ();
    my $ok = 0;
    eval {
	require "$uxmon::rule_file";
	$ok = 1;
    };
    BigSister::common::log( "err", "cannot load rule file $uxmon::rule_file" ) unless( $ok );
    &rules::config;
    return 1;
}


sub reconfigure {
    print "reconfiguring uxmon\n" if( $BigSister::common::dl>1 );
    my $time = time;
    Schedule::schedule( \@uxmon::checkers, 0, "unconfigure" );
    CWorker::do_them();
    @uxmon::checkers = @uxmon::reporters = @uxmon::schedule = ();
    %uxmon::report = ();
    %uxmon::comment = ();
    %uxmon::sort_comments = ();
    %uxmon::monitor_variables = ();
    &rules::config;
    return 1;
}


sub reporter {
    my( $reporter ) = @_;

    push( @uxmon::reporters, $reporter );
}



sub checker {
    my( $checker ) = @_;

    push( @uxmon::checkers, $checker );
}



sub load_module {
    my( $module ) = @_;
    my( $stat, $dir, $file );
    my( $ret );

    $ret = 0;
    # refuse to reload ourselves
    return if( $module eq "uxmon" );
    $module = "$module.pm";
    eval {
	require "Monitor/$module";
	$uxmon::modules{$module} = 1;
	$ret = 1;
    };
    unless( $ret ) {
	BigSister::common::log( "err", "module Monitor::$module does not load" );
    }
    return $ret;
}


sub postevent {
    my( $item, $time, $priority, $text ) = @_;

    foreach my $reporter (@uxmon::reporters) {
        $reporter->postevent( $item, $time, $priority, $text );
    }
}


sub perfreport {
    my( $alias, $variable, $time, $value ) = @_;

    foreach my $reporter (@uxmon::reporters) {
        $reporter->perfreport( $alias, $variable, $time, $value );
    }
}


sub report {
    my( $item, $value, $comment, $id ) = @_;
    my $ref;

    print "report $item: $value: $comment\n" if( $BigSister::common::dl>3 );
    unless( $uxmon::sort_comments{$item} ) {
	$ref = $uxmon::sort_comments{$item} = [ [ $value, $comment, $id ] ];
    }
    else {
	$ref = $uxmon::sort_comments{$item};
	my $i;

	for( $i=$#$ref; ($i>=0) && ($ref->[$i]->[0]>$value); $i-- ) {;}
	splice( @$ref, $i+1, 0, [ $value, $comment, $id ] );
    }
    $uxmon::report{$item} = $ref->[0]->[0];
}


sub unreport {
    my( $item, $id ) = @_;

    return unless( $uxmon::sort_comments{$item} );
    my $ref = $uxmon::sort_comments{$item};
    my $i;

    for( $i=$#$ref; ($i>=0) && ($ref->[$i]->[2] ne $id); $i-- ) {;}
    if( $i >= 0 ) {
	splice( @$ref, $i, 1 );
	if( $i == 0 ) {
	    if( $#$ref < 0 ) {
	        delete $uxmon::report{$item};
	    }
	    else {
		$uxmon::report{$item} = $ref->[0]->[0];
	    }
	}
    }
}



sub set_variable {
    my( $host, $variable, $value ) = @_;

    print "uxmon::set_variable( $host, $variable, $value )\n" if( $BigSister::common::dl>6 );
    unless( defined $uxmon::monitor_variables{$host} ) {
	$uxmon::monitor_variables{$host} = {};
    }
    $uxmon::monitor_variables{$host}->{$variable} = $value;
}



sub unset_variable {
    my( $host, $variable ) = @_;

    print "uxmon::unset_variable( $host, $variable )\n" if( $BigSister::common::dl>6 );
    return unless( defined $uxmon::monitor_variables{$host} );
    delete $uxmon::monitor_variables{$host}->{$variable};
}



sub set_time {
    $uxmon::override_time = $_[0];
}


sub test_deprecated {
    my( $args ) = @_;

    my $test = $args->{"cmd"};
	print STDERR <<END;
WARNING: test '$test' without DESCR in uxmon-net is deprecated!
END
}


1;
