//============================================================
//
//  draw13.c - SDL 1.3 drawing implementation
//
//  Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
//  Visit http://mamedev.org for licensing and usage restrictions.
//
//  SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================

// standard C headers
#include <math.h>
#include <stdio.h>

// MAME headers
#include "emu.h"
#include "options.h"

// standard SDL headers
#include "sdlinc.h"

// OSD headers
#include "osdsdl.h"
#include "window.h"

//============================================================
//  DEBUGGING
//============================================================

//============================================================
//  CONSTANTS
//============================================================

#define STAT_PIXEL_THRESHOLD (150*150)

enum
{
	TEXTURE_TYPE_NONE,
	TEXTURE_TYPE_PLAIN,
	TEXTURE_TYPE_SURFACE
};


//============================================================
//  MACROS
//============================================================

#define IS_OPAQUE(a)		(a >= 1.0f)
#define IS_TRANSPARENT(a)	(a <  0.0001f)

#define MAX4(a, b, c, d) MAX(a, MAX(b, MAX(c, d)))
#define MIN4(a, b, c, d) MIN(a, MIN(b, MIN(c, d)))


//============================================================
//  TYPES
//============================================================


typedef struct _quad_setup_data quad_setup_data;
struct _quad_setup_data
{
	INT32			dudx, dvdx, dudy, dvdy;
	INT32			startu, startv;
	INT32			rotwidth, rotheight;
};

typedef struct _texture_info texture_info;

typedef void (*texture_copy_func)(texture_info *texture, const render_texinfo *texsource);

typedef struct _copy_info copy_info;
struct _copy_info {
	int 				src_fmt;
	Uint32				dst_fmt;
	int					dst_bpp;
	int					rotate;
	texture_copy_func	func;
	Uint32				bm_mask;
	const char			*srcname;
	const char			*dstname;
	/* Statistics */
	UINT64				pixel_count;
	INT64				time;
	int					samples;
	int					perf;
	/* list */
	copy_info			*next;
};

/* texture_info holds information about a texture */
struct _texture_info
{
	texture_info *		next;				// next texture in the list

	HashT				hash;				// hash value for the texture (must be >= pointer size)
	UINT32				flags;				// rendering flags
	render_texinfo		texinfo;			// copy of the texture info

	int					rawwidth, rawheight;// raw width/height of the texture

	int					format;				// texture format
	void				*pixels;			// pixels for the texture
	int					pitch;
	int					pixels_own;			// do we own / allocated it ?

	SDL_Texture			*texture_id;

	copy_info			*copyinfo;
	Uint32				sdl_access;
	SDL_BlendMode		sdl_blendmode;
	quad_setup_data		setup;
	int					is_rotated;

	osd_ticks_t			last_access;
};

/* sdl_info is the information about SDL for the current screen */
typedef struct _sdl_info sdl_info;
struct _sdl_info
{
	INT32			blittimer;
	UINT32			extra_flags;

	SDL_Renderer	*sdl_renderer;
	texture_info *	texlist;				// list of active textures
	INT32			texture_max_width;  	// texture maximum width
	INT32			texture_max_height; 	// texture maximum height

	float			last_hofs;
	float			last_vofs;

	// resize information

	UINT8			resize_pending;
	UINT32			resize_width;
	UINT32			resize_height;

	// Stats
	INT64			last_blit_time;
	INT64			last_blit_pixels;
};

//============================================================
//  PROTOTYPES
//============================================================

// core functions

static void draw13_exit(void);
static void draw13_attach(sdl_draw_info *info, sdl_window_info *window);
static int draw13_window_create(sdl_window_info *window, int width, int height);
static void draw13_window_resize(sdl_window_info *window, int width, int height);
static void draw13_window_destroy(sdl_window_info *window);
static int draw13_window_draw(sdl_window_info *window, UINT32 dc, int update);
static render_primitive_list &draw13_window_get_primitives(sdl_window_info *window);
static void draw13_destroy_all_textures(sdl_window_info *window);
static void draw13_window_clear(sdl_window_info *window);
static int draw13_xy_to_render_target(sdl_window_info *window, int x, int y, int *xt, int *yt);
static void draw13_destroy_texture(sdl_info *sdl, texture_info *texture);

//============================================================
//  Textures
//============================================================

static void texture_set_data(sdl_info *sdl, texture_info *texture, const render_texinfo *texsource, UINT32 flags);
static texture_info *texture_create(sdl_window_info *window, const render_texinfo *texsource, quad_setup_data *setup, UINT32 flags);
static texture_info *texture_find(sdl_info *sdl, const render_primitive *prim, quad_setup_data *setup);
static texture_info * texture_update(sdl_window_info *window, const render_primitive *prim);


//============================================================
//  TEXCOPY FUNCS
//============================================================

#include "blit13.h"

//============================================================
//  STATIC VARIABLES
//============================================================

#define SDL_TEXFORMAT_LAST SDL_TEXFORMAT_PALETTE16A
#define BM_ALL (-1)
//( SDL_BLENDMODE_MASK | SDL_BLENDMODE_BLEND | SDL_BLENDMODE_ADD | SDL_BLENDMODE_MOD)

