import math
import time
import logging
import smtplib
import sys
import os
import datetime

from ipptopsps import IppToPsps
from czardb import CzarDb
from plot import Plot

'''
Metrics class
'''
class Metrics(IppToPsps):

    '''
    Constructor
    '''
    def __init__(self, argv):
        super(Metrics, self).__init__(argv)

        if len(sys.argv) > 2: self.parsePollTimeArg(sys.argv[2])
    
        self.czarDb = CzarDb(self.logger, self.config)
        self.plot = Plot(self.logger, self.config, self.ippToPspsDb)
   
    '''
    Run...
    '''
    def run(self):

        while True:
    
            self.logger.infoTitle("ippToPsps loading summary")
            now = datetime.datetime.now()
            self.logger.infoPair("Time now", now.strftime("%Y-%m-%d %H:%M:%S"))
            self.logger.infoPair("Config", self.config.name)
            self.logger.infoPair("Loading epoch", self.config.epoch)
            self.logger.infoPair("DVO label", self.config.dvoLabel)
            self.config.printBoxCoords()
        
            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
            self.logger.info("|Type| batches per hour | last 24 hours | per day this week | detections/batch | last published |")
            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
            rateP2 = self.printStats("P2")
            rateST = self.printStats("ST")
            rateOB = self.printStats("OB")
            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
        
            self.logger.info("")
        
            stages = self.ippToPspsDb.getStages()
            colCount = len(stages)
            self.writeTableSeparator(colCount)
            sys.stdout.write("|Type")
        
            for stage in stages: sys.stdout.write("|%19s" % stage)
            sys.stdout.write("|\n")
            sys.stdout.write("|    ")
            for stage in stages: sys.stdout.write("| Pend  Succ  Fail  ")
            sys.stdout.write("|\n")
            self.writeTableSeparator(colCount)
            pendP2Processed = self.printTableRow(stages, "P2")
            pendSTProcessed = self.printTableRow(stages, "ST")
            pendOBProcessed = self.printTableRow(stages, "OB")
            self.writeTableSeparator(colCount)

            self.logger.info("")
            try: self.logger.infoPair("Estimated time for P2s", "%.1f hours" % (pendP2Processed / rateP2)) 
            except: pass
            try: self.logger.infoPair("Estimated time for STs", "%.1f hours" % (pendSTProcessed / rateST)) 
            except: pass
            try: self.logger.infoPair("Estimated time for OBs", "%.1f hours" % (pendOBProcessed / rateOB)) 
            except: pass
       
            self.checkClientStatus()

            # create a density plot of pending stuff for czartool
            self.plot.createDensityPlot("P2", True)
            self.plot.createDensityPlot("ST", True)
            self.plot.createDensityPlot("OB", True)

            # some clean-up before going to sleep
            self.ippToPspsDb.purgeDeadClients()
            if not self.waitForPollTime(): break


    '''
    Formats an int to a string
    '''
    def getIntAsString(self, value):
        if value == 0: return ""
        return str(value)
    
    '''
    Prints (to the log) one row of data (one row being one batch-type)
    '''
    def printTableRow(self, stages, batchType):
    
        pending = self.ippToPspsDb.getPendingIds(batchType)

        sys.stdout.write("| %2s " % (batchType))
   
        firstStage = True
        for stage in stages:
    
           # get lists. Use unions with prev list to make sure the right stuff is included
           if firstStage:
               success = self.ippToPspsDb.getStageIDs(batchType, stage, 1)
               fail = list(set(self.ippToPspsDb.getStageIDs(batchType, stage, -1)) - set(success))
           else: 
               success = list(set(self.ippToPspsDb.getStageIDs(batchType, stage, 1)) & set(prevList)) 
               fail = list(set(self.ippToPspsDb.getStageIDs(batchType, stage, -1)) & set(prevList) - set(success))
               pending = list(set(prevList) - set(success) - set(fail))
    
           # count stuff
           numSuccess = len(success)
           numFail = len(fail)
           numPending = len(pending)
    
           #if stage == 'loaded_to_datastore' and batchType == "P2":
           #    for n in fail: print n
    
           str = "\033[1;34m%5s\033[1;m \033[1;32m%5s\033[1;m \033[1;31m%5s\033[1;m  " % (
                   self.getIntAsString(numPending), 
                   self.getIntAsString(numSuccess), 
                   self.getIntAsString(numFail))
           sys.stdout.write("|%-19s" % str)
    
           prevList = success
    
           self.czarDb.insertStats(stage, self.config.name, batchType, numPending, numSuccess, numFail)
    
           if stage == 'processed': numPendingProcessed = numPending
    
           firstStage = False

        sys.stdout.write("|\n")
    
        return numPendingProcessed
    
    '''
    Writes table separator
    '''
    def writeTableSeparator(self, colCount):
    
        sys.stdout.write("+----")
        for i in range(0, colCount): sys.stdout.write("+-------------------")
        sys.stdout.write("+\n")
   
    '''
    Safe divide function
    '''
    def divide(self, num, denom):
        if denom == 0: return 0
        else: return num / denom

    '''
    Prints various timing stats for this batch type
    '''
    def printStats(self, batchType):
    
        rate = self.ippToPspsDb.countBatches(batchType, "1 HOUR") / 1.0 
   

        batchesInLast24Hours = self.ippToPspsDb.countBatches(batchType, "24 HOUR")
        detectionsInLast24Hours = self.ippToPspsDb.getTotalDetectionsPublished(batchType, "24 HOUR")

        self.logger.info("| %2s | %16.1f | %13d | %17.1f | %16d | %14s |" % 
                (batchType,
                 rate,
                 batchesInLast24Hours,
                 self.ippToPspsDb.countBatches(batchType, "1 WEEK") / 7.0, 
                 self.divide(detectionsInLast24Hours, batchesInLast24Hours),
                 self.ippToPspsDb.getTimeOfLastBatchPublished(batchType)))
    
        return rate
   

    '''
    Overrides base-class version
    '''
    def printUsage(self):
        super(Metrics, self).printUsage("[<hours>]")



'''
Start of program.
'''
try:
    metrics = Metrics(sys.argv)
    metrics.run()
    metrics.exitProgram("completed")
except: pass



