#!/usr/bin/perl

use strict;
use warnings;

use Net::SNMP;

my $host = $ARGV[0] or die "Usage: $0 <host>\n";

print "Scanning $host...\n";

my $sess = Net::SNMP->session(
	-hostname => $host . '.kam.mff.cuni.cz',
	-version => 1,
	-community => 'public',
) or die;

my $prmib = '1.3.6.1.2.1.43';

sub parse_table($) {
	my $raw = $sess->get_table(-baseoid => $_[0]) or return {};

	my %tab = ();
	for my $bb (keys %$raw) {
		my $bk = substr($bb, length($_[0]) + 1);
		$bk =~ /(.*)\.1\.(\d+)/ or die "OID parse error at $bk";
		$tab{$2}{$1} = $raw->{$bb};
	}

	return \%tab;
}

sub media_unit($) {
	my ($unit) = @_;
	return unless defined $unit;
	return 25.4 / 10000 if $unit == 3;
	return 0.001 if $unit == 4;
	return 1 if $unit == 0;		# Some printers (Xerox 3300MFP) send unit 0 -- what does it mean?
	die "Unknown media unit $unit\n";
}

sub subunit_status($) {
	my ($s) = @_;
	my @bases = ('Idle', 'OnReq', 'Standby', 'Broken', 'Active', 'Unknown', 'Busy', '??7??');
	my $base = $bases[$s & 7];
	if ($s & 8) { $base .= '/Alert'; }
	if ($s & 16) { $base .= '/CritAlert'; }
	if ($s & 32) { $base .= '/Offline'; }
	if ($s & 64) { $base .= '/Transition'; }
	return $base;
}

sub current($) {
	my ($c) = @_;
	return "other" if $c == -2;
	return "unknown" if $c == -2;
	return "OK" if $c == -3;
	return "???" if $c < 0;
	return $c;
}

