#!/usr/bin/perl
use 5.008 ; use strict ; use warnings ; 
use Getopt::Std ; getopts ".:bc:dgsw:z:" , \my %o ; 
use Time::HiRes qw[ gettimeofday tv_interval sleep ] ; # Perl 5.7.3からコアモジュール
use Term::ANSIColor qw[ color :constants ] ; $Term::ANSIColor::AUTORESET = 1 ;
use FindBin qw[ $Script ] ; 

sub FB ($){FAINT BOLD $_[0]} 
sub Y ($){YELLOW $_[0]} 

my ($t0s,$t0p) =  gettimeofday ; # 「起動した時刻」の記録。15マイクロ秒のオーバーヘッド
$ENV{TZ} = $o{z} if exists $o{z} ;
sub TSTP { local $|=1 ;say STDERR CYAN "$. line(s) output so far. ($Script)" ; $SIG{TSTP} ='DEFAULT' ; kill 'TSTP', $$ } ;
$SIG{TSTP} = sub { TSTP } ; 
$SIG{CONT} = sub { $SIG{TSTP} = sub { TSTP } } ;

$| = 1 unless $o{b} ;
$o{'.'} //= 3 ;
do { & proc_w ; exit } if defined $o{w} ; 

my $dig = $o{'.'} if $o{'.'} ; # 桁数 (表示する秒の小数点以下の桁数)
my $lay = 1e6 / 10 ** $dig if defined $dig ; # lay は英語の「置く」のつもり。
my $HMS = '%02d:%02d:%02d' ; # 何度も使われる書式
* gapproc = $o{g} ? sub (){ ($t0s, $t0p) = gettimeofday } : sub (){} ; # 50ナノ秒のオーバーヘッド。1回の実行につき。
do { & main ; exit } ; 

sub proc_w () { 
  while ( <> ) {
    do { my $s = "$o{w}" * $. - tv_interval( [$t0s,$t0p] , [gettimeofday] ) ; sleep $s > 0 ? $s : 0 } ; 
    print $_ ; # print は 2 〜 5 ミリ秒動作に時間がかかる。
  }
}

sub hhmmss0 () { sprintf $HMS , @{[localtime]}[2,1,0] }
sub hhmmssD () { my($s,$m)=gettimeofday ; sprintf "$HMS.%0${dig}d",@{[localtime $s]}[2,1,0],int $m/$lay }
sub ymdhms () { my @f=localtime;$f[4]+=1;$f[5]+=1900 ; sprintf "%02d-%02d-%02d $HMS" , @f[5,4,3,2,1,0] }
sub secondsSince0 () { return sprintf "%0.0f" , time - $t0s } 
sub secondsSinceD () { return sprintf "%0.${dig}f" , tv_interval [ $t0s, $t0p ] }

sub timenow () {
  my $t1 = $o{d} ? ymdhms : defined $dig ? hhmmssD : hhmmss0 ; 
  my $t2 = $dig ? secondsSinceD : secondsSince0 ; 
  "$t1 $t2" ;
}

sub main () { 
  * timenow = $o{d} ? * ymdhms : defined $dig ? * hhmmssD : * hhmmss0 ; 
  * timenow = $o{'.'} ? * secondsSinceD : * secondsSince0 if $o{s} || $o{g} ; 
  my $C = defined $o{c} ? $o{c} eq 0 ? '' : color $o{c} : color 'faint bold' ;
  my $R = defined $o{c} ? $o{c} eq 0 ? '' : color 'reset' : color 'reset' ; 
  while (<>) { my $t = timenow ; print "$C$t$R\t$_" ; & gapproc } ;
}

## ヘルプの扱い
sub VERSION_MESSAGE {}
sub HELP_MESSAGE{
    use FindBin qw[ $Script ] ; 
    $ARGV[1] //= '' ;
    open my $FH , '<' , $0 ;
    while(<$FH>){
        s/\$0/$Script/g ;
        print $_ if $ARGV[1] eq 'opt' ? m/^\ +\-/ : s/^=head1// .. s/^=cut// ;
    }
    close $FH ;
    exit 0 ;
}

=encoding utf8 

=head1

 timeput 

  入力を1行ずつ読み、その各行の先頭に、読み取った時点の時刻をタブ区切りで出力する。
  ただし、"-w 秒数" の指定により、1行読んで出力して、指定秒数動作を止める(sleepする)。

 オプション : 
    -b : 出力のバッファリングを許す(何か理由が無い限り使うことは無いであろう)
    -c STR : 色の指定。0で色無し。"cyan","faint red bold","bright_blue"など。色名指定はTerm::ANSIColor を参照。
    -d : 日付も出力。yyyy-mm-dd HH:MM:SS 形式で。(date)
    -g : 開始からの秒数でなくて、1行ごとの間隔秒数を表示。(gap)
    -s : 起動してからの秒数を出力。(start)
    -w N: 入力から1行ずつ逐次読み取り出力して、N秒停止することを繰り返す。正の浮動小数点数を指定可能。(wait)
    -z STR : タイムゾーンの設定。"Asia/Tokyo", "-9", "-9:00", "JST-9" などが設定できる。(システム依存)

    -. N : 秒数を小数点以下 N 桁出力する。0から6の整数値が使える。未指定なら3。(-d とは両立しない。) 

 利用例 : 
    yes | head | timeput -.6 
    yes | head | timeput -d  | sed 's/ /T/' # POSIXの日時形式にしたい場合。このsed文は最初の空白文字のみTに変更。
    seq 5 | timeput -.6 | timeput -s.6 | timeput -g.6
    seq 10 | timeput -w0.45 | timeput -g.3 # 0.45秒ずつ1行を読み取る。
    command 1> output1.txt 2> >( timeput error.log ) # command は標準エラー出力に何か出力をするプログラムである。便利。

  開発メモ:
    * -w の動作を高度化したい。-w 2,0.2x10,0 と指定することで最初は2秒、次に10回は0.2秒、残りは即座のように。,は+、xは*でも可としたい。
    * -3 のオプションを作りたい。timeput -3 で timeput -g | timeput -s | timeput とおなじ機能を持たせたい。
    * 名前を timeput (タイムプット)ではなく、puttime の方が「プッタイム」で発音しやすい気がする。その方が記憶しやすい。

=head1 AUTHOR

下野寿之 Toshiyuki SHIMONO <bin4tsv@gmail.com>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2021 Toshiyuki SHIMONO. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut