// -kr -i4 -ts4 -bls -bl -bli0

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#include <pixlib2.h>
#include "pixdriver.h"

#include <kos32sys.h>

void* load_library(const char *name);


#define DISPLAY_VERSION         0x0200	/*      2.00     */

#define SRV_GETVERSION              0
#define SRV_GET_CAPS                3

#define BUFFER_SIZE(n) ((n)*sizeof(uint32_t))
#define __ALIGN_MASK(x,mask)  (((x)+(mask))&~(mask))
#define ALIGN(x,a)            __ALIGN_MASK(x,(typeof(x))(a)-1)

#define to_surface(x) (surface_t*)((x)->handle)

typedef struct
{
	uint32_t width;
	uint32_t height;
	void *data;
	uint32_t pitch;
	uint32_t bo;
	uint32_t bo_size;
	uint32_t flags;
} surface_t;

static uint32_t service;
static uint32_t hw_caps;
static struct pix_driver pix_driver;

uint32_t init_pixlib(uint32_t caps)
{
    void *lib;
    uint32_t (*drventry)(uint32_t service, struct pix_driver *driver);

	uint32_t api_version;
	ioctl_t io;

	if (service != 0)
		return caps & hw_caps;

	service = get_service("DISPLAY");
	if (service == 0)
		goto fail;

	io.handle = service;
	io.io_code = SRV_GETVERSION;
	io.input = NULL;
	io.inp_size = 0;
	io.output = &api_version;
	io.out_size = BUFFER_SIZE(1);

	if (call_service(&io) != 0)
		goto fail;

	if ((DISPLAY_VERSION > (api_version & 0xFFFF)) ||
		(DISPLAY_VERSION < (api_version >> 16)))
		goto fail;

    lib = load_library("intel-sna.drv");
    if(lib == 0)
        lib = load_library("intel-uxa.drv");
    if(lib == 0)
        goto fail;

    drventry = get_proc_address(lib, "DrvInit");

    if( drventry == NULL)
        goto fail;

    hw_caps = drventry(service, &pix_driver);

	if (hw_caps)
		printf("2D caps %s%s%s\n",
			   (hw_caps & HW_BIT_BLIT) != 0 ? "HW_BIT_BLIT " : "",
			   (hw_caps & HW_TEX_BLIT) != 0 ? "HW_TEX_BLIT " : "",
			   (hw_caps & HW_VID_BLIT) != 0 ? "HW_VID_BLIT " : "");

	return caps & hw_caps;

  fail:
	service = 0;
	return 0;
};

void done_pixlib()
{
	if (hw_caps != 0)
        pix_driver.fini();
};

int create_bitmap(bitmap_t * bitmap)
{
	uint32_t size, bo_size;
	uint32_t pitch, max_pitch;
	void *buffer;
	surface_t *sf;

	bitmap->handle = -1;
	bitmap->data = (void *) -1;
	bitmap->pitch = -1;

    if (bitmap->flags &= hw_caps)
       return pix_driver.create_bitmap(bitmap);

	pitch = ALIGN(bitmap->width * 4, 16);
	max_pitch = ALIGN(bitmap->max_width * 4, 16);

	size = ALIGN(pitch * bitmap->height, 4096);
	bo_size = ALIGN(max_pitch * bitmap->max_height, 4096);

	if (bo_size < size)
		bo_size = size;

	sf = malloc(sizeof(*sf));
	if (sf == NULL)
		return -1;

	buffer = user_alloc(bo_size);

	if (buffer == NULL)
	{
		free(sf);
		return -1;
	};

	sf->width = bitmap->width;
	sf->height = bitmap->height;
	sf->data = buffer;
	sf->pitch = pitch;
	sf->bo = 0;
	sf->bo_size = bo_size;
	sf->flags = bitmap->flags;

	bitmap->handle = (uint32_t) sf;

//    printf("create bitmap %p handle %p data %p  w %d h%d\n",
//            bitmap, bitmap->handle, bitmap->data, bitmap->width, bitmap->height);

	return 0;
};

int destroy_bitmap(bitmap_t * bitmap)
{
	surface_t *sf = to_surface(bitmap);

    if (sf->flags & hw_caps)
        return pix_driver.destroy_bitmap(bitmap);

	user_free(sf->data);
	free(sf);

	bitmap->handle = -1;
	bitmap->data = (void *) -1;
	bitmap->pitch = -1;

	return 0;
};

int lock_bitmap(bitmap_t * bitmap)
{
	surface_t *sf = to_surface(bitmap);

	if (bitmap->data != (void *) -1)
		return 0;

    if (sf->flags & hw_caps)
       return pix_driver.lock_bitmap(bitmap);

	bitmap->data = sf->data;
	bitmap->pitch = sf->pitch;

	return 0;
};

int blit_bitmap(bitmap_t * bitmap, int dst_x, int dst_y,
                int w, int h, int src_x, int src_y)
{
	struct blit_call bc;
	int ret;

	surface_t *sf = to_surface(bitmap);

    if (sf->flags & hw_caps & HW_BIT_BLIT)
        return pix_driver.blit(bitmap, 0, 0, dst_x, dst_y, w, h, src_x, src_y);

	bc.dstx     = dst_x;
	bc.dsty     = dst_y;
	bc.w        = w;
	bc.h        = h;
	bc.srcx     = 0;
	bc.srcy     = 0;
	bc.srcw     = w;
	bc.srch     = h;
	bc.stride   = sf->pitch;
	bc.bitmap   = sf->data;

	__asm__ __volatile__(
    "int $0x40":"=a"(ret):"a"(73), "b"(0x00),
	"c"(&bc):"memory");

	bitmap->data = (void *) -1;
	bitmap->pitch = -1;

	return ret;
};

int fplay_blit_bitmap(bitmap_t * bitmap, int dst_x, int dst_y, int w, int h)
{
	struct blit_call bc;
	int ret;

	surface_t *sf = to_surface(bitmap);

    if (sf->flags & hw_caps & HW_TEX_BLIT)
        return pix_driver.blit(bitmap, 1, 1, dst_x, dst_y, w, h, 0, 0);

	bc.dstx = dst_x;
	bc.dsty = dst_y;
	bc.w = w;
	bc.h = h;
	bc.srcx = 0;
	bc.srcy = 0;
	bc.srcw = w;
	bc.srch = h;
	bc.stride = sf->pitch;
	bc.bitmap = sf->data;

	__asm__ __volatile__(
    "int $0x40":"=a"(ret):"a"(73), "b"(0x00),
	"c"(&bc):"memory");

	bitmap->data = (void *) -1;
	bitmap->pitch = -1;

	return ret;
};

int resize_bitmap(bitmap_t * bitmap)
{
	uint32_t size;
	uint32_t pitch;

//    printf("%s\n", __FUNCTION__);

	surface_t *sf = to_surface(bitmap);

    if (sf->flags & hw_caps)
        return pix_driver.resize_bitmap(bitmap);

	pitch = ALIGN(bitmap->width * 4, 16);
	size = ALIGN(pitch * bitmap->height, 4096);

	bitmap->pitch = -1;
	bitmap->data = (void *) -1;

	if (size > sf->bo_size)
	{
		sf->data = user_realloc(sf->data, size);	/* grow buffer */
		if (sf->data == NULL)
			return -1;

		sf->bo_size = size;
	} else if (size < sf->bo_size)
		user_unmap(sf->data, size, sf->bo_size - size);	/* unmap unused pages */

	sf->width  = bitmap->width;
	sf->height = bitmap->height;
	sf->pitch  = pitch;

	return 0;
};

int sna_create_mask()
{
    return 0;
}