#!/usr/bin/perl -l

#ntparse version 7.08 (raspberry pi & windows) by Adam Kumiszcza
use strict;
use warnings;
use Time::HiRes qw(time usleep);
use POSIX qw(strftime);
use if $^O eq 'MSWin32', 'Win32::Console::ANSI';
use threads;
use threads::shared;
use constant {N => "\e[0K\n", T => ":\e[36G"}; # end of coloring and a tab
use constant {V => "\e[0m"};
use Getopt::Long;

my $slow;
my $sleep = 5;
GetOptions (
	"sleep=i" => \$sleep,
	"slow"  => \$slow)   # flag
	or die("Error in command line arguments\n");

my $nd :shared;
my $d :shared;
my $U;

$nd = 0;

if($^O eq 'MSWin32') { $U = "\e[1m"; }
else { $U = "\e[4m"; }

# regexp precompilation
my $r1 = qr/system peer:\s+(\S+)\n.+reference ID:\s+(\S+)\n.+stratum=(\d+).+rootdelay=(\S+),.*rootdisp=(\S+),.+offset=(\S+),.*frequency=(\S+),.*sys_jitter=(\S+),/s;
# https://regex101.com/r/jy9ih8/2
my $r2 = qr/\*(\S+)\s+(\S+)\s+(?:\S+\s+){6}(\S+)\s+(\S+)/;
# https://regex101.com/r/4IAQ2k/1

printf "\e[?25l\e[2J"; # screen and cursor reset
threads->create(\&ntpdata);

if($slow) {
	while (1) {
		my $t = time;
		my $ot = 0;
		my $date = strftime "%Y-%m-%dT%H:%M:%S", localtime $t;
		print "\e[H\e[37;44;1m $date \e[0m\e[0K";
		if($nd) {
			print "\e[?25l$d\e[0J";
			$nd = 0;
		}
		sleep($sleep);
	}
}
else {
	while (1) {
		my $t = time;
		my $ot = 0;
		my $nt = int(($t-int($t))*100);

		if($nt != $ot) {
			my $date = strftime "%Y-%m-%dT%H:%M:%S", localtime $t;
			$date .= sprintf ".%02d", $nt;
			print "\e[H\e[37;44;1m $date \e[0m\e[0K";
			if($nd) {
				print "\e[?25l$d\e[0J";
				$nd = 0;
			}
		}
		$ot = $nt;
		usleep(5e3);
	}
}

sub ntpdata {
	threads->detach();
	while (1) {
		my (@p) = (`ntpq -csysinfo -crv` =~ /$r1/);
		$_ = `ntpq -n -clpe`; # więcej serwerów niż -np
		my (@q) = (/$r2/);
		if($_ && scalar @p == 8 && scalar @q == 4) {
			$d = N."Sync to".T."$p[0]".N.
			"Reference ID".T.$p[1].N.
			"System stratum".T.$p[2].N.
			"Reference IP".T.$q[0].N.
			"Reference offset [ms]".T.$q[2].N.
			"Reference jitter".T.$q[3].N.$U.
			"Offset".T.printms($p[5]).N.
			"Jitter".T.printms($p[7]).V.N.
			"Total (root) roundtrip delay [ms]".T.$p[3].N.
			"Total (root) dispersion [ms]".T.$p[4].N.
			"Frequency offset [PPM]".T.$p[6].N;
			# coloring of lines
			s/\n-/\n\e[0;37;41m\-/g;
			s/\n\+/\n\e[0;30;43m\+/g;
			s/\n\*/\n\e[1;37;42m\*/g;
			s/\no/\n\e[1;37;42mo/g;
			s/\n/\e[0m\e[0K\n/g;
			$d .= "\e[0K\n$_\e[0J";
		} else {
			$d = "NTP data incomplete\e[0J";
		}
		$nd++;
		sleep(5);
	}

}

sub printms {
	my ($val) = @_;
	my $str;
	if(abs($val)>1.0) {
		$str = sprintf '%.5g', $val;
		$str .= " ms";
	} else {
		$val *= 1000;
		if(abs($val)>1.0) {
			$str = sprintf '%.5g', $val;
			if($^O eq 'MSWin32') { $str .= " us"; }
			else { $str .= " µs"; }
		} else {
			$val *= 1000;
			$str = sprintf '%.5g', $val;
			$str .= " ns";
		}
	}
	return $str;
}

