#!/usr/bin/perl -w

###########################################################################
#
# Main polling program for czartool. Grabs stuff from various sources every few minutes.
#
###########################################################################

use warnings;
use strict;
use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use POSIX qw/strftime/;

# local classes
use czartool::Config;
use czartool::CzarDb;
use czartool::Gpc1Db;
use czartool::Pantasks;
use czartool::Nebulous;
use czartool::Plotter;
use czartool::DayMetrics;
use czartool::MetricsIndex;

my $period = 60;
my $save_temps = 0;

GetOptions (
        "period|p=s" => \$period, # TODO more Db args
        );

my $config = new czartool::Config();
my $czarDb = $config->getCzarDbInstance(); 
my $gpc1Db = $config->getGpc1Instance;
my $nebulous = new czartool::Nebulous($czarDb);
my $pantasks = new czartool::Pantasks();
my $plotter = czartool::Plotter->new_file($config, $config->getGnuplotPath(), $save_temps); 


my @stages = (
        "chip", 
        "cam", 
        "fake", 
        "warp", 
        "stack", 
        "staticsky", 
        "diff", 
        "dist", 
        "pub",
        "chipbackground");

my @ippToPspsStages = (
        "processed",
        "loaded_to_datastore",
        "loaded_to_ODM",
        "merge_worthy",
        "deleted_datastore",
        "deleted_dxlayer",
        "merged",
        "deleted_local");

my @allStages = (@stages, @ippToPspsStages);

timePoll($period);

###########################################################################
#
# Updates the dates from pantasks for all interested servers 
#
###########################################################################
sub updateDates {

    print "* Updating dates\n";
    my @servers = ("stdscience", "registration");

    my $server = undef;
    $czarDb->emptyServerDates();
    foreach $server (@servers) {

        my @dates = @{$pantasks->getDates($server)};
        if (@dates) { 

            $czarDb->updateServerDates($server, \@dates);
        }
        else {

            print "WARNING: No dates to update for '$server'\n";
        }
    }
}

###########################################################################
#
# Updates the labels from pantasks for all interested servers 
#
###########################################################################
sub updateLabels {

    print "* Updating labels\n";
    my @servers = ("stdscience", "distribution", "publishing", "update");

    my $server = undef;
    foreach $server (@servers) {

        my @labels = @{$pantasks->getLabels($server)};
        if (@labels) { 

            $czarDb->updateCurrentLabels($server, \@labels);
        }
        else {

            print "WARNING: No labels to update for '$server'\n";
        }
    }
}

###########################################################################
#
# Updates pantasks server status TODO should really get info for all servers at once 
#
###########################################################################
sub updateServerStatus {
    print "* Checking all pantasks servers\n";

    my $servers = $pantasks->getServerList();

    my $server = undef;
    my $alive = undef;
    my $running = undef;
    foreach $server (@{$servers}) {

        $pantasks->getServerStatus($server, \$alive, \$running);
        $czarDb->updateServerStatus($server, $alive, $running);
    }
}

###########################################################################
#
# Polls with provided period (seconds) 
#
###########################################################################
sub timePoll {
    my ($period) = @_;

    my $label;
    my $new;
    my $full;
    my $faults;
    my $stage;
    my $query = undef;
    my $str = undef;
    my $labels = undef;
    my $updateLabels = undef;
    my $row = undef;
    my $begin = undef;
    my $end = undef;
    my $priority = undef;
    my $newState = undef;
    my $nsStatus = undef;
    my $today = undef;
    my $yesterday = undef;
    my $newDayTime = $config->getMetricsStartTime();
    my $lastDayDailyTasks = "2010-01-01";

    # main polling loop
    while (1) {

        # sort out times
        $today = strftime('%Y-%m-%d', localtime);
        $yesterday = $czarDb->subtractInterval($today, "1 DAY");
        $end = $czarDb->getNowTimestamp();

        # if before 18:00 today, then start plots from 18:00 yesterday
        if ($czarDb->isBefore($end, "$today $newDayTime")) {$begin = "$yesterday $newDayTime";}
        # if after 18:00 today, then perform some daily tasks and start plots from 18:00 today
        else {

            $begin = "$today $newDayTime";

            # check whether yesterday was cleaned. if not, cleanup tables and optimize
            if ($lastDayDailyTasks ne $yesterday) {

                print "* performing daily tasks (clean-up, metrics)\n";

                # create metrics for last 24 hours
                print "* Creating metrics for last 24 hours\n";
                my $dayMetrics = new czartool::DayMetrics($config, 1, 0, $today); 
                $dayMetrics->writeHTML();

                # now update metrics index page
                my $metricsIndex = new czartool::MetricsIndex($config, 1, 0); 
                $metricsIndex->writeHTML();

                # now cleanup tables from yesterday and optimize
                print "* Performing database cleanup\n";
                $czarDb->cleanupDateRange($yesterday, $yesterday, $config->getCzarCleanupInterval());
                print "* Optimizing the database\n";
                $czarDb->optimize();
                $lastDayDailyTasks = $yesterday;
            }
        }

        # check nightly science status
        print "* Checking nightly science status\n";
        if (!$pantasks->getNightlyScienceStatus(\$nsStatus)) {$nsStatus = "Unknown";}
        $czarDb->updateNightlyScience($nsStatus);

        # check nebulous
        print "* Checking Nebulous\n";
        $nebulous->updateClusterSpaceInfo();
        $plotter->plotStorageTimeSeries($czarDb->subtractInterval($begin, "1 WEEK") , $end);
        $plotter->plotDiskUsageHistogram();
        updateServerStatus();

        # check pantasks dates
        updateDates();

        # check labels
        updateLabels();

        # servers to check
        my @serversToCheck = ("stdscience", "update");

        my $thisServer = undef;
        foreach $thisServer (@serversToCheck) {

            if ($thisServer eq "update") {$newState = "update";}
            else {$newState = "new";}

            # deal with stdscience labels
            if (!$czarDb->getCurrentLabels($thisServer, \$labels)) {next;}
            my $size = @{$labels};
            if($size > 0) {

                # get priority
                foreach $row ( @{$labels} ) {
                    my ($label) = @{$row};
                    $priority = $gpc1Db->getPriority($label);
                    $czarDb->setLabelPriority($label, $priority);
                }

                updateAllStages($thisServer, $newState, $labels, $begin, $end);
                if ($thisServer eq "stdscience") {createPlots($thisServer, $labels, $begin, $end);}
            }
            else { print "* WARNING: no $thisServer labels found in Db\n";}
        }

        print "--------------------------------------------------------------------------\n";
        #print "* Going to sleep\n";
        #sleep($period);
        #print "* Waking up\n";
    };
}