#define texcopy_NULL NULL
#define ENTRY(a,b,c,d,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, c, d, texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, 0}
#define ENTRY_BM(a,b,c,d,f,bm) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, c, d, texcopy_ ## f, bm, #a, #b, 0, 0, 0, 0}
#define ENTRY_LR(a,b,c,d,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, c, d, texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, -1}

static copy_info blit_info_default[] =
{
	/* no rotation */
	ENTRY(ARGB32,			ARGB8888,	4, 0, NULL),
	ENTRY_LR(ARGB32,		RGB888, 	4, 0, argb32_rgb32),
	/* Entry for primarily for directfb */
	ENTRY_BM(ARGB32,		RGB888, 	4, 0, argb32_rgb32, SDL_BLENDMODE_ADD),
	ENTRY_BM(ARGB32,		RGB888, 	4, 0, argb32_rgb32, SDL_BLENDMODE_MOD),
	ENTRY_BM(ARGB32,		RGB888, 	4, 0, argb32_rgb32, SDL_BLENDMODE_NONE),

	ENTRY(RGB32,			ARGB8888,	4, 0, rgb32_argb32),
	ENTRY(RGB32,			RGB888, 	4, 0, NULL),

	ENTRY(RGB32_PALETTED,	ARGB8888,	4, 0, rgb32pal_argb32),
	ENTRY(RGB32_PALETTED,	RGB888, 	4, 0, rgb32pal_argb32),

	ENTRY(YUY16,			UYVY,		2, 0, NULL /* yuv16_uyvy*/),
	ENTRY(YUY16,			YUY2,		2, 0, yuv16_yuy2),
	ENTRY(YUY16,			YVYU,		2, 0, yuv16_yvyu),
	ENTRY(YUY16,			ARGB8888,	4, 0, yuv16_argb32),
	ENTRY(YUY16,			RGB888,		4, 0, yuv16pal_argb32),

	ENTRY(YUY16_PALETTED,	UYVY,		2, 0, yuv16pal_uyvy),
	ENTRY(YUY16_PALETTED,	YUY2,		2, 0, yuv16pal_yuy2),
	ENTRY(YUY16_PALETTED,	YVYU,		2, 0, yuv16pal_yvyu),
	ENTRY(YUY16_PALETTED,	ARGB8888,	4, 0, yuv16pal_argb32),
	ENTRY(YUY16_PALETTED,	RGB888, 	4, 0, yuv16pal_argb32),

	ENTRY(PALETTE16,		ARGB8888,	4, 0, pal16_argb32),
	ENTRY(PALETTE16,		RGB888, 	4, 0, pal16_argb32),

	ENTRY(RGB15,			RGB555, 	2, 0, NULL /* rgb15_argb1555 */),
	ENTRY(RGB15,			ARGB1555,	2, 0, rgb15_argb1555),
	ENTRY(RGB15,			ARGB8888,	4, 0, rgb15_argb32),
	ENTRY(RGB15,			RGB888, 	4, 0, rgb15_argb32),

	ENTRY(RGB15_PALETTED,	ARGB8888,	4, 0, rgb15pal_argb32),
	ENTRY(RGB15_PALETTED,	RGB888, 	4, 0, rgb15pal_argb32),

	ENTRY(PALETTE16A,		ARGB8888,	4, 0, pal16a_argb32),
	ENTRY(PALETTE16A,		RGB888, 	4, 0, pal16a_rgb32),

	/* rotation */
	ENTRY(ARGB32,			ARGB8888,	4, 1, rot_argb32_argb32),
	ENTRY_LR(ARGB32,		RGB888, 	4, 1, rot_argb32_rgb32),
	/* Entry for primarily for directfb */
	ENTRY_BM(ARGB32,		RGB888, 	4, 1, rot_argb32_rgb32, SDL_BLENDMODE_ADD),
	ENTRY_BM(ARGB32,		RGB888, 	4, 1, rot_argb32_rgb32, SDL_BLENDMODE_MOD),
	ENTRY_BM(ARGB32,		RGB888, 	4, 1, rot_argb32_rgb32, SDL_BLENDMODE_NONE),

	ENTRY(RGB32,			ARGB8888,	4, 1, rot_rgb32_argb32),
	ENTRY(RGB32,			RGB888, 	4, 1, rot_argb32_argb32),

	ENTRY(RGB32_PALETTED,	ARGB8888,	4, 1, rot_rgb32pal_argb32),
	ENTRY(RGB32_PALETTED,	RGB888, 	4, 1, rot_rgb32pal_argb32),

	ENTRY(YUY16,			ARGB8888,	4, 1, rot_yuv16_argb32),
	ENTRY(YUY16,			RGB888,		4, 1, rot_yuv16_argb32),

	ENTRY(YUY16_PALETTED,	ARGB8888,	4, 1, rot_yuv16pal_argb32),
	ENTRY(YUY16_PALETTED,	RGB888,		4, 1, rot_yuv16pal_argb32),

	ENTRY(PALETTE16,		ARGB8888,	4, 1, rot_pal16_argb32),
	ENTRY(PALETTE16,		RGB888, 	4, 1, rot_pal16_argb32),

	ENTRY(RGB15,			RGB555, 	2, 1, rot_rgb15_argb1555),
	ENTRY(RGB15,			ARGB1555,	2, 1, rot_rgb15_argb1555),
	ENTRY(RGB15,			ARGB8888,	4, 1, rot_rgb15_argb32),
	ENTRY(RGB15,			RGB888, 	4, 1, rot_rgb15_argb32),

	ENTRY(RGB15_PALETTED,	ARGB8888,	4, 1, rot_rgb15pal_argb32),
	ENTRY(RGB15_PALETTED,	RGB888, 	4, 1, rot_rgb15pal_argb32),

	ENTRY(PALETTE16A,		ARGB8888,	4, 1, rot_pal16a_argb32),
	ENTRY(PALETTE16A,		RGB888, 	4, 1, rot_pal16a_rgb32),

{ -1 },
};

