#!/usr/bin/perl
use 5.008 ; use strict ; use warnings ; 
use Getopt::Std ;
use Getopt::Long qw [ GetOptions :config bundling no_ignore_case pass_through ] ; # GetOptionsFromArray ] ;
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]} 

GetOptions 'sleep=f' => \my$sleep ; 
getopts ".:b:c:dgsz:" , \my %o ; 

my ($t0s,$t0p) = gettimeofday ; # 「起動した時刻」の記録。15マイクロ秒のオーバーヘッド
$ENV{TZ} //= $o{z} ; # タイムゾーン

## Ctrl+Z 押下に対する反応 の指定
sub cZ { say STDERR CYAN "$. line(s) output so far. ($Script $$)" ; $SIG{TSTP} ='DEFAULT' ; kill 'TSTP', $$ } ;
$SIG{TSTP} = sub { cZ } ; 
$SIG{CONT} = sub { $SIG{TSTP} = sub { cZ } } ;

$| = 0 eq ($o{b}//'') ? 0 : 1 ; # -b 0 の設定で、バッファリングをしない。($| != 0 ならただちに出力。)

## --sleep 秒数(マイクロ秒単位) での動き
do { & proc_sleep ; exit } if defined $sleep ; 
sub proc_sleep () { 
  while ( <> ) {
    do { my $s = $sleep * $. - tv_interval( [$t0s,$t0p] , [gettimeofday] ) ; sleep $s > 0 ? $s : 0 } ; 
    print $_ ; # print は 2 〜 5 ミリ秒動作に時間がかかる。
  }
}

#$o{'.'} //= 3 ; # 秒数の小数点以下桁数の設定
my $dig = $o{'.'} // 0 ; # 桁数 (表示する秒の小数点以下の桁数)
my $lay = 1e6 / 10 ** $dig if defined $dig ; # lay は英語の「置く」のつもり。100万未満のマイクロ秒に対して、割る数。
* gapproc = $o{g} ? sub (){ ($t0s, $t0p) = gettimeofday } : sub (){} ; # 50ナノ秒のオーバーヘッド。1回の実行につき。

my $HMS0 = '%02d:%02d:%02d' ; #　何度も使われる書式
my $HMSd = "$HMS0.%0${dig}d" ; #　何度も使われる書式
my $dHMS0 = "%02d-%02d-%02d $HMS0" ; 
my $dHMSd = "%02d-%02d-%02d $HMSd" ; 

do { & main ; exit } ; 

sub hms0 () { sprintf $HMS0 , @{[localtime]}[2,1,0] }
sub hmsD () { my($s,$m)=gettimeofday ; sprintf $HMSd, @{[localtime $s]}[2,1,0], int $m/$lay }
sub Dhms0 () { my @f=localtime;$f[4]+=1;$f[5]+=1900 ; sprintf $dHMS0 , @f[5,4,3,2,1,0] }
sub DhmsD () { my($s,$m)=gettimeofday ; my @f=localtime $s;$f[4]+=1;$f[5]+=1900 ; sprintf $dHMSd , @f[5,4,3,2,1,0],int $m/$lay }
sub secondsSince0 () { return sprintf "%0.0f" , time - $t0s } 
sub secondsSinceD () { return sprintf "%0.${dig}f" , tv_interval [ $t0s, $t0p ] } ;
sub timenow () ;

sub main () { 
  * timenow = $o{d} ? $dig ? * DhmsD : *Dhms0 : $dig ? * hmsD : * hms0 ; 
  * 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する)。

  Ctrl+Z で操作を中断し、2次情報として何行まで読んだのかを表示する。

 オプション : 
    -b 0 : 出力のバッファリングをしない。(Buffering)
    -c STR : 色の指定。0で色無し。"cyan","faint red bold","bright_blue"など。色名はTerm::ANSIColor を参照。
    -d : 日付も出力。yyyy-mm-dd HH:MM:SS 形式で。(date)
    -g : 開始からの秒数でなくて、1行ごとの間隔秒数を表示。(gap)
    -s : 起動してからの秒数を出力。(start)
    --sleep N : 入力から1行ずつ逐次読み取り出力して、N秒停止することを繰り返す。
    -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 の方が「プッタイム」で発音しやすい気がする。その方が記憶しやすいかも。
    * タイムゾーンの設定について、 -z が明示的に必要なようだ (Macの場合)。

=head1 AUTHOR

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

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2021-2023 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 
