#! /usr/bin/env python
# -*- coding: utf-8 -*-

# bin/gracied
# Part of Gracie, an OpenID provider.
#
# Copyright © 2007–2010 Ben Finney <ben+python@benfinney.id.au>
# This is free software; you may copy, modify and/or distribute this work
# under the terms of the GNU General Public License, version 2 or later.
# No warranty expressed or implied. See the file ‘LICENSE.GPL-2’ for details.

""" Application behaviour for Gracie.
    """

import sys
import os.path
import optparse
import logging
import daemon
from daemon import pidlockfile

from gracie import version
from gracie.server import GracieServer
from gracie.httpserver import default_host, default_port, default_root_url


default_pidfile_path = "./gracied.pid"


class OptionParser(optparse.OptionParser):
    """ Commandline option parser for this application """

    def __init__(self):
        """ Set up a new instance """
        optparse.OptionParser.__init__(self)

        self.version = version.version_full
        self.init_options()

    def init_options(self):
        """ Set up options """

        self.add_option(
            '-V', '--version',
            action='version',
            help="Report version of program and exit.",
            )
        self.add_option(
            '--log-level',
            action='store', type='string', default="WARN",
            dest='loglevel', metavar='LEVEL',
            help="Set logging verbosity level to LEVEL."
                 " (default %default)",
            )
        self.add_option(
            '--pidfile',
            action='store', type='string', default=default_pidfile_path,
            dest='pidfile', metavar='PATH',
            help="Manage PID file at PATH."
                 " (default %default)",
            )
        self.add_option(
            '--data-dir',
            action='store', type='string', default="/var/lib/gracie",
            dest='datadir', metavar='DIR',
            help="Store server runtime data in directory DIR."
                 " (default %default)",
            )
        self.add_option(
            '--host',
            action='store', type='string', default=default_host,
            dest='host', metavar='HOST',
            help="Listen on address HOST; can be name or address."
                 " (default %default)",
            )
        self.add_option(
            '--port',
            action='store', type='int', default=default_port,
            dest='port', metavar='PORT',
            help="Listen on TCP port PORT."
                 " (default %default)",
            )
        self.add_option(
            '--root-url',
            action='store', type='string', default=default_root_url,
            dest='root_url', metavar='URL',
            help="Set root URL of the server to URL."
                 " (default %default)",
            )


class Gracie(object):
    """ Behaviour for Gracie application """

    def __init__(self, argv):
        """ Set up a new instance """
        self._process_commandline(argv)
        self._init_logging()

    def _process_commandline(self, argv):
        """ Process command line to this application """
        option_parser = OptionParser()
        (opts, args) = option_parser.parse_args(argv[1:])
        (min_args, max_args) = (0, 0)
        len_args = len(args)
        if not (min_args <= len_args <= max_args):
            message = (
                "Wrong number of arguments"
                " (%(len_args)d): %(args)r"
                ) % vars()
            option_parser.error(message)
        (self.opts, self.args) = (opts, args)

    def _init_logging(self):
        """ Initialise the logging system """
        level = getattr(logging, self.opts.loglevel, None)
        format =  "%(asctime)s %(name)s: %(levelname)s: %(message)s"
        time_format = "%F %T"
        logging.basicConfig(
            level = level,
            format = format, datefmt = time_format,
            )

    def _get_socket_params(self):
        """ Determine the server socket parameters """
        params = (self.opts.host, self.opts.port)
        return params

    def _become_daemon(self):
        """ Turn the running program into a daemon process. """
        pidfile_path = os.path.join(os.getcwd(), self.opts.pidfile)
        pidfile = pidlockfile.PIDLockFile(pidfile_path)
        files_preserve = [
            sys.stderr,
            self.server.socket_fileno(),
            ]
        self.daemon_context = daemon.DaemonContext(
            files_preserve=files_preserve,
            pidfile=pidfile,
            )
        self.daemon_context.open()

    def main(self):
        """ Run the Gracie application """
        socket_params = self._get_socket_params()
        self.server = GracieServer(socket_params, self.opts)

        self._become_daemon()
        self.server.serve_forever()


def __main__(argv=None):
    """ Mainline code for this program """

    from sys import argv as sys_argv
    if argv is None:
        argv = sys_argv

    app = Gracie(argv)
    exitcode = None
    try:
        app.main()
    except SystemExit, e:
        exitcode = e.code

    return exitcode

if __name__ == "__main__":      #pragma: nocover
    import sys
    exitcode = __main__(argv=sys.argv)
    sys.exit(exitcode)