###########################################################################
#
# Loops through labels and creates time series and histogram plots
#
###########################################################################
sub createPlots {
    my ($server, $rows, $begin, $end) = @_;

    my $stage = undef;
    my $row = undef;

    print "* Generating plots\n";

    # get ippToPsps labels
    my $ippToPspsLabels = undef;
    $czarDb->getCurrentIppToPspsLabels($begin, $end, \$ippToPspsLabels);
    $plotter->setIppToPspsMode();
    
    # create ippToPsps labels
    foreach $stage (@ippToPspsStages) {
        foreach $row ( @{$ippToPspsLabels} ) {
            my ($label) = @{$row};
            
            chomp($label);
            $plotter->createTimeSeries($label, $stage, $begin, $end, 1, 0, 1);
        }
    }

    # create plots for each label for all stages for ippToPsps
    foreach $row ( @{$ippToPspsLabels} ) {
        my ($label) = @{$row};

        $plotter->createTimeSeries($label, undef, $begin, $end, 1, 1, 1);
    }


    # create plots for each label for each stage
    $plotter->setStandardMode();
    foreach $stage (@stages) {
        foreach $row ( @{$rows} ) {
            my ($label) = @{$row};

            chomp($label);
            $plotter->createTimeSeries($label, $stage, $begin, $end, 1, 0, 1);
        }
    }

    my $allServerLabels = undef;

    # create plots for each label for all stages
    foreach $row ( @{$rows} ) {
        my ($label) = @{$row};

        $plotter->createTimeSeries($label, undef, $begin, $end, 1, 1, 1);
    }

    $allServerLabels = "all_".$server."_labels";

    $plotter->createTimeSeries($allServerLabels, undef, $begin, $end, 1, 0, 1);

    foreach $stage (@stages) {

        $plotter->createTimeSeries($allServerLabels, $stage, $begin, $end, 1, 0, 1); # TODO must be a neater way...
    }
}

###########################################################################
#
# Loops through some labels and updates processed/pending/faults in the Db
#
###########################################################################
sub updateAllStages {
    my ($labelServer, $newState, $rows, $begin, $end) = @_;

    print "* Updating stage data\n";
    my $totalNew = undef;
    my $totalFaults = undef;
    my $totalFull = undef;
    my $stage = undef;
    my $reverting = 0;
    my $row = undef;
    my $new = undef;
    my $full = undef;
    my $faults = undef;
    my $server = undef;
    my $state = undef;
    my $today = strftime('%Y-%m-%d', localtime);

    foreach $stage (@stages) {

        $server = $pantasks->getServerForThisStage($stage);
        $pantasks->getRevertStatus($stage, \$reverting);
        $czarDb->updateRevertStatus($stage, $reverting);

        print "* Checking labels for $stage stage\n";

        $totalNew=$totalFaults=$totalFull=0;
        foreach $row ( @{$rows} ) {
            my ($label) = @{$row};

            chomp($label);

            if ($stage eq "burntool") {

                $new = $full = $faults = 0;
                if ($labelServer eq "stdscience") {

                    $full = $gpc1Db->countRegisteredExposures($label, $today);
                }
            }
            else {

                $new = $gpc1Db->countExposures($label, $stage, $newState);
                $full = $gpc1Db->countExposures($label, $stage, "full");
                $faults = $gpc1Db->countFaults($label, $stage, $newState);
            }
            #printf("%s  %s, %s, %d, %d\n", $labelServer, $label, $stage, $new, $faults);
            $totalNew += $new;
            $totalFull += $full;
            $totalFaults += $faults;

            $czarDb->insertNewTimeData($stage, $label, $new, $full, $faults);
        }

        $czarDb->insertNewTimeData($stage, "all_".$labelServer."_labels", $totalNew, $totalFull, $totalFaults);
    }
}

