#!/usr/bin/python2.7 -tt

"""
Copyright (c) 2014-2015 by Johannes Findeisen
Copyright (c) 2011-2013 by Johannes Findeisen and Rafael Timmerberg

This file is part of Linspector (http://linspector.org).

Linspector is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""


__version__ = "0.16/AMNESIA"

import argparse
import copy
import datetime
import logging
import logging.handlers
import os
import os.path as path
import sys

from linspector.config.parser import FullConfigParser
from linspector.core.interface import LinspectorInterface
from linspector.core.job import LinspectorJob
#from linspector.core.scheduler import LinspectorScheduler
from linspector.frontends.lish import LishFrontend
from linspector.tasks.task import TaskExecutor

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor

logger = logging.getLogger(__name__)


def parse_args():
    parser = argparse.ArgumentParser(
        description="linspector is for monitoring the vital information of hosts, services and devices in a network.",
        epilog="linspector is not some program expecting computers to run! visit http://linspector.org for more "
               "information.",
        prog="linspector")

    parser.add_argument("--version", action="version", version="%(prog)s " + str(__version__))

    parser.add_argument("config", metavar="CONFIGFILE",
                        help="the configfile to use")

    parser.add_argument("-n", "--nocolor",
                        help="disable colored output")

    parser.add_argument("-l", "--logfile", default="./log/linspector.log", metavar="FILE",
                        help="set logfile to use (default: ./log/linspector.log)")

    parser.add_argument("-c", "--logcount", default=5, type=int,
                        help="maximum number of logfiles in rotation (default: 5)")

    parser.add_argument("-m", "--logsize", default=10485760, type=int,
                        help="maximum logfile size in bytes (default: 10485760)")

    parser.add_argument("-t", "--threads", default=3500, type=int,
                        help="maximum number of scheduler threads (default: 3500)")

    parser.add_argument("-p", "--processes", default=0, type=int,
                        help="number of scheduler processes (default: 0)")

    parser.add_argument("-x", "--delay", default=1.135791, type=float,
                        help="seconds delay between scheduled jobs (default: 1.135791)")

    output = parser.add_mutually_exclusive_group()
    output.add_argument("-q", "--quiet", action="store_const", dest="loglevel", const=logging.ERROR,
                        help="output only errors")

    output.add_argument("-w", "--warning", action="store_const", dest="loglevel", const=logging.WARNING,
                        help="output warnings")

    output.add_argument("-v", "--verbose", action="store_const", dest="loglevel", const=logging.INFO,
                        help="output info messages")

    output.add_argument("-d", "--debug", action="store_const", dest="loglevel", const=logging.DEBUG,
                        help="output debug messages")
    output.set_defaults(loglevel=logging.INFO)
    return parser.parse_args()


def handle_job(job):
    job.handle_call()


def main():
    global lin_conf
    args = parse_args()

    logfile = path.expanduser(args.logfile)
    if not path.exists(path.dirname(logfile)):
        os.makedirs(path.dirname(logfile))

    root_logger = logging.getLogger()
    formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s:%(message)s")
    handler = logging.handlers.RotatingFileHandler(args.logfile, maxBytes=args.logsize, backupCount=args.logcount)
    handler.setFormatter(formatter)
    root_logger.addHandler(handler)
    root_logger.setLevel(args.loglevel)

    try:
        config_parser = FullConfigParser()
        lin_conf, core = config_parser.parse_config(args.config)
    except Exception, msg:
        print("Configuration error: " + str(msg) + ". Exiting now.")
        logger.error(msg)
        exit()
    
    jobstores = {
        'memory': MemoryJobStore()
    }
    executors = {
        'default': ThreadPoolExecutor(args.threads),
        'processpool': ProcessPoolExecutor(args.processes)
    }
    job_defaults = {
        'max_instances': 10000
    }
    scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)

    TaskExecutor.Instance()

    job_count = 0
    for layout in lin_conf.get_enabled_layouts():
        for hostgroup in layout.get_hostgroups():
            job_count += (hostgroup.get_services().__len__() * hostgroup.get_hosts().__len__())

    start_date = datetime.datetime.now() + datetime.timedelta(seconds=30)

    time_delta = 0
    jobs = []
    count = 0
    percent = float(0)
    increment = float(100) / float(job_count)
    for layout in lin_conf.get_enabled_layouts():
        for hostgroup in layout.get_hostgroups():
            for service in hostgroup.get_services():
                for host in hostgroup.get_hosts():
                    for period in service.get_periods():
                        percent += increment
                        if percent >= count:
                            sys.stdout.write("Scheduling jobs. Be patient... " + str(count) + "%\r")
                            sys.stdout.flush()
                            while percent > count:
                                count += 1

                        time_delta += float(args.delay)
                        new_start_date = start_date + datetime.timedelta(seconds=time_delta)
                        job = LinspectorJob(service,
                                            host,
                                            hostgroup.get_members(),
                                            core,
                                            hostgroup)

                        scheduler_job = period.createJob(scheduler, job, handle_job, start_date=new_start_date)

                        if scheduler_job is not None:
                            scheduler_job.misfire_grace_time = 2
                            job.set_job(scheduler_job)
                            jobs.append(job)

    print("\nStarting Scheduler...")

    scheduler.start()

    print("Scheduled " + str(job_count) + " jobs")

    interface = LinspectorInterface(jobs, scheduler, lin_conf, root_logger, __version__)

    if "jsonrpc_backend" in core and core["jsonrpc_backend"]:
        from linspector.backends.jsonrpc import JsonrpcBackend
        jsonrpc = JsonrpcBackend(interface, core)
        jsonrpc.daemon = True
        jsonrpc.start()

    LishFrontend(interface)

    print("Shutting down... please wait!")

    logger.debug("shutting down scheduler")

    shutdown_wait = True
    if "shutdown_wait" in core:
        shutdown_wait = core["shutdown_wait"]

    scheduler.shutdown(wait=shutdown_wait)
    logging.shutdown()
    if shutdown_wait:
        TaskExecutor.Instance().stop()
    else:
        TaskExecutor.Instance().stop_immediately()


if __name__ == "__main__":
    main()