static copy_info blit_info_16bpp[] =
{
	/* no rotation */
	ENTRY(PALETTE16,		RGB555, 	2, 0, pal16_argb1555),
	ENTRY(PALETTE16,		ARGB1555,	2, 0, pal16_argb1555),

	ENTRY(RGB15_PALETTED,	RGB555, 	2, 0, rgb15pal_argb1555),
	ENTRY(RGB15_PALETTED,	ARGB1555,	2, 0, rgb15pal_argb1555),

	/* rotation */
	ENTRY(PALETTE16,		RGB555, 	2, 1, rot_pal16_argb1555),
	ENTRY(PALETTE16,		ARGB1555,	2, 1, rot_pal16_argb1555),

	ENTRY(RGB15_PALETTED,	RGB555, 	2, 1, rot_rgb15pal_argb1555),
	ENTRY(RGB15_PALETTED,	ARGB1555,	2, 1, rot_rgb15pal_argb1555),

{ -1 },
};

static copy_info *blit_info[SDL_TEXFORMAT_LAST+1];

static struct
{
	Uint32	format;
	int		status;
} fmt_support[30] = { { 0, 0 } };


//============================================================
//  INLINES
//============================================================


INLINE float round_nearest(float f)
{
	return floor(f + 0.5f);
}

INLINE HashT texture_compute_hash(const render_texinfo *texture, UINT32 flags)
{
	return (HashT)texture->base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK));
}

INLINE SDL_BlendMode map_blendmode(int blendmode)
{
	switch (blendmode)
	{
		case BLENDMODE_NONE:
			return SDL_BLENDMODE_NONE;
		case BLENDMODE_ALPHA:
			return SDL_BLENDMODE_BLEND;
		case BLENDMODE_RGB_MULTIPLY:
			return SDL_BLENDMODE_MOD;
		case BLENDMODE_ADD:
			return SDL_BLENDMODE_ADD;
		default:
			mame_printf_warning("Unknown Blendmode %d", blendmode);
	}
	return SDL_BLENDMODE_NONE;
}

INLINE void set_coloralphamode(SDL_Texture	*texture_id, const render_color *color)
{
	UINT32 sr = (UINT32)(255.0f * color->r);
	UINT32 sg = (UINT32)(255.0f * color->g);
	UINT32 sb = (UINT32)(255.0f * color->b);
	UINT32 sa = (UINT32)(255.0f * color->a);


	if (color->r >= 1.0f && color->g >= 1.0f && color->b >= 1.0f && IS_OPAQUE(color->a))
	{
		SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF);
		SDL_SetTextureAlphaMod(texture_id, 0xFF);
	}
	/* coloring-only case */
	else if (IS_OPAQUE(color->a))
	{
		SDL_SetTextureColorMod(texture_id, sr, sg, sb);
		SDL_SetTextureAlphaMod(texture_id, 0xFF);
	}
	/* alpha and/or coloring case */
	else if (!IS_TRANSPARENT(color->a))
	{
		SDL_SetTextureColorMod(texture_id, sr, sg, sb);
		SDL_SetTextureAlphaMod(texture_id, sa);
	}
	else
	{
		SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF);
		SDL_SetTextureAlphaMod(texture_id, 0xFF);
	}
}

INLINE void render_quad(sdl_info *sdl, texture_info *texture, render_primitive *prim, int x, int y)
{
	SDL_Texture	*texture_id;
	SDL_Rect target_rect;

	target_rect.x = x;
	target_rect.y = y;
	target_rect.w = round_nearest(prim->bounds.x1 - prim->bounds.x0);
	target_rect.h = round_nearest(prim->bounds.y1 - prim->bounds.y0);

	if (texture)
	{
		texture_id = texture->texture_id;

		texture->copyinfo->time -= osd_ticks();
#if 0
		if ((PRIMFLAG_GET_SCREENTEX(prim->flags)) && video_config.filter)
		{
			SDL_SetTextureScaleMode(texture->texture_id,  DRAW2_SCALEMODE_BEST);
		}
		else
		{
			SDL_SetTextureScaleMode(texture->texture_id,  DRAW2_SCALEMODE_NEAREST);
		}
#endif
		SDL_SetTextureBlendMode(texture_id, texture->sdl_blendmode);
		set_coloralphamode(texture_id, &prim->color);
		SDL_RenderCopy(sdl->sdl_renderer,  texture_id, NULL, &target_rect);
		texture->copyinfo->time += osd_ticks();

		texture->copyinfo->pixel_count += MAX(STAT_PIXEL_THRESHOLD , (texture->rawwidth * texture->rawheight));
		if (sdl->last_blit_pixels)
		{
			texture->copyinfo->time += (sdl->last_blit_time * (INT64) (texture->rawwidth * texture->rawheight)) / (INT64) sdl->last_blit_pixels;
		}
		texture->copyinfo->samples++;
		texture->copyinfo->perf = ( texture->copyinfo->pixel_count * (osd_ticks_per_second()/1000)) / texture->copyinfo->time;
	}
	else
	{
		UINT32 sr = (UINT32)(255.0f * prim->color.r);
		UINT32 sg = (UINT32)(255.0f * prim->color.g);
		UINT32 sb = (UINT32)(255.0f * prim->color.b);
		UINT32 sa = (UINT32)(255.0f * prim->color.a);

		SDL_SetRenderDrawBlendMode(sdl->sdl_renderer, map_blendmode(PRIMFLAG_GET_BLENDMODE(prim->flags)));
		SDL_SetRenderDrawColor(sdl->sdl_renderer, sr, sg, sb, sa);
		SDL_RenderFillRect(sdl->sdl_renderer, &target_rect);
	}
}

