# Copyright © 2016 by Yaroslav Kolomiiets
package Plan9;

sub vlongstrx($) {
	local $_ = shift;
	my ($lo, $hi);

	($lo, $hi) = ($_, 0);
	if($lo == ~0){
		$hi = ~0;
	}
	return sprintf("%.8lx%.8lx", $hi, $lo);
}

sub vlongstrd($) {
	local $_ = shift;
	my ($lo, $hi);

	($lo, $hi) = ($_, 0);
	if($lo == ~0){
		$hi = ~0;
	}
	sprintf("%lld", $hi<<32|$lo);
}

sub qidstr(\%) {
	local $_ = shift;
	my ($p, $t);

	$p = vlongstrx($_->{path});
	$t = qidtype($_->{type});
	return sprintf("(%s %lu %s)", $p, $_->{vers}, $t);
}

sub dirstr(\%) {
	my $d = shift;
	my $l;

	$l = vlongstrd($d->{length});
	return sprintf("'%s' '%s' '%s' '%s' q %s m %lo at %ld mt %ld l %s t %d d %d",
		@{$d}{qw(name uid gid muid)}, qidstr(%{$d->{qid}}),
		@{$d}{qw(mode atime mtime)}, $l, @{$d}{qw(type dev)});
}

sub qidtype($) {
	my $t = shift;
	my $s = '';

	$s .= 'd' if($t & QTDIR);
	$s .= 'a' if($t & QTAPPEND);
	$s .= 'l' if($t & QTEXCL);
	$s .= 'M' if($t & QTMOUNT);
	$s .= 'A' if($t & QTAUTH);
	$s .= 't' if($t & QTTMP);
	return $s;
}

sub fcallstr(\%) {
	my $f = shift;
	my ($i, $s, $nwname, $nwqid, $fntab, $fn);
	local $_;

	$fntab = {		
		&Tversion =>	sub {
			sprintf("Tversion tag %u msize %u version '%s'", @$f{'tag','msize','version'})
		},
		&Rversion =>	sub {
			sprintf("Rversion tag %u msize %u version '%s'", @$f{'tag','msize','version'})
		},
		&Tauth =>	sub {
			sprintf("Tauth tag %u afid %d uname %s aname %s",
				@{$f}{'tag','afid','uname','aname'})
		},
		&Rauth =>	sub { sprintf("Rauth tag %u aqid %s", $f->{tag}, qidstr(%{$f->{aqid}})) },
		&Rerror =>	sub { sprintf("Rerror tag %u ename '%s'", @{$f}{'tag','ename'}) },
		&Tflush =>	sub { sprintf("Tflush tag %u oldtag %u", @{$f}{'tag','oldtag'}) },
		&Rflush =>	sub { sprintf("Rflush tag %u", $f->{'tag'}) },
		&Tattach =>	sub { sprintf("Tattach tag %u fid %d afid %d uname '%s' aname '%s'",
			@{$f}{'tag','fid', 'afid','uname','aname'}) },
		&Rattach =>	sub { sprintf("Rattach tag %u qid %s", ${$f}{tag}, qidstr(%{$f->{qid}})) },
		&Twalk =>	sub {
			$nwname = @{$f->{wname}};
			$s = sprintf("Twalk tag %u fid %d newfid %d nwname %d",
				@{$f}{'tag','fid','newfid'}, $nwname);
			for($i=0; $i<$nwname; $i++){
				$s .= sprintf(" %d:%s", $i, $f->{wname}[$i]);
			}
			$s
		},
		&Rwalk =>	sub {
			$nwqid = @{$f->{wqid}};
			$s = sprintf("Rwalk tag %u nwqid %d", ${$f}{tag}, $nwqid);
			for($i=0; $i<$nwqid; $i++){
				$s .= sprintf(" %d:%s", $i, qidstr(%{$f->{wqid}[$i]}));
			}
			$s
		},
		&Topen =>	sub { sprintf("Topen tag %u fid %d mode %d", @{$f}{'tag', 'fid', 'mode'}) },
		&Ropen =>	sub { sprintf("Ropen tag %u qid %s iounit %u", $f->{tag}, qidstr(%{${$f}{qid}}), $f->{iounit}) },
		&Tcreate =>	sub { sprintf("Tcreate tag %u fid %d name %s perm %o mode %d", @{$f}{'tag','fid','name','perm','mode'}) },
		&Rcreate =>	sub { sprintf("Rcreate tag %u qid %s iounit %u", ${$f}{tag}, qidstr(%{${$f}{qid}}), ${$f}{iounit}) },
		&Tread =>	sub { 
sprintf("Tread tag %u fid %d offset %s count %u", @{$f}{'tag','fid'}, vlongstrd($f->{offset}), $f->{'count'}) },
		&Rread =>	sub { sprintf("Rread tag %u count %u ...", $f->{tag}, length($f->{data})) },
		&Twrite =>	sub { sprintf("Twrite tag %u fid %d offset %s count %u ...", @{$f}{'tag','fid'}, vlongstrd($f->{offset}), length($f->{data})) },
		&Rwrite =>	sub { sprintf("Rwrite tag %u count %u", @{$f}{'tag','count'}) },
		&Tclunk =>	sub { sprintf("Tclunk tag %u fid %d", @{$f}{'tag','fid'}) },
		&Rclunk =>	sub { sprintf("Rclunk tag %u", ${$f}{tag}) },
		&Tremove =>	sub { sprintf("Tremove tag %u fid %d", @{$f}{'tag','fid'}) },
		&Rremove =>	sub { sprintf("Rremove tag %u", ${$f}{tag}) },
		&Tstat =>	sub { sprintf("Tstat tag %u fid %d", @{$f}{'tag','fid'}) },
		&Rstat =>	sub { sprintf("Rstat tag %u %s", ${$f}{tag}, dirstr(%{$f->{stat}})) },
		&Twstat =>	sub { sprintf("Twstat tag %u %s", ${$f}{tag}, dirstr(%{$f->{stat}})) },
		&Rwstat =>	sub { sprintf("Rwstat tag %u", ${$f}{tag}) },
	};

	$fn = $fntab->{$f->{type}};
	if(defined $fn){
		$s = &$fn($f);
	}else{
		$s = sprintf("unknown type %d", $_);
	}
	return $s;
}
1;
