#!/usr/bin/perl

# Watch read/write activity, defrag style
#
# Simon Kirby, 2016-04-11

use strict;
use POSIX qw[ceil];
sub TIOCGWINSZ () { 0x5413; }
require "sys/ioctl.ph";
use Time::HiRes qw[gettimeofday tv_interval];

my ($row,$col,$lastrow,$lastcol);

my $width = 80;
my $height = 25;
my $screen = $width * $height;
my $swidth;
my $dwidth;
my $block_live;
my $block_dead;
sub sig_winch {
        my $winsize;
        -t STDIN or return;
        ioctl(STDIN, TIOCGWINSZ, $winsize) or return;
        ($height,$width) = unpack('S2', $winsize);
#        $width = 96 if ($width > 96);
        $block_live = chr(219);
        $block_dead = chr(176);
	$screen = $width * $height;
	print "\033[1;30m\033[H\033[s";
	for ($row = 0;$row < $height;$row++) {
		print $block_dead x $width;
	}
	print "\033[m\033[H";
	$lastrow = $lastcol = 0;
}
$SIG{'WINCH'} = \&sig_winch;
sig_winch();
$| = 1;

sub printblock ($$$) {
	my $ofs = $_[0] % $screen;
	my $n = $_[1];
	my $c = $_[2];
wraparound:
	my $row = int($ofs / $width);
	my $col = int($ofs % $width);
	if ($row == $lastrow and $col < $lastcol and $col >= $lastcol - 4) {
		print "\010" x ($lastcol - $col);
		$lastcol = $col;
	} else {
		print "\033[${row};${col}H";
		$lastrow = $row;
		$lastcol = $col;
	}
	if ($ofs + $n < $screen) {
		print $c x $n;
		print "\010";
		$ofs+= $n - 1;
		$lastrow = int($ofs / $width);
		$lastcol = int($ofs % $width);
		return;
	}
	my $f = $screen - $ofs - $n;
	print $c x $f;
	$n-= $f;
	$ofs = 0;
	goto wraparound;
}

my ($ofs, $count, $program, $rw, $r);
$ofs = $count = 0;
my @ofs = ();
my @count = ();
my %ofscount = ();
my $device = $ARGV[0];
open(IN, "-|") || exec('btrace',$device) or die "btrace \"$device\": $!";
open(DEV, '<', $device) or die "Opening \"$device\": $!";
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
 $blksize,$blocks) = stat(DEV) or die "stat \"$device\": $!";
close(DEV);
open(PART, '<', '/proc/partitions') or die "Opening /proc/partitions: $!";
my ($maj,$min,$sectors,$name);
while (<PART>) {
	chomp;
	my ($maj,$min,$sectors,$name) = (/^\s*(\S+)\s+(\S+)\s+(\d+)\s+(.*)/) or next;
	last if (($maj << 8) + $min == $rdev);
}
close(PART);
$sectors = 2929721344 if (!$sectors);
while (<IN>) {
	$r = substr($_,43,1);
	next if $r !~ /[CDSXA]/;
#	print; next;
	$_ = substr($_,45);
	chomp;
	if (scalar @ofs >= 64) {
		$ofs = shift @ofs;
		$count = shift @count;
		my $k = $ofs.'_'.$count;
		if ($ofscount{$k} == 1) {
			print "\033[1;30m";
			printblock($ofs, $count, $block_dead);
		}
		if (!--$ofscount{$k}) {
			delete $ofscount{$k};
		}
	}
	($rw,$ofs,$count,$program) = /^\s*(\S+)\s+(\d+) \+ (\d+) (.*)$/;
	$ofs = int($ofs * $screen / $sectors);
	$count = int(($count * $screen + $sectors - 1) / $sectors);
#	$ofs = int($ofs / 8);
#	$count = int(($count + 7) / 8);
#	$ofs = int($ofs / 64);
#	$count = int(($count + 63) / 64);
	next if (!$count);
	if ($r eq 'D') {
		print "\033[1;";
	} elsif ($r eq 'C') {
		print "\033[0;";
	} else {
		print "\033[0;5;7";
	}
	if ($rw =~ /W/) {
		print "31m";
	} else {
		print "32m";
	}
	printblock($ofs, $count, $block_live);
	push(@ofs, $ofs);
	push(@count, $count);
	$ofscount{$ofs.'_'.$count}++;
}
close(IN);