#if 0
static int RendererSupportsFormat(Uint32 format, Uint32 access, const char *sformat)
{
    struct SDL_RendererInfo render_info;
	int i;

    SDL_GetRendererInfo(&render_info);

	for (i=0; i < render_info.num_texture_formats; i++)
	{
		if (format == render_info.texture_formats[i])
			return 1;
	}
	mame_printf_verbose("Pixelformat <%s> not supported\n", sformat);
	return 0;
}
#else
static int RendererSupportsFormat(SDL_Renderer *renderer, Uint32 format, Uint32 access, const char *sformat)
{
	int i;
	SDL_Texture *texid;
	for (i=0; fmt_support[i].format != 0; i++)
	{
		if (format == fmt_support[i].format)
		{
			return fmt_support[i].status;
		}
	}
	/* not tested yet */
	fmt_support[i].format = format;
	fmt_support[i + 1].format = 0;
	texid = SDL_CreateTexture(renderer, format, access, 16, 16);
	if (texid)
	{
		fmt_support[i].status = 1;
		SDL_DestroyTexture(texid);
		return 1;
	}
	mame_printf_verbose("Pixelformat <%s> error %s \n", sformat, SDL_GetError());
	mame_printf_verbose("Pixelformat <%s> not supported\n", sformat);
	fmt_support[i].status = 0;
	return 0;
}
#endif

//============================================================
//  draw13_init
//============================================================

static void add_list(copy_info **head, copy_info *element, Uint32 bm)
{
	copy_info *newci = (copy_info *) osd_malloc(sizeof(copy_info));
	*newci = *element;

	newci->bm_mask = bm;
	newci->next = *head;
	*head = newci;
}

static void expand_copy_info(copy_info *list)
{
	copy_info	*bi;

	for (bi = list; bi->src_fmt != -1; bi++)
	{
		if (bi->bm_mask == BM_ALL)
		{
			add_list(&blit_info[bi->src_fmt], bi, SDL_BLENDMODE_NONE);
			add_list(&blit_info[bi->src_fmt], bi, SDL_BLENDMODE_ADD);
			add_list(&blit_info[bi->src_fmt], bi, SDL_BLENDMODE_MOD);
			add_list(&blit_info[bi->src_fmt], bi, SDL_BLENDMODE_BLEND);
		}
		else
			add_list(&blit_info[bi->src_fmt], bi, bi->bm_mask);
	}
}

int draw13_init(running_machine &machine, sdl_draw_info *callbacks)
{
	const char *stemp;

	// fill in the callbacks
	callbacks->exit = draw13_exit;
	callbacks->attach = draw13_attach;

	mame_printf_verbose("Using SDL native texturing driver (SDL 2.0+)\n");

	expand_copy_info(blit_info_default);
	//FIXME: -opengl16 should be -opengl -prefer16bpp
	//if (video_config.prefer16bpp_tex)
	expand_copy_info(blit_info_16bpp);

	// Load the GL library now - else MT will fail

	stemp = downcast<sdl_options &>(machine.options()).gl_lib();
	if (stemp != NULL && strcmp(stemp, SDLOPTVAL_AUTO) == 0)
		stemp = NULL;

	// No fatalerror here since not all video drivers support GL !
	if (SDL_GL_LoadLibrary(stemp) != 0) // Load library (default for e==NULL
		mame_printf_verbose("Warning: Unable to load opengl library: %s\n", stemp ? stemp : "<default>");
	else
		mame_printf_verbose("Loaded opengl shared library: %s\n", stemp ? stemp : "<default>");

	return 0;
}


//============================================================
//  draw13_exit
//============================================================

static void draw13_exit(void)
{
	int i;
	copy_info *bi, *freeme;
	for (i = 0; i <= SDL_TEXFORMAT_LAST; i++)
		for (bi = blit_info[i]; bi != NULL; )
		{
			if (bi->pixel_count)
				mame_printf_verbose("%s -> %s %s blendmode 0x%02x, %d samples: %d KPixel/sec\n", bi->srcname, bi->dstname,
						bi->rotate ? "rot" : "norot", bi->bm_mask, bi->samples,
						(int) bi->perf);
			freeme = bi;
			bi = bi->next;
			osd_free(freeme);
		}
}

