from cffi import FFI
from six.moves import range as srange

ffi = FFI()

CONSTANTS = [
    "X_PROTOCOL",
    "X_PROTOCOL_REVISION",
    "X_TCP_PORT",

    "XCB_NONE",
    "XCB_COPY_FROM_PARENT",
    "XCB_CURRENT_TIME",
    "XCB_NO_SYMBOL",

    "XCB_CONN_ERROR",
    "XCB_CONN_CLOSED_EXT_NOTSUPPORTED",
    "XCB_CONN_CLOSED_MEM_INSUFFICIENT",
    "XCB_CONN_CLOSED_REQ_LEN_EXCEED",
    "XCB_CONN_CLOSED_PARSE_ERR",
#    "XCB_CONN_CLOSED_INVALID_SCREEN",
#    "XCB_CONN_CLOSED_FDPASSING_FAILED",

    "XCB_REQUEST_CHECKED",
]


# constants
ffi.cdef('\n'.join("#define %s ..." % c for c in CONSTANTS))

# types
ffi.cdef("""
    // xcb.h
    typedef struct {
        uint8_t   response_type;  /**< Type of the response */
        uint8_t  pad0;           /**< Padding */
        uint16_t sequence;       /**< Sequence number */
        uint32_t length;         /**< Length of the response */
    } xcb_generic_reply_t;

    typedef struct {
        uint8_t   response_type;  /**< Type of the response */
        uint8_t  pad0;           /**< Padding */
        uint16_t sequence;       /**< Sequence number */
        uint32_t pad[7];         /**< Padding */
        uint32_t full_sequence;  /**< full sequence */
    } xcb_generic_event_t;

    typedef struct {
        uint8_t   response_type;  /**< Type of the response */
        uint8_t   error_code;     /**< Error code */
        uint16_t sequence;       /**< Sequence number */
        uint32_t resource_id;     /** < Resource ID for requests with side effects only */
        uint16_t minor_code;      /** < Minor opcode of the failed request */
        uint8_t major_code;       /** < Major opcode of the failed request */
        uint8_t pad0;
        uint32_t pad[5];         /**< Padding */
        uint32_t full_sequence;  /**< full sequence */
    } xcb_generic_error_t;

    typedef struct {
        unsigned int sequence;  /**< Sequence number */
    } xcb_void_cookie_t;

    typedef struct xcb_auth_info_t {
        int   namelen;
        char *name;
        int   datalen;
        char *data;
    } xcb_auth_info_t;

    typedef ... xcb_connection_t;

    // xproto.h
    typedef struct xcb_query_extension_reply_t {
        uint8_t  response_type;
        uint8_t  pad0;
        uint16_t sequence;
        uint32_t length;
        uint8_t  present;
        uint8_t  major_opcode;
        uint8_t  first_event;
        uint8_t  first_error;
    } xcb_query_extension_reply_t;

    typedef struct xcb_setup_t {
        uint8_t       status; /**<  */
        uint8_t       pad0; /**<  */
        uint16_t      protocol_major_version; /**<  */
        uint16_t      protocol_minor_version; /**<  */
        uint16_t      length; /**<  */
        uint32_t      release_number; /**<  */
        uint32_t      resource_id_base; /**<  */
        uint32_t      resource_id_mask; /**<  */
        uint32_t      motion_buffer_size; /**<  */
        uint16_t      vendor_len; /**<  */
        uint16_t      maximum_request_length; /**<  */
        uint8_t       roots_len; /**<  */
        uint8_t       pixmap_formats_len; /**<  */
        uint8_t       image_byte_order; /**<  */
        uint8_t       bitmap_format_bit_order; /**<  */
        uint8_t       bitmap_format_scanline_unit; /**<  */
        uint8_t       bitmap_format_scanline_pad; /**<  */
        uint8_t       min_keycode; /**<  */
        uint8_t       max_keycode; /**<  */
        uint8_t       pad1[4]; /**<  */
    } xcb_setup_t;

    typedef uint32_t xcb_drawable_t;
    typedef uint32_t xcb_visualid_t;

    typedef struct xcb_visualtype_t {
        xcb_visualid_t visual_id; /**<  */
        uint8_t        _class; /**<  */
        uint8_t        bits_per_rgb_value; /**<  */
        uint16_t       colormap_entries; /**<  */
        uint32_t       red_mask; /**<  */
        uint32_t       green_mask; /**<  */
        uint32_t       blue_mask; /**<  */
        uint8_t        pad0[4]; /**<  */
    } xcb_visualtype_t;


    // xcbext.h
    typedef struct xcb_extension_t {
        const char *name;
        int global_id;
    } xcb_extension_t;

    typedef struct {
        size_t count;
        xcb_extension_t *ext;
        uint8_t opcode;
        uint8_t isvoid;
    } xcb_protocol_request_t;

    // sys/uio.h
    struct iovec
    {
      void *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
      size_t iov_len; /* Must be size_t (1003.1g) */
    };

    // need to manually free some things that XCB allocates
    void free(void *ptr);
""")

# connection manipulation, mostly generated with:
# grep -v '^[ \/\}#]' xcb.h | grep -v '^typedef' | grep -v '^extern'
ffi.cdef("""
    int xcb_flush(xcb_connection_t *c);
    uint32_t xcb_get_maximum_request_length(xcb_connection_t *c);
    void xcb_prefetch_maximum_request_length(xcb_connection_t *c);
    xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c);
    xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c);
    const xcb_query_extension_reply_t *xcb_get_extension_data(xcb_connection_t *c, xcb_extension_t *ext);
    const xcb_setup_t *xcb_get_setup(xcb_connection_t *c);
    int xcb_get_file_descriptor(xcb_connection_t *c);
    int xcb_connection_has_error(xcb_connection_t *c);
    xcb_connection_t *xcb_connect_to_fd(int fd, xcb_auth_info_t *auth_info);
    void xcb_disconnect(xcb_connection_t *c);
    int xcb_parse_display(const char *name, char **host, int *display, int *screen);
    xcb_connection_t *xcb_connect(const char *displayname, int *screenp);
    xcb_connection_t *xcb_connect_to_display_with_auth_info(const char *display, xcb_auth_info_t *auth, int *screen);
    uint32_t xcb_generate_id(xcb_connection_t *c);
    xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t cookie);
""")

ffi.cdef("""
    unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *request);
    void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e);
    int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
""")

C = ffi.verify("""
    #include <xcb/xcb.h>
    #include <xcb/xcbext.h>
""", libraries=['xcb'])

def bytes_to_cdata(bs):
    buf = ffi.new('char[]', bs)
    return buf

# cfficairo needs an xcb_visualtype_t
def visualtype_to_c_struct(vt):
    s = ffi.new("xcb_visualtype_t *")

    s.visual_id = vt.visual_id
    s._class = vt._class
    s.bits_per_rgb_value = vt.bits_per_rgb_value
    s.colormap_entries = vt.colormap_entries
    s.red_mask = vt.red_mask
    s.green_mask = vt.green_mask
    s.blue_mask = vt.blue_mask

    return s
