#!/usr/bin/perl
use POSIX qw(strftime ceil);
use Time::HiRes qw(gettimeofday tv_interval);
##use Audio::Mixer;
use POSIX qw(mkfifo);

my $coeff = 1;
$coeff = $ARGV[0] if (defined($ARGV[0]));

##my $print_volume = 0;

my $prev_cpu_all_time = 0;
my $prev_cpu_idle_time = 0;

my @Batteries = ("BAT0","BAT1");

my $net_devices = "eth0|eth1|wlan0|tun0";
my %prev_net_time;
my $prev_net_glob_time;
my %prev_net_trans;
my %prev_net_rec;
my %down_rate;
my %up_rate;
my @net_if_to_display;

my $prev_bat_time;
my $prev_bat_val;
my $state;

my $prev_cpu_glob_time;
my $prev_cpu_value = 100;

my $prev_temp_glob_time;
my $prev_temp_value = 0;

my $prev_freq_glob_time;
my $prev_freq_value = 0;
my $prev_governor_value = 0;

my $timeout = 1;

$| = 1;

my $String_to_display;

$SIG{USR1} = sub { $prev_bat_time = undef; $prev_temp_glob_time = undef };
##$SIG{USR2} = sub { $SIG{USR2} = 'IGNORE'; $print_volume = 1 };

my $rin = '';
vec($rin,fileno(STDIN),1) = 1;

my $nfound = 0;
my $str_stdin = "";

