#!/usr/bin/env jython

import signal
import sys
import os
import time
import logging
import socket

from config import Config
from pslogger import PSLogger
from ipptopspsdb import IppToPspsDb

'''
Abstract base class for all ippToPsps programs
Handles command-line args, setting up of Config and logger objects, clean exiting etc
'''
class IppToPsps(object):

    '''
    Constructor
    '''
    def __init__(self, argv, logToStdout=1, logToFile=0):

        # are the arsg ok? all programs require a config name
        if len(sys.argv) < 2: 
            self.printUsage()
            self.exitProgram("Wrong args")
            
        # some class constants
        self.PROGNAME = sys.argv[0]
        CONFIGNAME = sys.argv[1]
        self.HOST = socket.gethostname()
        self.PID = os.getpid()
        self.PAUSEPERIOD = 60
        self.HOURS = None
        self.MINUTES = None
        self.SECONDS = None

        self.createNewConfig = False
        self.rotateConfigs = False
        # a new config?
        if CONFIGNAME == "edit": self.createNewConfig = True
        # should we rotate configs for this program?
        elif CONFIGNAME == "all": self.rotateConfigs = True

        # set up config object
        self.config = Config(self.PROGNAME, CONFIGNAME)
        self.logger = self.config.getLogger(self.HOST, self.PID, logToStdout, logToFile)

        # create connection to databases database
        try:
            self.ippToPspsDb = IppToPspsDb(self.logger, self.config)
        except:
            self.exitProgram("Could not connect to ipptopsps Db")
            raise

        self.checkClientStatus()

        # catch Ctrl-C signal
        signal.signal(signal.SIGINT, self.signal_handler)

        # title for log
        self.logger.infoSeparator()
        self.logger.infoTitle("ippToPsps '" + self.PROGNAME + "' started")
        self.logger.infoPair("Host", self.HOST)
        self.logger.infoPair("PID", "%d" % self.PID)
        self.logger.infoPair("Config", self.config.name)

    '''
    Parses a poll-time argument string and stored as class variables
    '''
    def parsePollTimeArg(self, hours):

        try:
            self.HOURS = float(hours)
            self.MINUTES = self.HOURS * 60.0
            self.SECONDS = self.MINUTES * 60.0
        except:
            pass

    '''
    Waits for user defined poll time
    '''
    def waitForPollTime(self):

        # if not POLL time set, then return False
        if not self.SECONDS: return False

        if self.HOURS < 1.0:
            str = "%.1f minutes" % self.MINUTES
        else:
            str = "%.1f hours" % self.HOURS

        self.logger.infoPair("Waiting for", str)
        time.sleep(self.SECONDS)

        return True

    '''
    Prints usage to stdout
    '''
    def printUsage(self, extra=""):

        # write message to log or stdout if no logger set up
        msg = sys.argv[0] + " <configName|all|edit> " + extra
        try:
            self.logger.errorPair("Usage:", msg)
        except: 
            print "*** Usage: " + msg

    '''
    Refreshes the config then recreates objects with new settings
    '''
    def refreshConfig(self):

        # new config?
        if self.createNewConfig: 
            self.ippToPspsDb.editConfig()
            self.ippToPspsDb.setConfigForThisClient(self.config.name, self.HOST, self.PID)
            self.createNewConfig = False

        # if we are rotating configs, then look for next active one in list
        if self.rotateConfigs:
            self.ippToPspsDb.getConfigForThisClient(self.HOST, self.PID)
            configs = self.ippToPspsDb.getActiveConfigList()
             
            i = 1
            for config in configs:
               if config == self.config.name: break
               i += 1

            if i >= len(configs): newConfig = configs[0]
            else: newConfig = configs[i]

            self.ippToPspsDb.setConfigForThisClient(newConfig, self.HOST, self.PID)

        return self.ippToPspsDb.readConfig(self.HOST, self.PID)

    '''
    All implementing subclasses (programs) should call this method once in a while to see if it should be paused or killed, otherwise this updates the clients table with current time and reloads the config data
    '''
    def checkClientStatus(self):

        self.ippToPspsDb.updateClient(self.PROGNAME, self.HOST, self.PID)

        if self.ippToPspsDb.isKilled(self.HOST, self.PID): 
            self.exitProgram("killed via Db")
       
        # this loop pauses the process if we have no config or we have set it to pause
        firstTimeIn = True
        while not self.refreshConfig() or self.ippToPspsDb.isPaused(self.HOST, self.PID):

            self.ippToPspsDb.updateClient(self.PROGNAME, self.HOST, self.PID)
            if self.ippToPspsDb.isKilled(self.HOST, self.PID): 
                self.exitProgram("killed while paused")
            if firstTimeIn: self.logger.infoPair("Paused. Will check again every", "%d secs" % self.PAUSEPERIOD)
            firstTimeIn = False
            time.sleep(self.PAUSEPERIOD)


    '''
    Handler for Ctrl-C signal
    '''
    def signal_handler(self, signal, frame):

       self.exitProgram("Ctrl-C")


    '''
    Exits the program cleanly, removes info from ipptopsps database
    '''
    def exitProgram(self, exitReason="quit"):

        # write message to log or stdout if no logger set up
        try:
            self.logger.infoPair(self.PROGNAME + " exited", exitReason)
        except: 
            print "*** Program exited: " + exitReason

        # remove from database if possible
        try:
            self.ippToPspsDb.removeClient(self.HOST, self.PID)
        except:
            pass

        sys.exit(0)
   