print "\n### Inputs ###\n";
my $ins = parse_table("$prmib.8.2.1");
for my $i (sort keys %$ins) {
	my $b = $ins->{$i};
	printf "%-10s ", ($b->{13} // "Input #$i");
	my $unit = media_unit($b->{3});
	print join("x", map { (!defined($_) || $_ < 0) ? '?' : sprintf("%.0f", $_*$unit) } ($b->{4}, $b->{5}));
	print " capa=", $b->{9};
	print " cur=", current($b->{10});
	print " stat=", subunit_status($b->{11});
	print " media=", $b->{12};
	print "\n";
}

print "\n### Outputs ###\n";
my $outs = parse_table("$prmib.9.2.1");
for my $i (sort keys %$outs) {
	my $b = $outs->{$i};
	printf "%-10s ", ($b->{7} // "Output #$i");
	my $unit = media_unit($b->{14});
	print join("x", map { (!defined($_) || $_ < 0) ? '?' : sprintf("%.0f", $_*$unit) } ($b->{15}, $b->{16}));
	print " capa=", $b->{4};
	print " remains=", current($b->{5});
	print " stat=", subunit_status($b->{6});
	print "\n";
}

print "\n### Marker ###\n";
my $mkrs = parse_table("$prmib.10.2.1");
my $mkr = $mkrs->{1};	# Multiple markers not supported by this simple-minded script
$mkr->{3} == 7 or die "Unknown marker counter unit " . $mkr->{3};
print "Life counter: ", $mkr->{4}, "\n";
my $mmu = media_unit($mkr->{8});
print "Resolution: ", join("x", map {
	sprintf("%.0f", $_ / (10000*$mmu) * 25.4);
} ($mkr->{9}, $mkr->{10})), " DPI\n";
print "Margins: ", join("/", map { sprintf("%.2f", $_*$mmu) } ($mkr->{11}, $mkr->{12}, $mkr->{13}, $mkr->{14})), " mm\n";
print "Status: ", subunit_status($mkr->{15}), "\n";

print "\n### Colorants ###\n";
my $colors = parse_table("$prmib.12.1.1");
my %clrants = ();
for my $i (sort keys %$colors) {
	my $c = $colors->{$i};
	print "Color $i: ", $c->{4}, "\n";
	$clrants{$i} = $c->{4};
}

my %supply_types = (
	1 => 'other',
	2 => 'unknown',
	3 => 'toner',
	4 => 'wasteToner',
	5 => 'ink',
	6 => 'inkCartridge',
	7 => 'inkRibbon',
	8 => 'wasteInk',
	9 => 'opc',
	10 => 'developer',
	11 => 'fuserOil',
	12 => 'solidWax',
	13 => 'ribbonWax',
	14 => 'wasteWax',
	15 => 'fuser',
	16 => 'coronaWire',
	17 => 'fuserOilWick',
	18 => 'cleanerUnit',
	19 => 'fuserCleaningPad',
	20 => 'transferUnit',
	21 => 'tonerCartridge',
	22 => 'fuserOiler',
	23 => 'water',
	24 => 'wasteWater',
	25 => 'glueWaterAdditive',
	26 => 'wastePaper',
	27 => 'bindingSupply',
	28 => 'bandingSupply',
	29 => 'stitchingWire',
	30 => 'shrinkWrap',
	31 => 'paperWrap',
	32 => 'staples',
	33 => 'inserts',
	34 => 'covers'
);

print "\n### Supplies ###\n";
my $supp = parse_table("$prmib.11.1.1");
for my $i (sort { $a <=> $b } keys %$supp) {
	my $s = $supp->{$i};
	printf "%2d: ", $i;
	printf "%-16s", ($supply_types{$s->{5}} // "??" . $s->{4} . "??");
	printf "%-8s", $s->{3} ? ($clrants{$s->{3}} // "Color" . $s->{3}) : "---";
	printf "%-16s", (current($s->{9}) . "/" . $s->{8});
	if ($s->{8} && $s->{9} >= 0) {
		printf " %3.0f%%", $s->{9} * 100 / $s->{8};
	} else {
		print " ----";
	}
	print "  ", $s->{6};
	print "\n";
}

print "\n### Console ###\n";
my $con = parse_table("$prmib.16.5.1");
for my $i (sort keys %$con) {
	print "$i:", $con->{$i}->{2}, "\n" if $con->{$i}->{2} =~ /\S/;
}

print "\n### Lights ###\n";
my $lights = parse_table("$prmib.17.6.1");
for my $i (sort keys %$lights) {
	my $l = $lights->{$i};
	my %color = ( 1=>'other', 2=>'unkn', 3=>'white', 4=>'red', 5=>'green', 6=>'blue', 7=>'cyan', 8=>'mgnta', 9=>'yellw', 10=>'ornge' );
	if ($l->{2} && !$l->{3}) { print "[*]"; }
	elsif (!$l->{2} && $l->{3}) { print "[ ]"; }
	elsif ($l->{2} && $l->{3}) { print "[.]"; }
	else { print "[?]"; }
	printf " %5s", $color{$l->{4}} // "?????";
	print "  ", $l->{5};
	print "\n";
}

print "\n### Alerts ###\n";
my $alerts = parse_table("$prmib.18.1.1");
for my $i (sort keys %$alerts) {
	my $a = $alerts->{$i};
	print "$i:";
	my %severity = ( 1=>'other', 2=>'CRIT', 3=>'WARN', 4=>'WARN' );
	print " sev=", $severity{$a->{2}} // "???";
	my %training = ( 6=>'management', 5=>'service', 4=>'trained', 3=>'untrained', 2=>'unknown', 1=>'other' );
	print " who=", $training{$a->{3}} // "???";
	print " group=", $a->{4};
	print " index=", $a->{5};
	print " loc=", $a->{6};
	print " code=", $a->{7};
	print " desc=", $a->{8};
	print " time=", $a->{9};
	print "\n";
}

print "\n### Finisher ###\n";
my $finisher = parse_table("$prmib.30.1.1");
for my $i (sort keys %$finisher) {
	my $f = $finisher->{$i};
	print "$i:";
	print " type=", $f->{2};
	print " present=", $f->{3};
	print " capa=", $f->{5};
	print " current=", current($f->{6});
	print " status=", subunit_status($f->{9});
	print " desc=", $f->{10};
	print "\n";
}

print "\n### Finisher supplies ###\n";
my $fsu = parse_table("$prmib.31.1.1");
for my $i (sort keys %$fsu) {
	my $f = $fsu->{$i};
	print "$i:";
	print " dev=", $f->{2};
	print " class=", $f->{3};
	print " type=", $f->{4};
	print " desc=", $f->{5};
	print " capa=", $f->{7};
	print " current=", current($f->{8});
	print "\n";
}