//============================================================
//  draw13_attach
//============================================================

static void draw13_attach(sdl_draw_info *info, sdl_window_info *window)
{
	// fill in the callbacks
	window->create = draw13_window_create;
	window->resize = draw13_window_resize;
	window->get_primitives = draw13_window_get_primitives;
	window->draw = draw13_window_draw;
	window->destroy = draw13_window_destroy;
	window->destroy_all_textures = draw13_destroy_all_textures;
	window->clear = draw13_window_clear;
	window->xy_to_render_target = draw13_xy_to_render_target;
}

//============================================================
//  draw13_window_create
//============================================================

static int draw13_window_create(sdl_window_info *window, int width, int height)
{
	// allocate memory for our structures
	sdl_info *sdl = (sdl_info *) osd_malloc(sizeof(*sdl));

	mame_printf_verbose("Enter draw13_window_create\n");

	memset(sdl, 0, sizeof(*sdl));

	window->dxdata = sdl;

	sdl->extra_flags = (window->fullscreen ?
			SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE);

	// create the SDL window
	window->sdl_window = SDL_CreateWindow(window->title, SDL_WINDOWPOS_UNDEFINED_DISPLAY(window->monitor->handle), SDL_WINDOWPOS_UNDEFINED,
			width, height, sdl->extra_flags);

	if (window->fullscreen && video_config.switchres)
	{
		SDL_DisplayMode mode;
		SDL_GetCurrentDisplayMode(window->monitor->handle, &mode);
		mode.w = width;
		mode.h = height;
		if (window->refresh)
			mode.refresh_rate = window->refresh;
		if (window->depth)
		{
			switch (window->depth)
			{
			case 15:
				mode.format = SDL_PIXELFORMAT_RGB555;
				break;
			case 16:
				mode.format = SDL_PIXELFORMAT_RGB565;
				break;
			case 24:
				mode.format = SDL_PIXELFORMAT_RGB24;
				break;
			case 32:
				mode.format = SDL_PIXELFORMAT_RGB888;
				break;
			default:
				mame_printf_warning("Ignoring depth %d\n", window->depth);
			}
		}
		SDL_SetWindowDisplayMode(window->sdl_window, &mode);	// Try to set mode
	}
	else
		SDL_SetWindowDisplayMode(window->sdl_window, NULL);	// Use desktop

	// create renderer

	if (video_config.waitvsync)
		sdl->sdl_renderer = SDL_CreateRenderer(window->sdl_window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
	else
		sdl->sdl_renderer = SDL_CreateRenderer(window->sdl_window, -1, SDL_RENDERER_ACCELERATED);

	if (!sdl->sdl_renderer)
	{
		fatalerror("Error on creating renderer: %s \n", SDL_GetError());
	}

    //SDL_SelectRenderer(window->sdl_window);

	SDL_ShowWindow(window->sdl_window);
	//SDL_SetWindowFullscreen(window->window_id, window->fullscreen);
	SDL_RaiseWindow(window->sdl_window);
	SDL_GetWindowSize(window->sdl_window, &window->width, &window->height);


	sdl->blittimer = 3;

	// in case any textures try to come up before these are validated,
	// OpenGL guarantees all implementations can handle something this size.
	sdl->texture_max_width = 64;
	sdl->texture_max_height = 64;

	SDL_RenderPresent(sdl->sdl_renderer);
	mame_printf_verbose("Leave draw13_window_create\n");
	return 0;
}

//============================================================
//  draw13_window_resize
//============================================================

static void draw13_window_resize(sdl_window_info *window, int width, int height)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;

	sdl->resize_pending = 1;
	sdl->resize_height = height;
	sdl->resize_width = width;

	window->width = width;
	window->height = height;

	sdl->blittimer = 3;

}

//============================================================
//  drawsdl_xy_to_render_target
//============================================================

static int draw13_xy_to_render_target(sdl_window_info *window, int x, int y, int *xt, int *yt)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;

	*xt = x - sdl->last_hofs;
	*yt = y - sdl->last_vofs;
	if (*xt<0 || *xt >= window->blitwidth)
		return 0;
	if (*yt<0 || *yt >= window->blitheight)
		return 0;
	return 1;
}

//============================================================
//  draw13_window_get_primitives
//============================================================

static render_primitive_list &draw13_window_get_primitives(sdl_window_info *window)
{
	if ((!window->fullscreen) || (video_config.switchres))
	{
		sdlwindow_blit_surface_size(window, window->width, window->height);
	}
	else
	{
		sdlwindow_blit_surface_size(window, window->monitor->center_width, window->monitor->center_height);
	}
	window->target->set_bounds(window->blitwidth, window->blitheight, sdlvideo_monitor_get_aspect(window->monitor));
	return window->target->get_primitives();
}

//============================================================
//  draw13_window_draw
//============================================================

