#!/usr/bin/env jython
# EAM : config -> skychunk DONE

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

from config import Config
from skychunk import Skychunk
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):

        # set up config object (global config information, also parse command-line options, eg -test / -t)
        self.config = Config()

        # are the args ok? all programs require a config name
        if len(sys.argv) < 2: 
            self.printUsage()
            self.exitProgram("Wrong args")

        # some class constants
        self.HOST = socket.gethostname()
        self.PID = os.getpid()
        self.PAUSEPERIOD = 60
        self.HOURS = None
        self.MINUTES = None
        self.SECONDS = None

        self.config.setScratchDbLeaf(self.HOST)

        self.logger = self.config.getLogger(self.HOST, self.PID, logToStdout, logToFile)

        # argv[1] -> self.skychunk.name
        self.skychunk = Skychunk(self.logger)

        # create connection to databases database
        try:
            self.ippToPspsDb = IppToPspsDb(self.logger, self.config, self.skychunk)
        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.config.programName + "' started")
        self.logger.infoPair("Host", self.HOST)
        self.logger.infoPair("PID", "%d" % self.PID)
        self.logger.infoPair("Skychunk", self.skychunk.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] + " <skychunkName|all|edit> " + extra
        try:
            self.logger.errorPair("Usage:", msg)
        except: 
            print "*** Usage: " + msg

    '''
    Refreshes the skychunk then recreates objects with new settings
    '''
    def refreshSkychunk(self):

        # new skychunk?
        if self.skychunk.createNewSkychunk: 
            self.ippToPspsDb.editSkychunk()
            self.ippToPspsDb.setSkychunkForThisClient(self.skychunk.name, self.HOST, self.PID)
            self.skychunk.createNewSkychunk = False

        # if we are rotating skychunks, then look for next active one in list
        if self.skychunk.rotateSkychunks:
            self.ippToPspsDb.getSkychunkForThisClient(self.HOST, self.PID)
            skychunks = self.ippToPspsDb.getActiveSkychunkList()
             
            i = 1
            for skychunk in skychunks:
               if skychunk == self.skychunk.name: break
               i += 1

            if i >= len(skychunks): newSkychunk = skychunks[0]
            else: newSkychunk = skychunks[i]

            self.ippToPspsDb.setSkychunkForThisClient(newSkychunk, self.HOST, self.PID)

        return self.ippToPspsDb.readSkychunk(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 skychunk data
    '''
    def checkClientStatus(self):

        self.ippToPspsDb.updateClient(self.config.programName, 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 skychunk or we have set it to pause
        firstTimeIn = True
        while not self.refreshSkychunk() or self.ippToPspsDb.isPaused(self.HOST, self.PID):
            self.ippToPspsDb.updateClient(self.config.programName, 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.config.programName + " 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)
