#!/usr/bin/python
# GPL. (C) 2014 Paolo Patruno.

# This program is free software; you can redistribute it and/or modify 
# it under the terms of the GNU General Public License as published by 
# the Free Software Foundation; either version 2 of the License, or 
# (at your option) any later version. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
# GNU General Public License for more details. 
# 
# You should have received a copy of the GNU General Public License 
# along with this program; if not, write to the Free Software 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
#

import signal
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'rmap.settings'
import django
django.setup()


from rmap import daemon
import rmap.settings
import fcntl


#add option for topic and dsn
class  mydaemon(daemon.Daemon):

    def optionparser(self):
        op = super(mydaemon, self).optionparser()
	op.add_option("-d", "--datalevel",dest="datalevel", help="sample or report: define the istance to run: select topic, dns,logfile, errorfile and lockfile (default %default)", default="sample")
	op.add_option("-s", "--stationtype",dest="stationtype", help="fixed or mobile: define the istance to run: select topic, dns,logfile, errorfile and lockfile (default %default)", default="fixed")
	#op.add_option("-t", "--topic",dest="topic", help="topic root to subscribe on mqtt broker (default %default)", default="rmap")
	#op.add_option("-d", "--dsn",dest="dsn", help="topic root to subscribe on mqtt broker (default %default)", default=rmap.settings.dsnrmap)
        return op 	  				 

mqtt2dballed = mydaemon(
        stdin="/dev/null",
        stdout=rmap.settings.logfilemqtt2dballed,
        stderr=rmap.settings.errfilemqtt2dballed,
        pidfile=rmap.settings.lockfilemqtt2dballed,
        user=rmap.settings.usermqtt2dballed,
        group=rmap.settings.groupmqtt2dballed
)


# we want to read line from pipe; with bloking file we can wait forever
# set pipe to a  a non-blocking file
def nonblockingfile(pipe):

    # make a pipe a non-blocking file
    fd = pipe.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

# catch signal to terminate the process
class GracefulKiller:
    kill_now = False
    def __init__(self):
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

    def exit_gracefully(self,signum, frame):
        self.kill_now = True