static int draw13_window_draw(sdl_window_info *window, UINT32 dc, int update)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;
	render_primitive *prim;
	texture_info *texture=NULL;
	float vofs, hofs;
	int blit_pixels = 0;

	if (video_config.novideo)
	{
		return 0;
	}

    if (sdl->resize_pending)
	{
		SDL_SetWindowSize(window->sdl_window, sdl->resize_width, sdl->resize_height);
		SDL_GetWindowSize(window->sdl_window, &window->width, &window->height);
		sdl->resize_pending = 0;
		SDL_RenderSetViewport(sdl->sdl_renderer, NULL);
	}

    //SDL_SelectRenderer(window->sdl_window);

	if (sdl->blittimer > 0)
	{
		/* SDL Underlays need alpha = 0 ! */
		SDL_SetRenderDrawBlendMode(sdl->sdl_renderer, SDL_BLENDMODE_NONE);
		//SDL_SetRenderDrawColor(0,0,0,255);
		SDL_SetRenderDrawColor(sdl->sdl_renderer, 0,0,0,0);
		SDL_RenderFillRect(sdl->sdl_renderer, NULL);
		sdl->blittimer--;
	}

	// compute centering parameters
	vofs = hofs = 0.0f;

	if (video_config.centerv || video_config.centerh)
	{
		int ch, cw;

		if ((window->fullscreen) && (!video_config.switchres))
		{
			ch = window->monitor->center_height;
			cw = window->monitor->center_width;
		}
		else
		{
			ch = window->height;
			cw = window->width;
		}

		if (video_config.centerv)
		{
			vofs = (ch - window->blitheight) / 2.0f;
		}
		if (video_config.centerh)
		{
			hofs = (cw - window->blitwidth) / 2.0f;
		}
	}

	sdl->last_hofs = hofs;
	sdl->last_vofs = vofs;

	window->primlist->acquire_lock();

	// now draw
	for (prim = window->primlist->first(); prim != NULL; prim = prim->next())
	{
		Uint8 sr, sg, sb, sa;

		switch (prim->type)
		{
			case render_primitive::LINE:
				sr = (int)(255.0f * prim->color.r);
				sg = (int)(255.0f * prim->color.g);
				sb = (int)(255.0f * prim->color.b);
				sa = (int)(255.0f * prim->color.a);

				SDL_SetRenderDrawBlendMode(sdl->sdl_renderer, map_blendmode(PRIMFLAG_GET_BLENDMODE(prim->flags)));
				SDL_SetRenderDrawColor(sdl->sdl_renderer, sr, sg, sb, sa);
				SDL_RenderDrawLine(sdl->sdl_renderer, prim->bounds.x0 + hofs, prim->bounds.y0 + vofs,
						prim->bounds.x1 + hofs, prim->bounds.y1 + vofs);
				break;
			case render_primitive::QUAD:
				texture = texture_update(window, prim);
				if (texture)
					blit_pixels += (texture->rawheight * texture->rawwidth);
				render_quad(sdl, texture, prim,
						round_nearest(hofs + prim->bounds.x0),
						round_nearest(vofs + prim->bounds.y0));
				break;
			default:
				throw emu_fatalerror("Unexpected render_primitive type");
		}
	}

	window->primlist->release_lock();

	sdl->last_blit_pixels = blit_pixels;
	sdl->last_blit_time = -osd_ticks();
	SDL_RenderPresent(sdl->sdl_renderer);
	sdl->last_blit_time += osd_ticks();

	return 0;
}


//============================================================
//  draw13_window_clear
//============================================================

static void draw13_window_clear(sdl_window_info *window)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;

	sdl->blittimer = 2;
}


//============================================================
//  draw13_window_destroy
//============================================================

static void draw13_window_destroy(sdl_window_info *window)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;

	// skip if nothing
	if (sdl == NULL)
		return;

	// free the memory in the window

	draw13_destroy_all_textures(window);

	SDL_DestroyWindow(window->sdl_window);

	osd_free(sdl);
	window->dxdata = NULL;
}

//============================================================
//  texture handling
//============================================================

//============================================================
//  texture_compute_size and type
//============================================================

static copy_info *texture_compute_size_type(SDL_Renderer *renderer, const render_texinfo *texsource, texture_info *texture, UINT32 flags)
{
	copy_info *bi;
	copy_info *result = NULL;
	int maxperf = 0;
	//int bm = PRIMFLAG_GET_BLENDMODE(flags);

	for (bi = blit_info[texture->format]; bi != NULL; bi = bi->next)
	{
		if ((texture->is_rotated == bi->rotate)
				&& (texture->sdl_blendmode == bi->bm_mask))
		{
			if (RendererSupportsFormat(renderer, bi->dst_fmt, texture->sdl_access, bi->dstname))
			{
				if (bi->perf == 0)
					return bi;
				else if (bi->perf > (maxperf * 102) / 100)
				{
					result = bi;
					maxperf = bi->perf;
				}
			}
		}
	}
	if (result)
		return result;
	/* try last resort handlers */
	for (bi = blit_info[texture->format]; bi != NULL; bi = bi->next)
	{
		if ((texture->is_rotated == bi->rotate)
			&& (texture->sdl_blendmode == bi->bm_mask))
			if (RendererSupportsFormat(renderer, bi->dst_fmt, texture->sdl_access, bi->dstname))
				return bi;
	}
	//FIXME: crash implement a -do nothing handler */
	return NULL;
}

//============================================================
//  texture_create
//============================================================

