#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <assert.h>

#include <xf86drm.h>

#include "X11/Xlib.h"
#include "va.h"
#include "va_backend.h"

#include "va_dri.h"
#include "va_dricommon.h"

struct dri1_drawable 
{
    struct dri_drawable base;
    union dri_buffer buffer;
    int width;
    int height;
};

static struct dri_drawable * 
dri1CreateDrawable(VADriverContextP ctx, XID x_drawable)
{
    struct dri1_drawable *dri1_drawable;

    dri1_drawable = calloc(1, sizeof(*dri1_drawable));

    if (!dri1_drawable)
        return NULL;

    dri1_drawable->base.x_drawable = x_drawable;

    return &dri1_drawable->base;
}

static void 
dri1DestroyDrawable(VADriverContextP ctx, struct dri_drawable *dri_drawable)
{
    free(dri_drawable);
}

static void 
dri1SwapBuffer(VADriverContextP ctx, struct dri_drawable *dri_drawable)
{

}

static union dri_buffer *
dri1GetRenderingBuffer(VADriverContextP ctx, struct dri_drawable *dri_drawable)
{
    struct dri1_drawable *dri1_drawable = (struct dri1_drawable *)dri_drawable;

    return &dri1_drawable->buffer;
}

static void
dri1Close(VADriverContextP ctx)
{
    struct dri_state *dri_state = (struct dri_state *)ctx->drm_state;

    free_drawable_hashtable(ctx);
    VA_DRIDestroyContext(ctx->native_dpy, ctx->x11_screen, dri_state->hwContextID);
    assert(dri_state->pSAREA != MAP_FAILED);
    drmUnmap(dri_state->pSAREA, SAREA_MAX);
    assert(dri_state->base.fd >= 0);
    drmCloseOnce(dri_state->base.fd);
    VA_DRICloseConnection(ctx->native_dpy, ctx->x11_screen);
}

Bool 
isDRI1Connected(VADriverContextP ctx, char **driver_name)
{
    struct dri_state *dri_state = (struct dri_state *)ctx->drm_state;
    int direct_capable;
    int driver_major;
    int driver_minor;
    int driver_patch;
    int newlyopened;
    char *BusID;
    drm_magic_t magic;        

    *driver_name = NULL;
    dri_state->base.fd = -1;
    dri_state->pSAREA = MAP_FAILED;
    dri_state->base.auth_type = VA_NONE;

    if (!VA_DRIQueryDirectRenderingCapable(ctx->native_dpy, 
                                           ctx->x11_screen, 
                                           &direct_capable))
        goto err_out0;

    if (!direct_capable)
        goto err_out0;

    if (!VA_DRIGetClientDriverName(ctx->native_dpy, ctx->x11_screen, 
                                   &driver_major, &driver_minor,
                                   &driver_patch, driver_name))
        goto err_out0;

    if (!VA_DRIOpenConnection(ctx->native_dpy, ctx->x11_screen, 
                              &dri_state->hSAREA, &BusID))
        goto err_out0;

    
    dri_state->base.fd = drmOpenOnce(NULL, BusID, &newlyopened);
    XFree(BusID);

    if (dri_state->base.fd < 0)
        goto err_out1;


    if (drmGetMagic(dri_state->base.fd, &magic))
        goto err_out1;

    if (newlyopened && !VA_DRIAuthConnection(ctx->native_dpy, ctx->x11_screen, magic))
        goto err_out1;

    if (drmMap(dri_state->base.fd, dri_state->hSAREA, SAREA_MAX, &dri_state->pSAREA))
        goto err_out1;

    if (!VA_DRICreateContext(ctx->native_dpy, ctx->x11_screen,
                             DefaultVisual(ctx->native_dpy, ctx->x11_screen),
                             &dri_state->hwContextID, &dri_state->hwContext))
        goto err_out1;

    dri_state->base.auth_type = VA_DRI1;
    dri_state->createDrawable = dri1CreateDrawable;
    dri_state->destroyDrawable = dri1DestroyDrawable;
    dri_state->swapBuffer = dri1SwapBuffer;
    dri_state->getRenderingBuffer = dri1GetRenderingBuffer;
    dri_state->close = dri1Close;

    return True;

err_out1:
    if (dri_state->pSAREA != MAP_FAILED)
        drmUnmap(dri_state->pSAREA, SAREA_MAX);

    if (dri_state->base.fd >= 0)
        drmCloseOnce(dri_state->base.fd);

    VA_DRICloseConnection(ctx->native_dpy, ctx->x11_screen);

err_out0:
    if (*driver_name)
        XFree(*driver_name);

    dri_state->pSAREA = MAP_FAILED;
    dri_state->base.fd = -1;
    *driver_name = NULL;
    
    return False;
}