#!/Users/toshiyuki-shimono/.plenv/versions/5.14.4/bin/perl5.14.4
use 5.001 ; use strict ; use warnings ;
use Getopt::Std ; getopts '01y:!.:,:~@:' , \my%o ;
use Term::ANSIColor qw[ color :constants ] ; $Term::ANSIColor::AUTORESET = 1 ;
use FindBin qw[ $Script ] ;

sub sigint () ; # Ctrl-C が押下された時の動作
sub dtfmtlocal () ; # 現在の日時を返す
sub choreOpt () ; # コマンド引数の処理
sub mainCore() ; 
sub y_init () ; 
sub y_filter ($) ;
sub cyc_rep () ;

my $printOut ; # 各行でどういう出力処理をするか。オプションで書き換わる。
my $prevline ; # 直前行の内容
my $count = 1 ;
my @y_ranges ; 
my $cyc_len = $o{'@'} // 1e5 ; # 何行毎にレポートを発生させるか。
my ( $time0 , $time00 ) = ( time ) x 2 ; 

y_init ; 
choreOpt ;
mainCore ; 

sub mainCore () { 
    $SIG{INT} = \&sigint ;
    chomp ( $prevline = <> ) ;
    $count = 1 ; 
    while ( <> ) {
      cyc_rep if $cyc_len && $. % $cyc_len == 0 ; 
      chomp ; 
      if ( $prevline eq $_ && $count != $o{'.'} ) {
          $count ++ ;
          next ;
      }
      $printOut -> () if y_filter ( $count ) ; 
      $count = 1 ;
      $prevline = $_  ;
    }
    $printOut -> () if y_filter ( $count ) ; 
}

sub choreOpt () { 
    $| = 1 if $o{'!'} ;
    $o{'.'} //= 0 ;
    $o{','} = do { $o{','}//= "\t"  ; eval qq[qq[$o{','}]] } ; 
    
    $printOut = 
        $o{0} ? 
            sub { print "$prevline$/" } :
        $o{1} ?
            sub { print "$count$/" } : 
        $o{'~'} ? 
            sub { print "$prevline$o{','}$count$/" } :  
            sub { print "$count$o{','}$prevline$/" } ;  
}

sub sigint () {
    alarm 0 ;
    print STDERR $/ , color('yellow') , "$count+\t$prevline\t" , dtfmtlocal , "($Script)" ,"$/" , color( 'reset') ;
    $SIG{INT} = sub { sigint ; die "`$Script' stopped because of double `Ctrl-C' signals within 1 second.\n" } ;
    alarm 1 ;
    $SIG{ALRM} = sub { $SIG{ INT } = \&sigint } ;
}

sub dtfmtlocal () {
    my @f = @{[localtime]}[5,4,3,2,1,0] ;
    $f[0] += 1900 ;
    $f[1] += 1 ;
    return sprintf ( "%04u-%02u-%02u %02u:%02u:%02u" , @f ) ;
}

# 次の2個の関数は、出力すべき値の範囲をフィルターの様に指定する。
sub y_init ( ) { 
    $o{y} //= '' ;
    my @ranges = split /,/ , $o{y} , -1 ; 
    grep { $_ = $_ . ".." . $_ unless m/\.\./ } @ranges ; # .. で囲まれていない数 x は x..xに書き換え
    for ( @ranges ) { 
        m/^(\d*)\.\.(\d*)/ ; 
        push @y_ranges , [  ( $1 || 1 ) ,  ( $2 || "Inf" ) ] ; 
    }
}

sub y_filter ( $ ) { 
    return not 0 unless @y_ranges ; # 指定が無かった場合はとにかく真を変えす。
    for ( @y_ranges ) { 
        return not 0 if $_->[0] <= $_[0] && $_[0] <= $_->[1] ;
    }
    return not 1 ; 
}

sub cyc_rep ( ) {
    use FindBin '$Script' ;
    $| = 1 ; 
    my $num = $. ; 
    $num =~ s/(?<=\d)(?=(\d\d\d)+($|\D))/,/g ; # 3桁毎にコンマで区切る
    print STDERR GREEN $num , ":\t" , sprintf "%02d:%02d:%02d" , ( localtime )[2,1,0] ;  #  <-- 標準出力に書込み
    print STDERR "\t" , GREEN  time - $time0 , " sec.\t($Script)" ; 
    $time0 = time ;
    print STDERR "\n" ;
}

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


=encoding utf8

=head1
 $0

  Unixコマンドの  uniq を使った uniq -c の代替。
  同じ入力が連続して何個続いた火を出力する。
  実行中に Ctrl+Cキーを押したら途中までの結果を表示する。
  (文字コードの問題で uniq がうまく機能しない場合も使える。)

オプション :

 [入力上のオプション]
  -, str : 個数とそれに対応する文字列の区切りを str に変更。未指定ならタブ文字。
  -. num  : 同じ行がnum行に達するごとに表示をして、カウントを連続個数にリセット。

 [出力上のオプション]
  -0  : 個数を表示せずに、同一であるとみなした行のみを1行表示する。
  -1  : 個数のみ表示する。
  -~  : 個数を行の先頭ではなくて、末尾に表示する。
  -!  : 出力をバッファにためずに、すぐフラッシュアウトするようにする。
  -y ranges : 個数を出力する際に、出力する値の範囲をranges で値の範囲を指定できる。範囲は,と..で指定する。

 [副次的な情報出力についてのオプション]
  -@ num : 一定行数を読み取る毎に、補助情報を表示。未指定なら1000万行ずつ。出力しない場合は 0 を指定。  


=cut