static texture_info *texture_create(sdl_window_info *window, const render_texinfo *texsource, quad_setup_data *setup, UINT32 flags)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;
	texture_info *texture;

	// allocate a new texture
	texture = (texture_info *) osd_malloc(sizeof(*texture));
	memset(texture, 0, sizeof(*texture));

	// fill in the core data
	texture->hash = texture_compute_hash(texsource, flags);
	texture->flags = flags;
	texture->texinfo = *texsource;
	texture->texinfo.seqid = -1; // force set data
	texture->is_rotated = FALSE;
	texture->setup = *setup;
	texture->sdl_blendmode = map_blendmode(PRIMFLAG_GET_BLENDMODE(flags));

	switch (PRIMFLAG_GET_TEXFORMAT(flags))
	{
		case TEXFORMAT_ARGB32:
			texture->format = SDL_TEXFORMAT_ARGB32;
			break;
		case TEXFORMAT_RGB32:
            texture->format = texsource->palette ? SDL_TEXFORMAT_RGB32_PALETTED : SDL_TEXFORMAT_RGB32;
			break;
		case TEXFORMAT_PALETTE16:
			texture->format = SDL_TEXFORMAT_PALETTE16;
			break;
		case TEXFORMAT_PALETTEA16:
			texture->format = SDL_TEXFORMAT_PALETTE16A;
			break;
		case TEXFORMAT_YUY16:
			texture->format = texsource->palette ? SDL_TEXFORMAT_YUY16_PALETTED : SDL_TEXFORMAT_YUY16;
			break;

		default:
			mame_printf_error("Unknown textureformat %d\n", PRIMFLAG_GET_TEXFORMAT(flags));
	}

	texture->rawwidth = texsource->width;
	texture->rawheight = texsource->height;
	if (setup->rotwidth != texture->rawwidth || setup->rotheight != texture->rawheight
			|| setup->dudx < 0 )
		texture->is_rotated = TRUE;
	else
		texture->is_rotated = FALSE;

	//texture->sdl_access = SDL_TEXTUREACCESS_STATIC;
	texture->sdl_access = SDL_TEXTUREACCESS_STREAMING;

	// Watch out for 0x0 textures ...
	if (!texture->setup.rotwidth || !texture->setup.rotheight)
		mame_printf_warning("Trying to create texture with zero dim\n");

	// compute the size
	texture->copyinfo = texture_compute_size_type(sdl->sdl_renderer, texsource, texture, flags);

	texture->texture_id = SDL_CreateTexture(sdl->sdl_renderer, texture->copyinfo->dst_fmt, texture->sdl_access,
			texture->setup.rotwidth, texture->setup.rotheight);

	if (!texture->texture_id)
		mame_printf_error("Error creating texture: %d x %d, pixelformat %s error: %s\n", texture->setup.rotwidth, texture->setup.rotheight,
				texture->copyinfo->dstname, SDL_GetError());

	if ( (texture->copyinfo->func != NULL) && (texture->sdl_access == SDL_TEXTUREACCESS_STATIC))
	{
		texture->pixels = osd_malloc_array(texture->setup.rotwidth * texture->setup.rotheight * texture->copyinfo->dst_bpp);
	 texture->pixels_own=TRUE;
 }
	/* add us to the texture list */
	texture->next = sdl->texlist;
	sdl->texlist = texture;

	texture->last_access = osd_ticks();

	return texture;
}

//============================================================
//  texture_set_data
//============================================================

static void texture_set_data(sdl_info *sdl, texture_info *texture, const render_texinfo *texsource, UINT32 flags)
{
	texture->copyinfo->time -= osd_ticks();
	if (texture->sdl_access == SDL_TEXTUREACCESS_STATIC)
	{
		if ( texture->copyinfo->func )
	    {
			texture->pitch = texture->setup.rotwidth * texture->copyinfo->dst_bpp;
			texture->copyinfo->func(texture, texsource);
	    }
		else
		{
			texture->pixels = texsource->base;
			texture->pitch = texture->texinfo.rowpixels * texture->copyinfo->dst_bpp;
		}
		SDL_UpdateTexture(texture->texture_id, NULL, texture->pixels, texture->pitch);
	}
	else
	{
		SDL_LockTexture(texture->texture_id, NULL, (void **) &texture->pixels, &texture->pitch);
		if ( texture->copyinfo->func )
			texture->copyinfo->func(texture, texsource);
		else
		{
			UINT8 *src = (UINT8 *) texsource->base;
			UINT8 *dst = (UINT8 *) texture->pixels;
			int spitch = texsource->rowpixels * texture->copyinfo->dst_bpp;
			int num = texsource->width * texture->copyinfo->dst_bpp;
			int h = texsource->height;
			while (h--) {
				memcpy(dst, src, num);
				src += spitch;
				dst += texture->pitch;
			}
		}
		SDL_UnlockTexture(texture->texture_id);
	}
	texture->copyinfo->time += osd_ticks();
}

//============================================================
//  compute rotation setup
//============================================================