def main(self):

    import os,sys,time
    import logging,logging.handlers
    import subprocess
    import traceback

    #arm the signal handler
    killer = GracefulKiller()

    # configure the logger
    formatter=logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s",datefmt="%Y-%m-%d %H:%M:%S")
    handler = logging.handlers.RotatingFileHandler(self.options.stdout, maxBytes=5000000, backupCount=10)
    handler.setFormatter(formatter)
    
    # Add the log message handler to the root logger
    logging.getLogger().addHandler(handler)
    logging.getLogger().setLevel(logging.INFO)

    logging.info('Starting up mqtt2dballed')

    dsndict={"sample":{},"report":{}}
    dsndict["sample"]["fixed"]=rmap.settings.dsnsample_fixed
    dsndict["sample"]["mobile"]=rmap.settings.dsnsample_mobile
    dsndict["report"]["fixed"]=rmap.settings.dsnreport_fixed
    dsndict["report"]["mobile"]=rmap.settings.dsnreport_mobile

    
    if not (self.options.datalevel in dsndict.keys()):
        logging.error('Invalid dsn')
        sys.stdout.write("Invalid dsn\n")
        return False

    if not (self.options.stationtype in dsndict[self.options.datalevel].keys()):
        logging.error('Invalid dsn')
        sys.stdout.write("Invalid dsn\n")
        return False

    
    topicdict={"sample":{},"report":{}}
    topicdict["sample"]["fixed"]  = "{}/+/+/{}/#".format(rmap.settings.topicsample,"fixed")
    topicdict["sample"]["mobile"] = "{}/+/+/{}/#".format(rmap.settings.topicsample,"mobile")
    topicdict["report"]["fixed"]  = "{}/+/+/{}/#".format(rmap.settings.topicreport,"fixed")
    topicdict["report"]["mobile"] = "{}/+/+/{}/#".format(rmap.settings.topicreport,"mobile")


    if not (self.options.datalevel in topicdict.keys()):
        logging.error('Invalid topic')
        sys.stdout.write("Invalid topic\n")
        return False

    if not (self.options.stationtype in topicdict[self.options.datalevel].keys()):
        logging.error('Invalid topic')
        sys.stdout.write("Invalid topic\n")
        return False


    dsn   = dsndict[self.options.datalevel][self.options.stationtype]
    logging.info('DSN: %s'% dsn)
    topic = topicdict[self.options.datalevel][self.options.stationtype]
    logging.info('Topic: %s'% topic)
    
    # compatibility with old topic: to be removed !

    oldtopic1="None"
    oldtopic2="None"
    oldtopic3="None"

    if self.options.datalevel == "sample":
        oldtopic1="rmap/#"
        oldtopic2="sample/+/+/rmap/#"
        oldtopic3="sample/+/+/arpae/#"
    elif self.options.datalevel == "report":
        oldtopic1="fixed/#"
        oldtopic2="report/+/+/rmap/#"

    if self.options.stationtype == "mobile":
        oldtopic1="mobile/#"

        
    # infinite loop: restart p1 or p2 if terminate with error
    while True:

        try:
            p1 = subprocess.Popen(["mqtt2bufr","-t",oldtopic1,"-t",oldtopic2,"-t",oldtopic3,"-t",topic],cwd=self.cwd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            #p1 = subprocess.Popen(["mqtt2bufr","-h","rmap.cc","-t","rmap/#","-t","mobile/#"],cwd=self.cwd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            nonblockingfile(p1.stderr)

            while p1.poll() is None:
                p2 = subprocess.Popen(["/usr/local/bin/pipe2dba","-o",dsn],cwd=self.cwd,env={"NONE_NONE_NONE_DBA_FORTRAN_TRANSACTION":"True"}, stdin=p1.stdout, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                #p2 = subprocess.Popen(["dbamsg","dump"],cwd=self.cwd, stdin=p1.stdout, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                #p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits. Do not use inside this while loop

                nonblockingfile(p2.stderr)
                nonblockingfile(p2.stdout)

                while  p2.poll() is None:

                    # write on logging messages from p1 stderr
                    #p1.stderr.flush()
                    while True:
                        try:
                            line = p1.stderr.readline()
                        except:
                            line=""
                        if line:
                            #print 'Got data:', line
                            logging.error(line.rstrip("\n"))
                        else:
                            break

                    # write on logging messages from p2 stderr
                    #p2.stderr.flush()
                    while True:
                        try:
                            line = p2.stderr.readline()
                        except:
                            line=""
                        if line:
                            #print 'Got data:', line
                            logging.error(line.rstrip("\n"))
                        else:
                            break

                    # write on logging messages from p2 stdout
                    #p2.stdout.flush()
                    while True:
                        try:
                            line = p2.stdout.readline()
                        except:
                            line=""
                        if line:
                            #print 'Got data:', line
                            logging.info(line.rstrip("\n"))
                        else:
                            break

                    # check if we have to terminate
                    if killer.kill_now:
                        p1.kill()  
                        p2.kill()  
                        logging.info("killed by signal\n")
                        logging.info('Subprocess finished')
                        return False
                    
                    time.sleep(1)

        # log and retry on exception 
        except Exception as exception:
            logging.error('Exception occured: ' + str(exception))
            logging.error(traceback.format_exc())
            logging.error('Subprocess failed')
            time.sleep(10)
        # terminate on keyboard interrupt
        except KeyboardInterrupt:
            sys.stdout.write("keyboard interrupt\n")
            logging.info("keyboard interrupt\n")
            p1.wait()
            p2.wait()
            #p2.kill()
            return False
        # terminate without error
        else:
            # no exception was raised
            logging.info('Subprocess finished')
        finally:
            
            # check if we have to terminate together with other exceptions
            if killer.kill_now:
                try:
                    p1.kill()
                except:
                    pass
                try:
                    p2.kill()
                except:
                    pass
                logging.info("killed by signal\n")
                logging.info('Subprocess finished')
                return False
            

if __name__ == '__main__':

    import sys, os
    
    mqtt2dballed.cwd=os.getcwd()

    if mqtt2dballed.service():

        sys.stdout.write("Daemon started with pid %d\n" % os.getpid())

        main(mqtt2dballed)  # (this code was run as script)
            
        for proc in mqtt2dballed.procs:
            proc.wait()

        sys.stdout.write("Daemon stoppped\n")
        sys.exit(0)
