import sys

from eventlet import greenio
from eventlet import greenthread
from eventlet import greenpool
from eventlet.green import socket
from eventlet.support import greenlets as greenlet

def connect(addr, family=socket.AF_INET, bind=None):
    """Convenience function for opening client sockets.

    :param addr: Address of the server to connect to.  For TCP sockets, this is a (host, port) tuple.
    :param family: Socket family, optional.  See :mod:`socket` documentation for available families.
    :param bind: Local address to bind to, optional.
    :return: The connected green socket object.
    """
    sock = socket.socket(family, socket.SOCK_STREAM)
    if bind is not None:
        sock.bind(bind)
    sock.connect(addr)
    return sock


def listen(addr, family=socket.AF_INET, backlog=50):
    """Convenience function for opening server sockets.  This
    socket can be used in :func:`~eventlet.serve` or a custom ``accept()`` loop.

    Sets SO_REUSEADDR on the socket to save on annoyance. 

    :param addr: Address to listen on.  For TCP sockets, this is a (host, port)  tuple.
    :param family: Socket family, optional.  See :mod:`socket` documentation for available families.
    :param backlog: The maximum number of queued connections. Should be at least 1; the maximum value is system-dependent.
    :return: The listening green socket object.
    """
    sock = socket.socket(family, socket.SOCK_STREAM)
    if sys.platform[:3] != "win":
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(addr)
    sock.listen(backlog)
    return sock

class StopServe(Exception):
    """Exception class used for quitting :func:`~eventlet.serve` gracefully."""
    pass

def _stop_checker(t, server_gt, conn):
    try:
        try:
            t.wait()
        finally:
            conn.close()
    except greenlet.GreenletExit:
        pass
    except Exception:
        greenthread.kill(server_gt, *sys.exc_info())

def serve(sock, handle, concurrency=1000):
    """Runs a server on the supplied socket.  Calls the function *handle* in a 
    separate greenthread for every incoming client connection.  *handle* takes
    two arguments: the client socket object, and the client address::
        
        def myhandle(client_sock, client_addr):
            print "client connected", client_addr
        
        eventlet.serve(eventlet.listen(('127.0.0.1', 9999)), myhandle)
        
    Returning from *handle* closes the client socket.
     
    :func:`serve` blocks the calling greenthread; it won't return until 
    the server completes.  If you desire an immediate return,
    spawn a new greenthread for :func:`serve`.
      
    Any uncaught exceptions raised in *handle* are raised as exceptions 
    from :func:`serve`, terminating the server, so be sure to be aware of the 
    exceptions your application can raise.  The return value of *handle* is 
    ignored.      
      
    Raise a :class:`~eventlet.StopServe` exception to gracefully terminate the 
    server -- that's the only way to get the server() function to return rather 
    than raise.

    The value in *concurrency* controls the maximum number of
    greenthreads that will be open at any time handling requests.  When
    the server hits the concurrency limit, it stops accepting new
    connections until the existing ones complete.
    """
    pool = greenpool.GreenPool(concurrency)
    server_gt = greenthread.getcurrent()
 
    while True:
        try:
            conn, addr = sock.accept()
            gt = pool.spawn(handle, conn, addr)
            gt.link(_stop_checker, server_gt, conn)
            conn, addr, gt = None, None, None
        except StopServe:
            return


def wrap_ssl(sock, *a, **kw):
    """Convenience function for converting a regular socket into an
    SSL socket.  Has the same interface as :func:`ssl.wrap_socket`,
    but works on 2.5 or earlier, using PyOpenSSL (though note that it
    ignores the *cert_reqs*, *ssl_version*, *ca_certs*,
    *do_handshake_on_connect*, and *suppress_ragged_eofs* arguments
    when using PyOpenSSL).

    The preferred idiom is to call wrap_ssl directly on the creation
    method, e.g., ``wrap_ssl(connect(addr))`` or
    ``wrap_ssl(listen(addr), server_side=True)``. This way there is
    no "naked" socket sitting around to accidentally corrupt the SSL
    session.

    :return Green SSL object.
    """
    return wrap_ssl_impl(sock, *a, **kw)

try:
    from eventlet.green import ssl
    wrap_ssl_impl = ssl.wrap_socket
except ImportError:
    # < 2.6, trying PyOpenSSL
    try:
        from eventlet.green.OpenSSL import SSL
        def wrap_ssl_impl(sock, keyfile=None, certfile=None, server_side=False,
                          cert_reqs=None, ssl_version=None, ca_certs=None,
                          do_handshake_on_connect=True, 
                          suppress_ragged_eofs=True, ciphers=None):
            # theoretically the ssl_version could be respected in this
            # next line
            context = SSL.Context(SSL.SSLv23_METHOD)
            if certfile is not None:
                context.use_certificate_file(certfile)
            if keyfile is not None:
                context.use_privatekey_file(keyfile)
            context.set_verify(SSL.VERIFY_NONE, lambda *x: True)

            connection = SSL.Connection(context, sock)
            if server_side:
                connection.set_accept_state()
            else:
                connection.set_connect_state()
            return connection
    except ImportError:
        def wrap_ssl_impl(*a, **kw):
            raise ImportError("To use SSL with Eventlet, "
                              "you must install PyOpenSSL or use Python 2.6 or later.")
        
