import os
import fcntl
import socket
import errno
from errno import EWOULDBLOCK

try:
    from OpenSSL import SSL
except ImportError:
    class SSL(object):
        class WantWriteError(object):
            pass

        class WantReadError(object):
            pass

        class ZeroReturnError(object):
            pass


def g_log(*args):
    import sys
    import greenlet
    from eventlet.greenlib import greenlet_id
    g_id = greenlet_id()
    if g_id is None:
        if greenlet.getcurrent().parent is None:
            ident = 'greenlet-main'
        else:
            g_id = id(greenlet.getcurrent())
            if g_id < 0:
                g_id += 1 + ((sys.maxint + 1) << 1)
            ident = '%08X' % (g_id,)
    else:
        ident = 'greenlet-%d' % (g_id,)
    print >>sys.stderr, '[%s] %s' % (ident, ' '.join(map(str, args)))

CONNECT_ERR = (errno.EINPROGRESS, errno.EALREADY, EWOULDBLOCK)
CONNECT_SUCCESS = (0, errno.EISCONN)
def socket_connect(descriptor, address):
    err = descriptor.connect_ex(address)
    if err in CONNECT_ERR:
        return None
    if err not in CONNECT_SUCCESS:
        raise socket.error(err, errno.errorcode[err])
    return descriptor

__original_socket__ = socket.socket

def tcp_socket():
    s = __original_socket__(socket.AF_INET, socket.SOCK_STREAM)
    set_nonblocking(s)
    return s


__original_ssl__ = socket.ssl


def wrap_ssl(sock, certificate=None, private_key=None):
    from OpenSSL import SSL
    from eventlet import wrappedfd, util
    context = SSL.Context(SSL.SSLv23_METHOD)
    print certificate, private_key
    if certificate is not None:
        context.use_certificate_file(certificate)
    if private_key is not None:
        context.use_privatekey_file(private_key)

    return wrappedfd.wrapped_fd(
        SSL.Connection(context, sock))


def wrap_socket_with_coroutine_socket():
    def new_socket(*args, **kw):
        from eventlet import wrappedfd
        s = __original_socket__(*args, **kw)
        set_nonblocking(s)
        return wrappedfd.wrapped_fd(s)
    socket.socket = new_socket

    socket.ssl = wrap_ssl


def socket_bind_and_listen(descriptor, addr=('', 0), backlog=50):
    set_reuse_addr(descriptor)
    descriptor.bind(addr)
    descriptor.listen(backlog)
    return descriptor
    
def socket_accept(descriptor):
    try:
        return descriptor.accept()
    except socket.error, e:
        if e[0] == EWOULDBLOCK:
            return None
        raise

def socket_send(descriptor, data):
    try:
        return descriptor.send(data)
    except socket.error, e:
        if e[0] == EWOULDBLOCK:
            return 0
        raise
    except SSL.WantWriteError:
        return 0

# winsock sometimes throws ENOTCONN
SOCKET_CLOSED = (errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN)
def socket_recv(descriptor, buflen):
    try:
        return descriptor.recv(buflen)
    except socket.error, e:
        if e[0] == EWOULDBLOCK:
            return None
        if e[0] in SOCKET_CLOSED:
            return ''
        raise
    except SSL.WantReadError:
        return None
    except SSL.ZeroReturnError:
        return ''

def set_reuse_addr(descriptor):
    try:
        descriptor.setsockopt(
            socket.SOL_SOCKET,
            socket.SO_REUSEADDR,
            descriptor.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1,
        )
    except socket.error:
        pass
    
def set_nonblocking(descriptor):
    if hasattr(descriptor, 'setblocking'):
        # socket
        descriptor.setblocking(0)
    else:
        # fd
        if hasattr(descriptor, 'fileno'):
            fd = descriptor.fileno()
        else:
            fd = descriptor
        flags = fcntl.fcntl(fd, fcntl.F_GETFL)
        fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
    return descriptor