static void compute_setup(sdl_info *sdl, const render_primitive *prim, quad_setup_data *setup, int flags)
{
	const render_quad_texuv *texcoords = &prim->texcoords;
	int texwidth = prim->texture.width;
	int texheight = prim->texture.height;
	float fdudx, fdvdx, fdudy, fdvdy;
	float width, height;
	float fscale;
	/* determine U/V deltas */
	if ((PRIMFLAG_GET_SCREENTEX(flags)))
		fscale = (float) video_config.prescale;
	else
		fscale = 1.0f;

	fdudx = (texcoords->tr.u - texcoords->tl.u) / fscale; // a a11
	fdvdx = (texcoords->tr.v - texcoords->tl.v) / fscale; // c a21
	fdudy = (texcoords->bl.u - texcoords->tl.u) / fscale; // b a12
	fdvdy = (texcoords->bl.v - texcoords->tl.v) / fscale; // d a22

	/* compute start and delta U,V coordinates now */

	setup->dudx = round_nearest(65536.0f * fdudx);
	setup->dvdx = round_nearest(65536.0f * fdvdx);
	setup->dudy = round_nearest(65536.0f * fdudy);
	setup->dvdy = round_nearest(65536.0f * fdvdy);
	setup->startu = round_nearest(65536.0f * (float) texwidth * texcoords->tl.u);
	setup->startv = round_nearest(65536.0f * (float) texheight * texcoords->tl.v);

	/* clamp to integers */

	width = fabs((fdudx * (float) (texwidth) + fdvdx * (float) (texheight)) * fscale * fscale);
	height = fabs((fdudy * (float)(texwidth) + fdvdy * (float) (texheight)) * fscale * fscale);

	setup->rotwidth = width;
	setup->rotheight = height;

	setup->startu += (setup->dudx + setup->dudy) / 2;
	setup->startv += (setup->dvdx + setup->dvdy) / 2;

}

//============================================================
//  texture_find
//============================================================

static texture_info *texture_find(sdl_info *sdl, const render_primitive *prim, quad_setup_data *setup)
{
	HashT texhash = texture_compute_hash(&prim->texture, prim->flags);
	texture_info *texture;
	osd_ticks_t now = osd_ticks();

	// find a match
	for (texture = sdl->texlist; texture != NULL; )
		if (texture->hash == texhash &&
			texture->texinfo.base == prim->texture.base &&
			texture->texinfo.width == prim->texture.width &&
			texture->texinfo.height == prim->texture.height &&
			texture->texinfo.rowpixels == prim->texture.rowpixels &&
			texture->setup.dudx == setup->dudx &&
			texture->setup.dvdx == setup->dvdx &&
			texture->setup.dudy == setup->dudy &&
			texture->setup.dvdy == setup->dvdy &&
			((texture->flags ^ prim->flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0)
		{
			/* would we choose another blitter ? */
			if ((texture->copyinfo->samples & 0x1f) == 0x1f)
			{
				if (texture->copyinfo != texture_compute_size_type(sdl->sdl_renderer, &texture->texinfo, texture, prim->flags))
					return NULL;
#if 0
				else
				{
					/* reset stats */
					texture->copyinfo->samples = 0;
					texture->copyinfo->time = 0;
					texture->copyinfo->pixel_count = 0;
				}
#endif
			}
			texture->last_access = now;
			return texture;
		}
		else
		{
			/* free resources not needed any longer? */
			texture_info *expire = texture;
			texture = texture->next;
			if (now - expire->last_access > osd_ticks_per_second())
				draw13_destroy_texture(sdl, expire);
		}

	// nothing found
	return NULL;
}

//============================================================
//  texture_update
//============================================================

static texture_info * texture_update(sdl_window_info *window, const render_primitive *prim)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;
	quad_setup_data setup;
	texture_info *texture;

	compute_setup(sdl, prim, &setup, prim->flags);

	texture = texture_find(sdl, prim, &setup);

	// if we didn't find one, create a new texture
	if (texture == NULL && prim->texture.base != NULL)
    {
        texture = texture_create(window, &prim->texture, &setup, prim->flags);
    }

    if (texture != NULL)
	{
		if (prim->texture.base != NULL && texture->texinfo.seqid != prim->texture.seqid)
		{
			texture->texinfo.seqid = prim->texture.seqid;
			// if we found it, but with a different seqid, copy the data
			texture_set_data(sdl, texture, &prim->texture, prim->flags);
		}

	}
    return texture;
}

static void draw13_destroy_texture(sdl_info *sdl, texture_info *texture)
{
	texture_info *p;

	SDL_DestroyTexture(texture->texture_id);
	if ( texture->pixels_own )
	{
		osd_free(texture->pixels);
		texture->pixels=NULL;
		texture->pixels_own=FALSE;
	}

	for (p=sdl->texlist; p != NULL; p = p->next)
		if (p->next == texture)
			break;
	if (p == NULL)
	    sdl->texlist = NULL;
	else
		p->next = texture->next;
	osd_free(texture);
}

static void draw13_destroy_all_textures(sdl_window_info *window)
{
	sdl_info *sdl = (sdl_info *) window->dxdata;
	texture_info *next_texture=NULL, *texture = NULL;
	int lock=FALSE;

	if (sdl == NULL)
		return;

	if(window->primlist)
	{
		lock=TRUE;
		window->primlist->acquire_lock();
	}

	texture = sdl->texlist;

	while (texture)
	{
		next_texture = texture->next;
		draw13_destroy_texture(sdl, texture);
		texture = next_texture;
	}

	if (lock)
		window->primlist->release_lock();
}