while (1){
    $String_to_display = "";
    if ($nfound > 0){
        my $tmp_str = "";
        do{
            $str_stdin .= $tmp_str;
            sysread(STDIN,$tmp_str,1);
        }while (defined($tmp_str) and ($tmp_str ne "\n") and ($tmp_str ne ''));
        if ($tmp_str eq "\n"){
            #print("$str_stdin\n");
            $String_to_display .= "$str_stdin\n";
            $str_stdin = "";
            $timeout = 2;
        }
    }else{
##    if ($print_volume == 1){
##        $print_volume = 0;
##        my ($val1,$val2) = Audio::Mixer::get_cval('pcm');
##        print("PCM=$val1,$val2\n");
##        $SIG{USR2} = sub { $SIG{USR2} = 'IGNORE'; $print_volume = 1 };
##        select(undef,undef,undef, 1 * $coeff);
##        next;
##    }
    # % CPU used
    # /proc/stat : idle == column 4 --> calculer le nb de sec
    my $cpu_time = [gettimeofday()];
    my $cpu_elapse = tv_interval ($prev_cpu_glob_time, $cpu_time);
    if ($cpu_elapse >= 1 * $coeff){
        my $curr_all_time;
        my $curr_idle_time;
        if (open(CPU, "/proc/stat")){
            if (<CPU> =~ /^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/){
                $curr_all_time = $1 + $2 + $3 + $4;
                $curr_idle_time = $4;
                my $value = $curr_all_time - $prev_cpu_all_time;
                if ($value > 0){
                    $value = ceil(100 - (100 * ($curr_idle_time - $prev_cpu_idle_time) / $value));
                    $value = 100 if ($value > 100);
                    $value = 0 if ($value < 0);
                    $prev_cpu_idle_time = $curr_idle_time;
                    $prev_cpu_all_time = $curr_all_time;
                    $prev_cpu_value = $value;
                }
            }else{
                $prev_cpu_value = -1;
            }
            close(CPU);
        }else{
            $prev_cpu_value = -1;
        }
        $prev_cpu_glob_time = $cpu_time;
    }
    #print("$prev_cpu_value%");
    $String_to_display .= "$prev_cpu_value%";
    
    # % free memory 
    # /proc/meminfo MemFree/MemTotal
#    if (open(MEM, "/proc/meminfo")){
#        my $mem_total;
#        my $mem_free;
#        my $swap_total;
#        my $end = 0;
#        while (($_ = <MEM>) and ($end == 0)){
#            if ($_ =~ /^MemTotal:\s+(\d+)\skB$/){
#                $mem_total = $1;
#            }
#            if ($_ =~ /^MemFree:\s+(\d+)\skB$/){
#                $mem_free = $1;
#                my $value = int(100 * $mem_free / $mem_total);
#                print("Free_mem=$value% ");
#            }
##            if ($_ =~ /^Cached:\s+(\d+)\skB$/){
##            }
#            if ($_ =~ /^SwapTotal:\s+(\d+)\skB$/){
#                $swap_total = $1;
#            }
#            if ($_ =~ /^SwapFree:\s+(\d+)\skB$/){
#                my $value = int(100 * $1 / $swap_total);
#                print("Free_swap=$value%");
#                $end = 1;
#            }
#        }
#        close(MEM);
#    }
    #print(" | ");
    $String_to_display .= " | ";
    
    # network bandwidth
    # /proc/net/dev
    my $net_time = [gettimeofday()];
    my $net_elapse = tv_interval ($prev_net_glob_time, $net_time);
    if ($net_elapse >= 1 * $coeff){
        @net_if_to_display = ();
        $prev_net_glob_time = $net_time;
        if (open(NET, "/proc/net/dev")){
            while ($_ = <NET>){
                if ($_ =~ /^\s+($net_devices):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/){
                    my $time = [gettimeofday()];
                    my $elapse = tv_interval ($prev_net_time{$1}, $time);
                    $down_rate{$1} = ceil(($2-$prev_net_rec{$1})/(1024 * $elapse));
                    $up_rate{$1} = ceil(($3-$prev_net_trans{$1})/(1024 * $elapse));
                    $prev_net_rec{$1} = $2;
                    $prev_net_trans{$1} = $3;
                    $prev_net_time{$1} = $time;
                    push(@net_if_to_display, $1);
                }
            }
            close(NET);
        }
    }
    foreach my $k (@net_if_to_display){
        #print("$k:$down_rate{$k}".",$up_rate{$k}"." ") if (($prev_net_trans{$k} > 0) or ($prev_net_rec{$k} > 0));
        $String_to_display .= "$k:$down_rate{$k}".",$up_rate{$k}"." " if (($prev_net_trans{$k} > 0) or ($prev_net_rec{$k} > 0));
    }
    #print("| ");
    $String_to_display .= "| ";

    # Temperature
    my $temp_time = [gettimeofday()];
    my $temp_elapse = tv_interval ($prev_temp_glob_time, $temp_time);
    if ($temp_elapse >= 10 * $coeff){
        my $temp = 0;
        if (open(TEMP, "/proc/acpi/thermal_zone/THRM/temperature")){
            if (<TEMP> =~ /^temperature:\s+(\d+)\sC$/){
                $prev_temp_value = $1;
            }else{
                $prev_temp_value = 0;
            }
            close(TEMP);
        }else{
            $prev_temp_value = 0;
        }
        $prev_temp_glob_time = $temp_time;
    }
    #print($prev_temp_value."C ");
    $String_to_display .= $prev_temp_value."C ";
 
    # % battery
    # /proc/acpi/battery/BAT0/state
    # /proc/acpi/battery/BAT0/info
    my $time = [gettimeofday()];
    my $elapse = tv_interval ($prev_bat_time, $time);
    if ($elapse > 30 * $coeff){
        my $cumul;
        my $state_cumul = "+";
        foreach my $b (@Batteries){
            my $max_bat = 0;
            if (open(BAT, "/proc/acpi/battery/$b/info")){
                my $end = 0;
                while (($_ = <BAT>) and ($end == 0)){
                    if ($_ =~ /^last full capacity:\s+(\d+)\smWh$/){
                        $max_bat = $1;
                        $end = 1;
                    }
                }
                close(BAT);
                if ($max_bat > 0){
                    if (open(BAT, "/proc/acpi/battery/$b/state")){
                        $end = 0;
                        $state->{$b} = "+";
                        my $value_tmp = -1;
                        while (($_ = <BAT>) and ($end == 0)){
                            if ($_ =~ /^remaining capacity:\s+(\d+)\smWh$/){
                                $value_tmp = ceil(100*$1/$max_bat);
                                $value_tmp = 100 if ($value_tmp > 100);
                                $end = 1;
                            }
                            if ($_ =~ /^charging state:\s+discharging$/){
                                $state->{$b} = "-";
                                $state_cumul = "-";
                            }
                        }
                        close(BAT);
                        if ($value_tmp >= 0){
                            $prev_bat_val->{$b} = $value_tmp;
                            $cumul += $value_tmp;
                        }else{
                            $prev_bat_val->{$b} = "-";
                            $state->{$b} = "-";
                        }
                    }else{
                        $prev_bat_val->{$b} = "-";
                        $state->{$b} = "-";
                    }
                }else{
                    $prev_bat_val->{$b} = "-";
                    $state->{$b} = "-";
                }
            }else{
                $prev_bat_val->{$b} = "-";
                $state->{$b} = "-";
            }
        }
        if (defined($cumul)){
            my $limit = $cumul/$#Batteries;
            if ((($limit) <= 5) and ($state_cumul eq "-")){
                system("/home/capitn/bin/batteryAlarm.sh $limit red 1 >& /dev/null &");
            }elsif (($limit <= 10) and ($state_cumul eq "-")){
                system("/home/capitn/bin/batteryAlarm.sh $limit green 0 >& /dev/null &");
            }
        }
        $prev_bat_time = $time;
    }
    my $pre = "";
    foreach my $b (keys(%{$prev_bat_val})){
        if ($prev_bat_val->{$b} ne "-"){
            #print($pre."$b=".$state->{$b}.$prev_bat_val->{$b}."%");
            $String_to_display .= $pre."$b=".$state->{$b}.$prev_bat_val->{$b}."%";
            $pre = ",";
        }
    }

    # CPUFREQ
    my $cpufreq_time = [gettimeofday()];
    my $cpufreq_elapse = tv_interval ($prev_cpufreq_glob_time, $cpufreq_time);
    if ($cpufreq_elapse >= 1 * $coeff){
        my $freq = "0";
        if (open(FREQ, "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq")){
            $freq = <FREQ>;
            close(FREQ);
            chop($freq);
            $freq /= 1000000;
        }
        $prev_freq_value = $freq;
        $prev_freq_glob_time = $freq_time;
        
        my $gov = "undef";
        if (open(GOV, "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor")){
            $gov = <GOV>;
            close(GOV);
            chop($gov);
        }
        $prev_freq_value = $freq;
        $prev_freq_glob_time = $freq_time;
        $prev_governor_value = $gov;
    }
    #print(" (".$prev_governor_value.",".$prev_freq_value."GHz".")");
    $String_to_display .= " (".$prev_governor_value.",".$prev_freq_value."GHz".")";
    
    #print(" -- ");
    $String_to_display .= " -- ";
    #print(strftime("%F %T",localtime));
    $String_to_display .= strftime("%F %T",localtime);
    
    #print("\n");
    $String_to_display .= "\n";
    $timeout = 1;
    }
    print($String_to_display);
    $nfound = 0;
    $nfound = select($rinTmp = $rin,undef,undef, $timeout * $coeff);
}

