##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A WSGI Application wrapper for zope

$Id: __init__.py 82663 2008-01-03 22:52:56Z yusei $
"""
import os
import sys
import logging
import ZConfig

from zope.event import notify
from zope.interface import implements
from zope.publisher.publish import publish

from zope.app.appsetup import appsetup
from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
from zope.app.wsgi import interfaces


class WSGIPublisherApplication(object):
    """A WSGI application implementation for the zope publisher

    Instances of this class can be used as a WSGI application object.

    The class relies on a properly initialized request factory.
    """
    implements(interfaces.IWSGIApplication)

    def __init__(self, db=None, factory=HTTPPublicationRequestFactory):
        self.requestFactory = None

        if db is not None:
            self.requestFactory = factory(db)

    def __call__(self, environ, start_response):
        """See zope.app.wsgi.interfaces.IWSGIApplication"""
        request = self.requestFactory(environ['wsgi.input'], environ)

        # Let's support post-mortem debugging
        handle_errors = environ.get('wsgi.handleErrors', True)

        request = publish(request, handle_errors=handle_errors)
        response = request.response

        # Start the WSGI server response
        start_response(response.getStatusString(), response.getHeaders())

        # Return the result body iterable.
        return response.consumeBodyIter()


class PMDBWSGIPublisherApplication(WSGIPublisherApplication):

    def __call__(self, environ, start_response):
        environ['wsgi.handleErrors'] = False

        # Call the application to handle the request and write a response
        try:
            app =  super(PMDBWSGIPublisherApplication, self)
            return app.__call__(environ, start_response)
        except Exception, error:
            import sys, pdb
            print "%s:" % sys.exc_info()[0]
            print sys.exc_info()[1]
            #import zope.security.management
            #zope.security.management.restoreInteraction()
            try:
                pdb.post_mortem(sys.exc_info()[2])
                raise
            finally:
                pass #zope.security.management.endInteraction()


def config(configfile, schemafile=None, features=()):
    # Load the configuration schema
    if schemafile is None:
        schemafile = os.path.join(
            os.path.dirname(appsetup.__file__), 'schema', 'schema.xml')

    # Let's support both, an opened file and path
    if isinstance(schemafile, basestring):
        schema = ZConfig.loadSchema(schemafile)
    else:
        schema = ZConfig.loadSchemaFile(schemafile)

    # Load the configuration file
    # Let's support both, an opened file and path
    try:
        if isinstance(configfile, basestring):
            options, handlers = ZConfig.loadConfig(schema, configfile)
        else:
            options, handlers = ZConfig.loadConfigFile(schema, configfile)
    except ZConfig.ConfigurationError, msg:
        sys.stderr.write("Error: %s\n" % str(msg))
        sys.exit(2)

    # Insert all specified Python paths
    if options.path:
        sys.path[:0] = [os.path.abspath(p) for p in options.path]

    # Setup the event log
    options.eventlog()

    # Insert the devmode feature, if turned on
    if options.devmode:
        features += ('devmode',)
        logging.warning("Developer mode is enabled: this is a security risk "
            "and should NOT be enabled on production servers. Developer mode "
            "can be turned off in etc/zope.conf")

    # Execute the ZCML configuration.
    appsetup.config(options.site_definition, features=features)

    # Connect to and open the database, notify subscribers.
    db = appsetup.multi_database(options.databases)[0][0]
    notify(appsetup.interfaces.DatabaseOpened(db))

    return db

def getWSGIApplication(configfile, schemafile=None, features=(),
                       requestFactory=HTTPPublicationRequestFactory):
    db = config(configfile, schemafile, features)
    application = WSGIPublisherApplication(db, requestFactory)

    # Create the application, notify subscribers.
    notify(interfaces.WSGIPublisherApplicationCreated(application))

    return application
