text:
#ifndef KVSTORAGE_H_
#define KVSTORAGE_H_

#include "config.h"

struct rspamd_kv_cache;
struct rspamd_kv_backend;
struct rspamd_kv_storage;
struct rspamd_kv_expire;
struct rspamd_kv_element;

/* Locking definitions */
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
#define RW_R_LOCK g_rw_lock_reader_lock
#define RW_R_UNLOCK g_rw_lock_reader_unlock
#define RW_W_LOCK g_rw_lock_writer_lock
#define RW_W_UNLOCK g_rw_lock_writer_unlock
#else
#define RW_R_LOCK g_static_rw_lock_reader_lock
#define RW_R_UNLOCK g_static_rw_lock_reader_unlock
#define RW_W_LOCK g_static_rw_lock_writer_lock
#define RW_W_UNLOCK g_static_rw_lock_writer_unlock
#endif

/* Callbacks for cache */
typedef void (*cache_init)(struct rspamd_kv_cache *cache);
typedef struct rspamd_kv_element * (*cache_insert)(struct rspamd_kv_cache *cache,
	gpointer key, guint keylen, gpointer value, gsize len);
typedef gboolean (*cache_replace)(struct rspamd_kv_cache *cache, gpointer key,
	guint keylen,
	struct rspamd_kv_element *elt);
typedef struct rspamd_kv_element * (*cache_lookup)(struct rspamd_kv_cache *cache,
	gpointer key, guint keylen);
typedef struct rspamd_kv_element * (*cache_delete)(struct rspamd_kv_cache *cache,
	gpointer key, guint keylen);
typedef void (*cache_steal)(struct rspamd_kv_cache *cache,
	struct rspamd_kv_element * elt);
typedef void (*cache_destroy)(struct rspamd_kv_cache *cache);

/* Callbacks for backend */
typedef void (*backend_init)(struct rspamd_kv_backend *backend);
typedef gboolean (*backend_insert)(struct rspamd_kv_backend *backend,
	gpointer key, guint keylen,
	struct rspamd_kv_element *elt);
typedef gboolean (*backend_replace)(struct rspamd_kv_backend *backend,
	gpointer key, guint keylen,
	struct rspamd_kv_element *elt);
typedef struct rspamd_kv_element * (*backend_lookup)(struct rspamd_kv_backend *
	backend, gpointer key,
	guint keylen);
typedef void (*backend_delete)(struct rspamd_kv_backend *backend, gpointer key,
	guint keylen);
typedef gboolean (*backend_sync)(struct rspamd_kv_backend *backend);
typedef gboolean (*backend_incref)(struct rspamd_kv_backend *backend,
	gpointer key, guint keylen);
typedef void (*backend_destroy)(struct rspamd_kv_backend *backend);

/* Callbacks for expire */
typedef void (*expire_init)(struct rspamd_kv_expire *expire);
typedef void (*expire_insert)(struct rspamd_kv_expire *expire,
	struct rspamd_kv_element *elt);
typedef void (*expire_delete)(struct rspamd_kv_expire *expire,
	struct rspamd_kv_element *elt);
typedef gboolean (*expire_step)(struct rspamd_kv_expire *expire,
	struct rspamd_kv_storage *storage,
	time_t now, gboolean forced);
typedef void (*expire_destroy)(struct rspamd_kv_expire *expire);

/* Flags of element */
enum rspamd_kv_flags {
	KV_ELT_ARRAY = 1 << 0,
	KV_ELT_PERSISTENT = 1 << 1,
	KV_ELT_DIRTY = 1 << 2,
	KV_ELT_OUSTED = 1 << 3,
	KV_ELT_NEED_FREE = 1 << 4,
	KV_ELT_INTEGER = 1 << 5,
	KV_ELT_NEED_INSERT = 1 << 6,
	KV_ELT_NEED_EXPIRE = 1 << 7
};

#define ELT_DATA(elt) (gchar *)(elt)->data + (elt)->keylen + 1
#define ELT_LONG(elt) *((glong *)((elt)->data + (elt)->keylen + 1))
#define ELT_KEY(elt) (gchar *)(elt)->data
#define ELT_SIZE(elt) elt->size + sizeof(struct rspamd_kv_element) + \
	elt->keylen + 1

/* Common structures description */

struct rspamd_kv_element {
	time_t age;                                 /*< age of element */
	guint32 expire;                             /*< expire of element */
	gint flags;                                 /*< element flags  */
	gsize size;                                 /*< size of element */
	TAILQ_ENTRY (rspamd_kv_element) entry;      /*< list entry */
	guint keylen;                               /*< length of key */

	gpointer p;                                 /*< pointer to data */
	gchar data[1];                              /*< expandable data */
};

struct rspamd_kv_cache {
	cache_init init_func;                       /*< this callback is called on kv storage initialization */
	cache_insert insert_func;                   /*< this callback is called when element is inserted */
	cache_replace replace_func;                 /*< this callback is called when element is replace */
	cache_lookup lookup_func;                   /*< this callback is used for lookup of element */
	cache_delete delete_func;                   /*< this callback is called when an element is deleted */
	cache_steal steal_func;                     /*< this callback is used to replace duplicates in cache */
	cache_destroy destroy_func;                 /*< this callback is used for destroying all elements inside cache */
};
struct rspamd_kv_backend {
	backend_init init_func;                     /*< this callback is called on kv storage initialization */
	backend_insert insert_func;                 /*< this callback is called when element is inserted */
	backend_replace replace_func;               /*< this callback is called when element is replaced */
	backend_lookup lookup_func;                 /*< this callback is used for lookup of element */
	backend_delete delete_func;                 /*< this callback is called when an element is deleted */
	backend_sync sync_func;                     /*< this callback is called when backend need to be synced */
	backend_incref incref_func;                 /*< this callback is called when element must be ref'd */
	backend_destroy destroy_func;               /*< this callback is used for destroying all elements inside backend */
};
struct rspamd_kv_expire {
	expire_init init_func;                      /*< this callback is called on kv storage initialization */
	expire_insert insert_func;                  /*< this callback is called when element is inserted */
	expire_step step_func;                      /*< this callback is used when cache is full */
	expire_delete delete_func;                  /*< this callback is called when an element is deleted */
	expire_destroy destroy_func;                /*< this callback is used for destroying all elements inside expire */
};

/* Main kv storage structure */

struct rspamd_kv_storage {
	struct rspamd_kv_cache *cache;
	struct rspamd_kv_backend *backend;
	struct rspamd_kv_expire *expire;

	gsize elts;                                 /*< current elements count in a storage */
	gsize max_elts;                             /*< maximum number of elements in a storage */

	gsize memory;                               /*< memory eaten */
	gsize max_memory;                           /*< memory limit */

	gint id;                                    /* char ID */
	gchar *name;                                /* numeric ID */

	gboolean no_overwrite;                      /* do not overwrite data with the same keys */
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
	GRWLock rwlock;                             /* rwlock in new glib */
#else
	GStaticRWLock rwlock;                       /* rwlock for threaded access */
#endif
};

/** Create new kv storage */
struct rspamd_kv_storage * rspamd_kv_storage_new (gint id, const gchar *name,
	struct rspamd_kv_cache *cache, struct rspamd_kv_backend *backend,
	struct rspamd_kv_expire *expire,
	gsize max_elts, gsize max_memory, gboolean no_overwrite);

/** Insert new element to the kv storage */
gboolean rspamd_kv_storage_insert (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	gpointer data,
	gsize len,
	gint flags,
	guint expire);

/** Insert element only in cache */
gboolean rspamd_kv_storage_insert_cache (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	gpointer data,
	gsize len,
	gint flags,
	guint expire,
	struct rspamd_kv_element **pelt);

/** Replace an element in the kv storage */
gboolean rspamd_kv_storage_replace (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	struct rspamd_kv_element *elt);

/** Increment value in kvstorage */
gboolean rspamd_kv_storage_increment (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	glong *value);

/** Lookup an element inside kv storage */
struct rspamd_kv_element * rspamd_kv_storage_lookup (
	struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	time_t now);

/** Expire an element from kv storage */
struct rspamd_kv_element * rspamd_kv_storage_delete (
	struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen);

/** Destroy kv storage */
void rspamd_kv_storage_destroy (struct rspamd_kv_storage *storage);

/** Insert array */
gboolean rspamd_kv_storage_insert_array (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	guint elt_size,
	gpointer data,
	gsize len,
	gint flags,
	guint expire);

/** Set element inside array */
gboolean rspamd_kv_storage_set_array (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	guint elt_num,
	gpointer data,
	gsize len,
	time_t now);

/** Get element inside array */
gboolean rspamd_kv_storage_get_array (struct rspamd_kv_storage *storage,
	gpointer key,
	guint keylen,
	guint elt_num,
	gpointer *data,
	gsize *len,
	time_t now);

/* Hash table functions */
guint kv_elt_hash_func (gconstpointer e);
gboolean kv_elt_compare_func (gconstpointer e1, gconstpointer e2);

/**
 * LRU expire
 */
struct rspamd_kv_expire * rspamd_lru_expire_new (void);

/**
 * Ordinary hash
 */
struct rspamd_kv_cache * rspamd_kv_hash_new (void);

/**
 * Radix tree
 */
struct rspamd_kv_cache * rspamd_kv_radix_new (void);

#ifdef WITH_JUDY
/**
 * Judy tree
 */
struct rspamd_kv_cache * rspamd_kv_judy_new (void);
#endif

#endif /* KVSTORAGE_H_ */


text:
// *************************************************************************************************
//
// Horde3D
//   Next-Generation Graphics Engine
// --------------------------------------
// Copyright (C) 2006-2011 Nicolas Schulz
//
// This software is distributed under the terms of the Eclipse Public License v1.0.
// A copy of the license may be obtained at: http://www.eclipse.org/legal/epl-v10.html
//
// *************************************************************************************************

#include "egRendererBase.h"
#include "egModules.h"
#include "egCom.h"
#include "utOpenGL.h"

#include "utDebug.h"

namespace Horde3D {

#ifdef H3D_VALIDATE_DRAWCALLS
#	define CHECK_GL_ERROR checkGLError();
#else
#	define CHECK_GL_ERROR
#endif

// =================================================================================================
// GPUTimer
// =================================================================================================

GPUTimer::GPUTimer() : _numQueries( 0 ),  _queryFrame( 0 ), _time( 0 ), _activeQuery( false )
{
	reset();
}

GPUTimer::~GPUTimer()
{
	if( !_queryPool.empty() )
		glDeleteQueries( (uint32)_queryPool.size(), &_queryPool[0] );
}

void GPUTimer::beginQuery( uint32 frameID )
{
	if( !glExt::ARB_timer_query ) return;
	ASSERT( !_activeQuery );

	if( _queryFrame != frameID )
	{
		if( !updateResults() ) return;

		_queryFrame = frameID;
		_numQueries = 0;
	}

	// Create new query pair if necessary
	uint32 queryObjs[2];
	if( _numQueries++ * 2 == _queryPool.size() )
	{
		glGenQueries( 2, queryObjs );
		_queryPool.push_back( queryObjs[0] );
		_queryPool.push_back( queryObjs[1] );
	}
	else
	{
		queryObjs[0] = _queryPool[(_numQueries - 1) * 2];
	}

	_activeQuery = true;
	 glQueryCounter( queryObjs[0], GL_TIMESTAMP );
}

void GPUTimer::endQuery()
{
	if( _activeQuery )
	{
		glQueryCounter( _queryPool[_numQueries * 2 - 1], GL_TIMESTAMP );
		_activeQuery = false;
	}
}

bool GPUTimer::updateResults()
{
	if( !glExt::ARB_timer_query ) return false;

	if( _numQueries == 0 )
	{
		_time = 0;
		return true;
	}

	// Make sure that last query is available
	GLint available;
	glGetQueryObjectiv( _queryPool[_numQueries * 2 - 1], GL_QUERY_RESULT_AVAILABLE, &available );
	if( !available ) return false;

	//  Accumulate time
	double time = 0;
	GLuint64 timeStart = 0, timeEnd = 0, timeAccum = 0;
	for( uint32 i = 0; i < _numQueries; ++i )
	{
		glGetQueryObjectui64v( _queryPool[i * 2], GL_QUERY_RESULT, &timeStart );
		glGetQueryObjectui64v( _queryPool[i * 2 + 1], GL_QUERY_RESULT, &timeEnd );
		timeAccum += timeEnd - timeStart;
	}

	_time = (float)((double)timeAccum / 1000000.0);
	return true;
}

void GPUTimer::reset()
{
	_time = glExt::ARB_timer_query ? 0.f : -1.f;
}

// =================================================================================================
// RenderDevice
// =================================================================================================

RenderDevice::RenderDevice()
{
	_numVertexLayouts = 0;

	_vpX = 0; _vpY = 0; _vpWidth = 320; _vpHeight = 240;
	_scX = 0; _scY = 0; _scWidth = 320; _scHeight = 240;
	_prevShaderId = _curShaderId = 0;
	_curRendBuf = 0; _outputBufferIndex = 0;
	_textureMem = 0; _bufferMem = 0;
	_curVertLayout = _newVertLayout = 0;
	_curIndexBuf = _newIndexBuf = 0;
	_indexFormat = (uint32)IDXFMT_16;
	_pendingMask = 0;
}

RenderDevice::~RenderDevice()
{
}

void RenderDevice::initStates()
{
	glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
}

bool RenderDevice::init()
{
	bool failed = false;

	char *vendor = (char *)glGetString( GL_VENDOR );
	char *renderer = (char *)glGetString( GL_RENDERER );
	char *version = (char *)glGetString( GL_VERSION );

	Modules::log().writeInfo( "Initializing GL2 backend using OpenGL driver '%s' by '%s' on '%s'",
	                          version, vendor, renderer );

	// Init extensions
	if( !initOpenGLExtensions() )
	{
		Modules::log().writeError( "Could not find all required OpenGL function entry points" );
		failed = true;
	}

	// Check that OpenGL 2.0 is available
	if( glExt::majorVersion * 10 + glExt::minorVersion < 20 )
	{
		Modules::log().writeError( "OpenGL 2.0 not available" );
		failed = true;
	}

	// Check that required extensions are supported
	if( !glExt::EXT_framebuffer_object )
	{
		Modules::log().writeError( "Extension EXT_framebuffer_object not supported" );
		failed = true;
	}
	if( !glExt::EXT_texture_filter_anisotropic )
	{
		Modules::log().writeError( "Extension EXT_texture_filter_anisotropic not supported" );
		failed = true;
	}
	if( !glExt::EXT_texture_compression_s3tc )
	{
		Modules::log().writeError( "Extension EXT_texture_compression_s3tc not supported" );
		failed = true;
	}
	if( !glExt::EXT_texture_sRGB )
	{
		Modules::log().writeError( "Extension EXT_texture_sRGB not supported" );
		failed = true;
	}

	if( failed )
	{
		Modules::log().writeError( "Failed to init renderer backend, debug info following" );
		char *exts = (char *)glGetString( GL_EXTENSIONS );
		Modules::log().writeInfo( "Supported extensions: '%s'", exts );

		return false;
	}

	// Get capabilities
	_caps.texFloat = glExt::ARB_texture_float ? 1 : 0;
	_caps.texNPOT = glExt::ARB_texture_non_power_of_two ? 1 : 0;
	_caps.rtMultisampling = glExt::EXT_framebuffer_multisample ? 1 : 0;

	// Find supported depth format (some old ATI cards only support 16 bit depth for FBOs)
	_depthFormat = GL_DEPTH_COMPONENT24;
	uint32 testBuf = createRenderBuffer( 32, 32, TextureFormats::BGRA8, true, 1, 0 );
	if( testBuf == 0 )
	{
		_depthFormat = GL_DEPTH_COMPONENT16;
		Modules::log().writeWarning( "Render target depth precision limited to 16 bit" );
	}
	else
		destroyRenderBuffer( testBuf );

	initStates();
	resetStates();

	return true;
}

// =================================================================================================
// Vertex layouts
// =================================================================================================

uint32 RenderDevice::registerVertexLayout( uint32 numAttribs, VertexLayoutAttrib *attribs )
{
	if( _numVertexLayouts == MaxNumVertexLayouts )
		return 0;

	_vertexLayouts[_numVertexLayouts].numAttribs = numAttribs;

	for( uint32 i = 0; i < numAttribs; ++i )
		_vertexLayouts[_numVertexLayouts].attribs[i] = attribs[i];

	return ++_numVertexLayouts;
}

// =================================================================================================
// Buffers
// =================================================================================================

uint32 RenderDevice::createVertexBuffer( uint32 size, const void *data )
{
	RDIBuffer buf;

	buf.type = GL_ARRAY_BUFFER;
	buf.size = size;
	glGenBuffers( 1, &buf.glObj );
	glBindBuffer( buf.type, buf.glObj );
	glBufferData( buf.type, size, data, GL_DYNAMIC_DRAW );
	glBindBuffer( buf.type, 0 );

	_bufferMem += size;
	return _buffers.add( buf );
}

uint32 RenderDevice::createIndexBuffer( uint32 size, const void *data )
{
	RDIBuffer buf;

	buf.type = GL_ELEMENT_ARRAY_BUFFER;
	buf.size = size;
	glGenBuffers( 1, &buf.glObj );
	glBindBuffer( buf.type, buf.glObj );
	glBufferData( buf.type, size, data, GL_DYNAMIC_DRAW );
	glBindBuffer( buf.type, 0 );

	_bufferMem += size;
	return _buffers.add( buf );
}

void RenderDevice::destroyBuffer( uint32 bufObj )
{
	if( bufObj == 0 ) return;

	RDIBuffer &buf = _buffers.getRef( bufObj );
	glDeleteBuffers( 1, &buf.glObj );

	_bufferMem -= buf.size;
	_buffers.remove( bufObj );
}

void RenderDevice::updateBufferData( uint32 bufObj, uint32 offset, uint32 size, void *data )
{
	const RDIBuffer &buf = _buffers.getRef( bufObj );
	ASSERT( offset + size <= buf.size );

	glBindBuffer( buf.type, buf.glObj );

	if( offset == 0 &&  size == buf.size )
	{
		// Replacing the whole buffer can help the driver to avoid pipeline stalls
		glBufferData( buf.type, size, data, GL_DYNAMIC_DRAW );
		return;
	}

	glBufferSubData( buf.type, offset, size, data );
}

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

uint32 RenderDevice::calcTextureSize( TextureFormats::List format, int width, int height, int depth )
{
	switch( format )
	{
	case TextureFormats::BGRA8:
		return width * height * depth * 4;
	case TextureFormats::DXT1:
		return std::max( width / 4, 1 ) * std::max( height / 4, 1 ) * depth * 8;
	case TextureFormats::DXT3:
		return std::max( width / 4, 1 ) * std::max( height / 4, 1 ) * depth * 16;
	case TextureFormats::DXT5:
		return std::max( width / 4, 1 ) * std::max( height / 4, 1 ) * depth * 16;
	case TextureFormats::RGBA16F:
		return width * height * depth * 8;
	case TextureFormats::RGBA32F:
		return width * height * depth * 16;
	default:
		return 0;
	}
}

uint32 RenderDevice::createTexture( TextureTypes::List type, int width, int height, int depth,
                                    TextureFormats::List format,
                                    bool hasMips, bool genMips, bool compress, bool sRGB )
{
	if( !_caps.texNPOT )
	{
		// Check if texture is NPOT
		if( (width & (width-1)) != 0 || (height & (height-1)) != 0 )
			Modules::log().writeWarning( "Texture has non-power-of-two dimensions although NPOT is not supported by GPU" );
	}

	RDITexture tex;
	tex.type = type;
	tex.format = format;
	tex.width = width;
	tex.height = height;
	tex.depth = (type == TextureTypes::Tex3D ? depth : 1);
	tex.sRGB = sRGB && Modules::config().sRGBLinearization;
	tex.genMips = genMips;
	tex.hasMips = hasMips;

	switch( format )
	{
	case TextureFormats::BGRA8:
		tex.glFmt = tex.sRGB ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8;
		break;
	case TextureFormats::DXT1:
		tex.glFmt = tex.sRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		break;
	case TextureFormats::DXT3:
		tex.glFmt = tex.sRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		break;
	case TextureFormats::DXT5:
		tex.glFmt = tex.sRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		break;
	case TextureFormats::RGBA16F:
		tex.glFmt = GL_RGBA16F_ARB;
		break;
	case TextureFormats::RGBA32F:
		tex.glFmt = GL_RGBA32F_ARB;
		break;
	case TextureFormats::DEPTH:
		tex.glFmt = _depthFormat;
		break;
	default:
		ASSERT( 0 );
		break;
	};

	glGenTextures( 1, &tex.glObj );
	glActiveTexture( GL_TEXTURE15 );
	glBindTexture( tex.type, tex.glObj );

	float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor );

	tex.samplerState = 0;
	applySamplerState( tex );

	glBindTexture( tex.type, 0 );
	if( _texSlots[15].texObj )
		glBindTexture( _textures.getRef( _texSlots[15].texObj ).type, _textures.getRef( _texSlots[15].texObj ).glObj );

	// Calculate memory requirements
	tex.memSize = calcTextureSize( format, width, height, depth );
	if( hasMips || genMips ) tex.memSize += ftoi_r( tex.memSize * 1.0f / 3.0f );
	if( type == TextureTypes::TexCube ) tex.memSize *= 6;
	_textureMem += tex.memSize;

	return _textures.add( tex );
}

void RenderDevice::uploadTextureData( uint32 texObj, int slice, int mipLevel, const void *pixels )
{
	const RDITexture &tex = _textures.getRef( texObj );
	TextureFormats::List format = tex.format;

	glActiveTexture( GL_TEXTURE15 );
	glBindTexture( tex.type, tex.glObj );

	int inputFormat = GL_BGRA, inputType = GL_UNSIGNED_BYTE;
	bool compressed = (format == TextureFormats::DXT1) || (format == TextureFormats::DXT3) ||
	                  (format == TextureFormats::DXT5);

	switch( format )
	{
	case TextureFormats::RGBA16F:
		inputFormat = GL_RGBA;
		inputType = GL_FLOAT;
		break;
	case TextureFormats::RGBA32F:
		inputFormat = GL_RGBA;
		inputType = GL_FLOAT;
		break;
	case TextureFormats::DEPTH:
		inputFormat = GL_DEPTH_COMPONENT;
		inputType = GL_FLOAT;
	};

	// Calculate size of next mipmap using "floor" convention
	int width = std::max( tex.width >> mipLevel, 1 ), height = std::max( tex.height >> mipLevel, 1 );

	if( tex.type == TextureTypes::Tex2D || tex.type == TextureTypes::TexCube )
	{
		int target = (tex.type == TextureTypes::Tex2D) ?
			GL_TEXTURE_2D : (GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice);

		if( compressed )
			glCompressedTexImage2D( target, mipLevel, tex.glFmt, width, height, 0,
			                        calcTextureSize( format, width, height, 1 ), pixels );
		else
			glTexImage2D( target, mipLevel, tex.glFmt, width, height, 0, inputFormat, inputType, pixels );
	}
	else if( tex.type == TextureTypes::Tex3D )
	{
		int depth = std::max( tex.depth >> mipLevel, 1 );

		if( compressed )
			glCompressedTexImage3D( GL_TEXTURE_3D, mipLevel, tex.glFmt, width, height, depth, 0,
			                        calcTextureSize( format, width, height, depth ), pixels );
		else
			glTexImage3D( GL_TEXTURE_3D, mipLevel, tex.glFmt, width, height, depth, 0,
			              inputFormat, inputType, pixels );
	}

	if( tex.genMips && (tex.type != GL_TEXTURE_CUBE_MAP || slice == 5) )
	{
		// Note: for cube maps mips are only generated when the side with the highest index is uploaded
		glEnable( tex.type );  // Workaround for ATI driver bug
		glGenerateMipmapEXT( tex.type );
		glDisable( tex.type );
	}

	glBindTexture( tex.type, 0 );
	if( _texSlots[15].texObj )
		glBindTexture( _textures.getRef( _texSlots[15].texObj ).type, _textures.getRef( _texSlots[15].texObj ).glObj );
}

void RenderDevice::destroyTexture( uint32 texObj )
{
	if( texObj == 0 ) return;

	const RDITexture &tex = _textures.getRef( texObj );
	glDeleteTextures( 1, &tex.glObj );

	_textureMem -= tex.memSize;
	_textures.remove( texObj );
}

void RenderDevice::updateTextureData( uint32 texObj, int slice, int mipLevel, const void *pixels )
{
	uploadTextureData( texObj, slice, mipLevel, pixels );
}

bool RenderDevice::getTextureData( uint32 texObj, int slice, int mipLevel, void *buffer )
{
	const RDITexture &tex = _textures.getRef( texObj );

	int target = tex.type == TextureTypes::TexCube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
	if( target == GL_TEXTURE_CUBE_MAP ) target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice;

	int fmt, type, compressed = 0;
	glActiveTexture( GL_TEXTURE15 );
	glBindTexture( tex.type, tex.glObj );

	switch( tex.format )
	{
	case TextureFormats::BGRA8:
		fmt = GL_BGRA;
		type = GL_UNSIGNED_BYTE;
		break;
	case TextureFormats::DXT1:
	case TextureFormats::DXT3:
	case TextureFormats::DXT5:
		compressed = 1;
		break;
	case TextureFormats::RGBA16F:
	case TextureFormats::RGBA32F:
		fmt = GL_RGBA;
		type = GL_FLOAT;
		break;
	default:
		return false;
	};

	if( compressed )
		glGetCompressedTexImage( target, mipLevel, buffer );
	else
		glGetTexImage( target, mipLevel, fmt, type, buffer );

	glBindTexture( tex.type, 0 );
	if( _texSlots[15].texObj )
		glBindTexture( _textures.getRef( _texSlots[15].texObj ).type, _textures.getRef( _texSlots[15].texObj ).glObj );

	return true;
}

// =================================================================================================
// Shaders
// =================================================================================================

uint32 RenderDevice::createShaderProgram( const char *vertexShaderSrc, const char *fragmentShaderSrc )
{
	int infologLength = 0;
	int charsWritten = 0;
	char *infoLog = 0x0;
	int status;

	_shaderLog = "";

	// Vertex shader
	uint32 vs = glCreateShader( GL_VERTEX_SHADER );
	glShaderSource( vs, 1, &vertexShaderSrc, 0x0 );
	glCompileShader( vs );
	glGetShaderiv( vs, GL_COMPILE_STATUS, &status );
	if( !status )
	{
		// Get info
		glGetShaderiv( vs, GL_INFO_LOG_LENGTH, &infologLength );
		if( infologLength > 1 )
		{
			infoLog = new char[infologLength];
			glGetShaderInfoLog( vs, infologLength, &charsWritten, infoLog );
			_shaderLog = _shaderLog + "[Vertex Shader]\n" + infoLog;
			delete[] infoLog; infoLog = 0x0;
		}

		glDeleteShader( vs );
		return 0;
	}

	// Fragment shader
	uint32 fs = glCreateShader( GL_FRAGMENT_SHADER );
	glShaderSource( fs, 1, &fragmentShaderSrc, 0x0 );
	glCompileShader( fs );
	glGetShaderiv( fs, GL_COMPILE_STATUS, &status );
	if( !status )
	{
		glGetShaderiv( fs, GL_INFO_LOG_LENGTH, &infologLength );
		if( infologLength > 1 )
		{
			infoLog = new char[infologLength];
			glGetShaderInfoLog( fs, infologLength, &charsWritten, infoLog );
			_shaderLog = _shaderLog + "[Fragment Shader]\n" + infoLog;
			delete[] infoLog; infoLog = 0x0;
		}

		glDeleteShader( vs );
		glDeleteShader( fs );
		return 0;
	}

	// Shader program
	uint32 program = glCreateProgram();
	glAttachShader( program, vs );
	glAttachShader( program, fs );
	glDeleteShader( vs );
	glDeleteShader( fs );

	return program;
}

bool RenderDevice::linkShaderProgram( uint32 programObj )
{
	int infologLength = 0;
	int charsWritten = 0;
	char *infoLog = 0x0;
	int status;

	_shaderLog = "";

	glLinkProgram( programObj );
	glGetProgramiv( programObj, GL_INFO_LOG_LENGTH, &infologLength );
	if( infologLength > 1 )
	{
		infoLog = new char[infologLength];
		glGetProgramInfoLog( programObj, infologLength, &charsWritten, infoLog );
		_shaderLog = _shaderLog + "[Linking]\n" + infoLog;
		delete[] infoLog; infoLog = 0x0;
	}

	glGetProgramiv( programObj, GL_LINK_STATUS, &status );
	if( !status ) return false;

	return true;
}

uint32 RenderDevice::createShader( const char *vertexShaderSrc, const char *fragmentShaderSrc )
{
	// Compile and link shader
	uint32 programObj = createShaderProgram( vertexShaderSrc, fragmentShaderSrc );
	if( programObj == 0 ) return 0;
	if( !linkShaderProgram( programObj ) ) return 0;

	uint32 shaderId = _shaders.add( RDIShader() );
	RDIShader &shader = _shaders.getRef( shaderId );
	shader.oglProgramObj = programObj;

	int attribCount;
	glGetProgramiv( programObj, GL_ACTIVE_ATTRIBUTES, &attribCount );

	for( uint32 i = 0; i < _numVertexLayouts; ++i )
	{
		RDIVertexLayout &vl = _vertexLayouts[i];
		bool allAttribsFound = true;

		for( uint32 j = 0; j < 16; ++j )
			shader.inputLayouts[i].attribIndices[j] = -1;

		for( int j = 0; j < attribCount; ++j )
		{
			char name[32];
			uint32 size, type;
			glGetActiveAttrib( programObj, j, 32, 0x0, (int *)&size, &type, name );

			bool attribFound = false;
			for( uint32 k = 0; k < vl.numAttribs; ++k )
			{
				if( vl.attribs[k].semanticName.compare(name) == 0 )
				{
					shader.inputLayouts[i].attribIndices[k] = glGetAttribLocation( programObj, name );
					attribFound = true;
				}
			}

			if( !attribFound )
			{
				allAttribsFound = false;
				break;
			}
		}

		shader.inputLayouts[i].valid = allAttribsFound;
	}

	return shaderId;
}

void RenderDevice::destroyShader( uint32 shaderId )
{
	if( shaderId == 0 ) return;

	RDIShader &shader = _shaders.getRef( shaderId );
	glDeleteProgram( shader.oglProgramObj );
	_shaders.remove( shaderId );
}

void RenderDevice::bindShader( uint32 shaderId )
{
	if( shaderId != 0 )
	{
		RDIShader &shader = _shaders.getRef( shaderId );
		glUseProgram( shader.oglProgramObj );
	}
	else
	{
		glUseProgram( 0 );
	}

	_curShaderId = shaderId;
	_pendingMask |= PM_VERTLAYOUT;
}

int RenderDevice::getShaderConstLoc( uint32 shaderId, const char *name )
{
	RDIShader &shader = _shaders.getRef( shaderId );
	return glGetUniformLocation( shader.oglProgramObj, name );
}

int RenderDevice::getShaderSamplerLoc( uint32 shaderId, const char *name )
{
	RDIShader &shader = _shaders.getRef( shaderId );
	return glGetUniformLocation( shader.oglProgramObj, name );
}

void RenderDevice::setShaderConst( int loc, RDIShaderConstType type, void *values, uint32 count )
{
	switch( type )
	{
	case CONST_FLOAT:
		glUniform1fv( loc, count, (float *)values );
		break;
	case CONST_FLOAT2:
		glUniform2fv( loc, count, (float *)values );
		break;
	case CONST_FLOAT3:
		glUniform3fv( loc, count, (float *)values );
		break;
	case CONST_FLOAT4:
		glUniform4fv( loc, count, (float *)values );
		break;
	case CONST_FLOAT44:
		glUniformMatrix4fv( loc, count, false, (float *)values );
		break;
	case CONST_FLOAT33:
		glUniformMatrix3fv( loc, count, false, (float *)values );
		break;
	}
}

void RenderDevice::setShaderSampler( int loc, uint32 texUnit )
{
	glUniform1i( loc, (int)texUnit );
}

// =================================================================================================
// Renderbuffers
// =================================================================================================

uint32 RenderDevice::createRenderBuffer( uint32 width, uint32 height, TextureFormats::List format,
                                         bool depth, uint32 numColBufs, uint32 samples )
{
	if( (format == TextureFormats::RGBA16F || format == TextureFormats::RGBA32F) && !_caps.texFloat )
	{
		return 0;
	}

	if( numColBufs > RDIRenderBuffer::MaxColorAttachmentCount ) return 0;

	uint32 maxSamples = 0;
	if( _caps.rtMultisampling )
	{
		GLint value;
		glGetIntegerv( GL_MAX_SAMPLES_EXT, &value );
		maxSamples = (uint32)value;
	}
	if( samples > maxSamples )
	{
		samples = maxSamples;
		Modules::log().writeWarning( "GPU does not support desired multisampling quality for render target" );
	}

	RDIRenderBuffer rb;
	rb.width = width;
	rb.height = height;
	rb.samples = samples;

	// Create framebuffers
	glGenFramebuffersEXT( 1, &rb.fbo );
	if( samples > 0 ) glGenFramebuffersEXT( 1, &rb.fboMS );

	if( numColBufs > 0 )
	{
		// Attach color buffers
		for( uint32 j = 0; j < numColBufs; ++j )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
			// Create a color texture
			uint32 texObj = createTexture( TextureTypes::Tex2D, rb.width, rb.height, 1, format, false, false, false, false );
			ASSERT( texObj != 0 );
			uploadTextureData( texObj, 0, 0, 0x0 );
			rb.colTexs[j] = texObj;
			RDITexture &tex = _textures.getRef( texObj );
			// Attach the texture
			glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j, GL_TEXTURE_2D, tex.glObj, 0 );

			if( samples > 0 )
			{
				glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS );
				// Create a multisampled renderbuffer
				glGenRenderbuffersEXT( 1, &rb.colBufs[j] );
				glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rb.colBufs[j] );
				glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, rb.samples, tex.glFmt, rb.width, rb.height );
				// Attach the renderbuffer
				glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + j,
				                              GL_RENDERBUFFER_EXT, rb.colBufs[j] );
			}
		}

		uint32 buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT,
		                     GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT };
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
		glDrawBuffers( numColBufs, buffers );

		if( samples > 0 )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS );
			glDrawBuffers( numColBufs, buffers );
		}
	}
	else
	{
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
		glDrawBuffer( GL_NONE );
		glReadBuffer( GL_NONE );

		if( samples > 0 )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS );
			glDrawBuffer( GL_NONE );
			glReadBuffer( GL_NONE );
		}
	}

	// Attach depth buffer
	if( depth )
	{
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
		// Create a depth texture
		uint32 texObj = createTexture( TextureTypes::Tex2D, rb.width, rb.height, 1, TextureFormats::DEPTH, false, false, false, false );
		ASSERT( texObj != 0 );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE );
		uploadTextureData( texObj, 0, 0, 0x0 );
		rb.depthTex = texObj;
		RDITexture &tex = _textures.getRef( texObj );
		// Attach the texture
		glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, tex.glObj, 0 );

		if( samples > 0 )
		{
			glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS );
			// Create a multisampled renderbuffer
			glGenRenderbuffersEXT( 1, &rb.depthBuf );
			glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rb.depthBuf );
			glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, rb.samples, _depthFormat, rb.width, rb.height );
			// Attach the renderbuffer
			glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
			                              GL_RENDERBUFFER_EXT, rb.depthBuf );
		}
	}

	uint32 rbObj = _rendBufs.add( rb );

	// Check if FBO is complete
	bool valid = true;
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
	uint32 status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
	if( status != GL_FRAMEBUFFER_COMPLETE_EXT ) valid = false;

	if( samples > 0 )
	{
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS );
		status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
		if( status != GL_FRAMEBUFFER_COMPLETE_EXT ) valid = false;
	}

	if( !valid )
	{
		destroyRenderBuffer( rbObj );
		return 0;
	}

	return rbObj;
}

void RenderDevice::destroyRenderBuffer( uint32 rbObj )
{
	RDIRenderBuffer &rb = _rendBufs.getRef( rbObj );

	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );

	if( rb.depthTex != 0 ) destroyTexture( rb.depthTex );
	if( rb.depthBuf != 0 ) glDeleteRenderbuffersEXT( 1, &rb.depthBuf );
	rb.depthTex = rb.depthBuf = 0;

	for( uint32 i = 0; i < RDIRenderBuffer::MaxColorAttachmentCount; ++i )
	{
		if( rb.colTexs[i] != 0 ) destroyTexture( rb.colTexs[i] );
		if( rb.colBufs[i] != 0 ) glDeleteRenderbuffersEXT( 1, &rb.colBufs[i] );
		rb.colTexs[i] = rb.colBufs[i] = 0;
	}

	if( rb.fbo != 0 ) glDeleteFramebuffersEXT( 1, &rb.fbo );
	if( rb.fboMS != 0 ) glDeleteFramebuffersEXT( 1, &rb.fboMS );
	rb.fbo = rb.fboMS = 0;

	_rendBufs.remove( rbObj );
}

uint32 RenderDevice::getRenderBufferTex( uint32 rbObj, uint32 bufIndex )
{
	RDIRenderBuffer &rb = _rendBufs.getRef( rbObj );

	if( bufIndex < RDIRenderBuffer::MaxColorAttachmentCount ) return rb.colTexs[bufIndex];
	else if( bufIndex == 32 ) return rb.depthTex;
	else return 0;
}

void RenderDevice::resolveRenderBuffer( uint32 rbObj )
{
	RDIRenderBuffer &rb = _rendBufs.getRef( rbObj );

	if( rb.fboMS == 0 ) return;

	glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, rb.fboMS );
	glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, rb.fbo );

	bool depthResolved = false;
	for( uint32 i = 0; i < RDIRenderBuffer::MaxColorAttachmentCount; ++i )
	{
		if( rb.colBufs[i] != 0 )
		{
			glReadBuffer( GL_COLOR_ATTACHMENT0_EXT + i );
			glDrawBuffer( GL_COLOR_ATTACHMENT0_EXT + i );

			int mask = GL_COLOR_BUFFER_BIT;
			if( !depthResolved && rb.depthBuf != 0 )
			{
				mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
				depthResolved = true;
			}
			glBlitFramebufferEXT( 0, 0, rb.width, rb.height, 0, 0, rb.width, rb.height, mask, GL_NEAREST );
		}
	}

	if( !depthResolved && rb.depthBuf != 0 )
	{
		glReadBuffer( GL_NONE );
		glDrawBuffer( GL_NONE );
		glBlitFramebufferEXT( 0, 0, rb.width, rb.height, 0, 0, rb.width, rb.height,
							  GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST );
	}

	glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, 0 );
	glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0 );
}

void RenderDevice::setRenderBuffer( uint32 rbObj )
{
	// Resolve render buffer if necessary
	if( _curRendBuf != 0 ) resolveRenderBuffer( _curRendBuf );

	// Set new render buffer
	_curRendBuf = rbObj;

	if( rbObj == 0 )
	{
		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
		glDrawBuffer( _outputBufferIndex == 1 ? GL_BACK_RIGHT : GL_BACK_LEFT );
		_fbWidth = _vpWidth + _vpX;
		_fbHeight = _vpHeight + _vpY;
		glDisable( GL_MULTISAMPLE );
	}
	else
	{
		// Unbind all textures to make sure that no FBO attachment is bound any more
		for( uint32 i = 0; i < 16; ++i ) setTexture( i, 0, 0 );
		commitStates( PM_TEXTURES );

		RDIRenderBuffer &rb = _rendBufs.getRef( rbObj );

		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fboMS != 0 ? rb.fboMS : rb.fbo );
		ASSERT( glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ) == GL_FRAMEBUFFER_COMPLETE_EXT );
		_fbWidth = rb.width;
		_fbHeight = rb.height;

		if( rb.fboMS != 0 ) glEnable( GL_MULTISAMPLE );
		else glDisable( GL_MULTISAMPLE );
	}
}

bool RenderDevice::getRenderBufferData( uint32 rbObj, int bufIndex, int *width, int *height,
                                        int *compCount, void *dataBuffer, int bufferSize )
{
	int x, y, w, h;
	int format = GL_RGBA;
	int type = GL_FLOAT;

	glPixelStorei( GL_PACK_ALIGNMENT, 4 );

	if( rbObj == 0 )
	{
		if( bufIndex != 32 && bufIndex != 0 ) return false;
		if( width != 0x0 ) *width = _vpWidth;
		if( height != 0x0 ) *height = _vpHeight;

		x = _vpX; y = _vpY; w = _vpWidth; h = _vpHeight;

		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
		if( bufIndex != 32 ) glReadBuffer( GL_BACK_LEFT );
		//format = GL_BGRA;
		//type = GL_UNSIGNED_BYTE;
	}
	else
	{
		resolveRenderBuffer( rbObj );
		RDIRenderBuffer &rb = _rendBufs.getRef( rbObj );

		if( bufIndex == 32 && rb.depthTex == 0 ) return false;
		if( bufIndex != 32 )
		{
			if( (unsigned)bufIndex >= RDIRenderBuffer::MaxColorAttachmentCount || rb.colTexs[bufIndex] == 0 )
				return false;
		}
		if( width != 0x0 ) *width = rb.width;
		if( height != 0x0 ) *height = rb.height;

		x = 0; y = 0; w = rb.width; h = rb.height;

		glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, rb.fbo );
		if( bufIndex != 32 ) glReadBuffer( GL_COLOR_ATTACHMENT0_EXT + bufIndex );
	}

	if( bufIndex == 32 )
	{
		format = GL_DEPTH_COMPONENT;
		type = GL_FLOAT;
	}

	int comps = (bufIndex == 32 ? 1 : 4);
	if( compCount != 0x0 ) *compCount = comps;

	if( dataBuffer == 0x0 ) return true;
	if( bufferSize < w * h * comps * (type == GL_FLOAT ? 4 : 1) ) return false;

	glFinish();
	glReadPixels( x, y, w, h, format, type, dataBuffer );

	return true;
}

// =================================================================================================
// Queries
// =================================================================================================

uint32 RenderDevice::createOcclusionQuery()
{
	uint32 queryObj;
	glGenQueries( 1, &queryObj );
	return queryObj;
}

void RenderDevice::destroyQuery( uint32 queryObj )
{
	if( queryObj == 0 ) return;

	glDeleteQueries( 1, &queryObj );
}

void RenderDevice::beginQuery( uint32 queryObj )
{
	glBeginQuery( GL_SAMPLES_PASSED, queryObj );
}

void RenderDevice::endQuery( uint32 /*queryObj*/ )
{
	glEndQuery( GL_SAMPLES_PASSED );
}

uint32 RenderDevice::getQueryResult( uint32 queryObj )
{
	uint32 samples = 0;
	glGetQueryObjectuiv( queryObj, GL_QUERY_RESULT, &samples );
	return samples;
}

// =================================================================================================
// Internal state management
// =================================================================================================

void RenderDevice::checkGLError()
{
	uint32 error = glGetError();
	ASSERT( error != GL_INVALID_ENUM );
	ASSERT( error != GL_INVALID_VALUE );
	ASSERT( error != GL_INVALID_OPERATION );
	ASSERT( error != GL_OUT_OF_MEMORY );
	ASSERT( error != GL_STACK_OVERFLOW && error != GL_STACK_UNDERFLOW );
}

bool RenderDevice::applyVertexLayout()
{
	if( _newVertLayout == 0 || _curShaderId == 0 ) return false;

	RDIVertexLayout &vl = _vertexLayouts[_newVertLayout - 1];
	RDIShader &shader = _shaders.getRef( _curShaderId );
	RDIInputLayout &inputLayout = shader.inputLayouts[_newVertLayout - 1];

	if( !inputLayout.valid )
		return false;

	// Set vertex attrib pointers
	uint32 newVertexAttribMask = 0;
	for( uint32 i = 0; i < vl.numAttribs; ++i )
	{
		int8 attribIndex = inputLayout.attribIndices[i];
		if( attribIndex >= 0 )
		{
			VertexLayoutAttrib &attrib = vl.attribs[i];
			const RDIVertBufSlot &vbSlot = _vertBufSlots[attrib.vbSlot];

			ASSERT( _buffers.getRef( _vertBufSlots[attrib.vbSlot].vbObj ).glObj != 0 &&
			        _buffers.getRef( _vertBufSlots[attrib.vbSlot].vbObj ).type == GL_ARRAY_BUFFER );

			glBindBuffer( GL_ARRAY_BUFFER, _buffers.getRef( _vertBufSlots[attrib.vbSlot].vbObj ).glObj );
			glVertexAttribPointer( attribIndex, attrib.size, GL_FLOAT, GL_FALSE,
			                       vbSlot.stride, (char *)0 + vbSlot.offset + attrib.offset );

			newVertexAttribMask |= 1 << attribIndex;
		}
	}

	for( uint32 i = 0; i < 16; ++i )
	{
		uint32 curBit = 1 << i;
		if( (newVertexAttribMask & curBit) != (_activeVertexAttribsMask & curBit) )
		{
			if( newVertexAttribMask & curBit ) glEnableVertexAttribArray( i );
			else glDisableVertexAttribArray( i );
		}
	}
	_activeVertexAttribsMask = newVertexAttribMask;

	return true;
}

void RenderDevice::applySamplerState( RDITexture &tex )
{
	uint32 state = tex.samplerState;
	uint32 target = tex.type;

	const uint32 magFilters[] = { GL_LINEAR, GL_LINEAR, GL_NEAREST };
	const uint32 minFiltersMips[] = { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_NEAREST };
	const uint32 maxAniso[] = { 1, 2, 4, 0, 8, 0, 0, 0, 16 };
	const uint32 wrapModes[] = { GL_CLAMP_TO_EDGE, GL_REPEAT, GL_CLAMP_TO_BORDER };

	if( tex.hasMips )
		glTexParameteri( target, GL_TEXTURE_MIN_FILTER, minFiltersMips[(state & SS_FILTER_MASK) >> SS_FILTER_START] );
	else
		glTexParameteri( target, GL_TEXTURE_MIN_FILTER, magFilters[(state & SS_FILTER_MASK) >> SS_FILTER_START] );

	glTexParameteri( target, GL_TEXTURE_MAG_FILTER, magFilters[(state & SS_FILTER_MASK) >> SS_FILTER_START] );
	glTexParameteri( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso[(state & SS_ANISO_MASK) >> SS_ANISO_START] );
	glTexParameteri( target, GL_TEXTURE_WRAP_S, wrapModes[(state & SS_ADDRU_MASK) >> SS_ADDRU_START] );
	glTexParameteri( target, GL_TEXTURE_WRAP_T, wrapModes[(state & SS_ADDRV_MASK) >> SS_ADDRV_START] );
	glTexParameteri( target, GL_TEXTURE_WRAP_R, wrapModes[(state & SS_ADDRW_MASK) >> SS_ADDRW_START] );

	if( !(state & SS_COMP_LEQUAL) )
	{
		glTexParameteri( target, GL_TEXTURE_COMPARE_MODE, GL_NONE );
	}
	else
	{
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL );
	}
}

bool RenderDevice::commitStates( uint32 filter )
{
	if( _pendingMask & filter )
	{
		uint32 mask = _pendingMask & filter;

		// Set viewport
		if( mask & PM_VIEWPORT )
		{
			glViewport( _vpX, _vpY, _vpWidth, _vpHeight );
			_pendingMask &= ~PM_VIEWPORT;
		}

		// Set scissor rect
		if( mask & PM_SCISSOR )
		{
			glScissor( _scX, _scY, _scWidth, _scHeight );
			_pendingMask &= ~PM_SCISSOR;
		}

		// Bind index buffer
		if( mask & PM_INDEXBUF )
		{
			if( _newIndexBuf != _curIndexBuf )
			{
				if( _newIndexBuf != 0 )
					glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _buffers.getRef( _newIndexBuf ).glObj );
				else
					glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );

				_curIndexBuf = _newIndexBuf;
				_pendingMask &= ~PM_INDEXBUF;
			}
		}

		// Bind vertex buffers
		if( mask & PM_VERTLAYOUT )
		{
			//if( _newVertLayout != _curVertLayout || _curShader != _prevShader )
			{
				if( !applyVertexLayout() )
					return false;
				_curVertLayout = _newVertLayout;
				_prevShaderId = _curShaderId;
				_pendingMask &= ~PM_VERTLAYOUT;
			}
		}

		// Bind textures and set sampler state
		if( mask & PM_TEXTURES )
		{
			for( uint32 i = 0; i < 16; ++i )
			{
				glActiveTexture( GL_TEXTURE0 + i );

				if( _texSlots[i].texObj != 0 )
				{
					RDITexture &tex = _textures.getRef( _texSlots[i].texObj );
					glBindTexture( tex.type, tex.glObj );

					// Apply sampler state
					if( tex.samplerState != _texSlots[i].samplerState )
					{
						tex.samplerState = _texSlots[i].samplerState;
						applySamplerState( tex );
					}
				}
				else
				{
					glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
					glBindTexture( GL_TEXTURE_3D, 0 );
					glBindTexture( GL_TEXTURE_2D, 0 );
				}
			}

			_pendingMask &= ~PM_TEXTURES;
		}

		CHECK_GL_ERROR
	}

	return true;
}

void RenderDevice::resetStates()
{
	static int maxVertexAttribs = 0;
	if( maxVertexAttribs == 0 )
		glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs );

	_curIndexBuf = 1; _newIndexBuf = 0;
	_curVertLayout = 1; _newVertLayout = 0;

	for( uint32 i = 0; i < 16; ++i )
		setTexture( i, 0, 0 );

	_activeVertexAttribsMask = 0;

	for( uint32 i = 0; i < (uint32)maxVertexAttribs; ++i )
		glDisableVertexAttribArray( i );

	_pendingMask = 0xFFFFFFFF;
	commitStates();
}

// =================================================================================================
// Draw calls and clears
// =================================================================================================

void RenderDevice::clear( uint32 flags, float *colorRGBA, float depth )
{
	uint32 mask = 0;

	if( flags & CLR_DEPTH )
	{
		mask |= GL_DEPTH_BUFFER_BIT;
		glClearDepth( depth );
	}
	if( flags & CLR_COLOR )
	{
		mask |= GL_COLOR_BUFFER_BIT;
		if( colorRGBA ) glClearColor( colorRGBA[0], colorRGBA[1], colorRGBA[2], colorRGBA[3] );
		else glClearColor( 0, 0, 0, 0 );
	}

	if( mask )
	{
		commitStates( PM_VIEWPORT | PM_SCISSOR );
		glClear( mask );
	}

	CHECK_GL_ERROR
}

void RenderDevice::draw( RDIPrimType primType, uint32 firstVert, uint32 numVerts )
{
	if( commitStates() )
	{
		glDrawArrays( (uint32)primType, firstVert, numVerts );
	}

	CHECK_GL_ERROR
}

void RenderDevice::drawIndexed( RDIPrimType primType, uint32 firstIndex, uint32 numIndices,
                                uint32 firstVert, uint32 numVerts )
{
	if( commitStates() )
	{
		firstIndex *= (_indexFormat == IDXFMT_16) ? sizeof( short ) : sizeof( int );

		glDrawRangeElements( (uint32)primType, firstVert, firstVert + numVerts,
		                     numIndices, _indexFormat, (char *)0 + firstIndex );
	}

	CHECK_GL_ERROR
}

}  // namespace

redpajama_set_name: RedPajamaGithub


text:
<?php

namespace Google\Service\BigQueryReservation\Resource;

use Google\Service\BigQueryReservation\BigqueryreservationEmpty;
use Google\Service\BigQueryReservation\CapacityCommitment;
use Google\Service\BigQueryReservation\ListCapacityCommitmentsResponse;
use Google\Service\BigQueryReservation\MergeCapacityCommitmentsRequest;
use Google\Service\BigQueryReservation\SplitCapacityCommitmentRequest;
use Google\Service\BigQueryReservation\SplitCapacityCommitmentResponse;

/**
 * The "capacityCommitments" collection of methods.
 * Typical usage is:
 *  <code>
 *   $bigqueryreservationService = new Google\Service\BigQueryReservation(...);
 *   $capacityCommitments = $bigqueryreservationService->capacityCommitments;
 *  </code>
 */
class ProjectsLocationsCapacityCommitments extends \Google\Service\Resource
{
  /**
   * Creates a new capacity commitment resource. (capacityCommitments.create)
   *
   * @param string $parent Required. Resource name of the parent reservation.
   * E.g., `projects/myproject/locations/US`
   * @param CapacityCommitment $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string capacityCommitmentId The optional capacity commitment ID.
   * Capacity commitment name will be generated automatically if this field is
   * empty. This field must only contain lower case alphanumeric characters or
   * dashes. The first and last character cannot be a dash. Max length is 64
   * characters. NOTE: this ID won't be kept if the capacity commitment is split
   * or merged.
   * @opt_param bool enforceSingleAdminProjectPerOrg If true, fail the request if
   * another project in the organization has a capacity commitment.
   * @return CapacityCommitment
   */
  public function create($parent, CapacityCommitment $postBody, $optParams = [])
  {
    $params = ['parent' => $parent, 'postBody' => $postBody];
    $params = array_merge($params, $optParams);
    return $this->call('create', [$params], CapacityCommitment::class);
  }
  /**
   * Deletes a capacity commitment. Attempting to delete capacity commitment
   * before its commitment_end_time will fail with the error code
   * `google.rpc.Code.FAILED_PRECONDITION`. (capacityCommitments.delete)
   *
   * @param string $name Required. Resource name of the capacity commitment to
   * delete. E.g., `projects/myproject/locations/US/capacityCommitments/123`
   * @param array $optParams Optional parameters.
   *
   * @opt_param bool force Can be used to force delete commitments even if
   * assignments exist. Deleting commitments with assignments may cause queries to
   * fail if they no longer have access to slots.
   * @return BigqueryreservationEmpty
   */
  public function delete($name, $optParams = [])
  {
    $params = ['name' => $name];
    $params = array_merge($params, $optParams);
    return $this->call('delete', [$params], BigqueryreservationEmpty::class);
  }
  /**
   * Returns information about the capacity commitment. (capacityCommitments.get)
   *
   * @param string $name Required. Resource name of the capacity commitment to
   * retrieve. E.g., `projects/myproject/locations/US/capacityCommitments/123`
   * @param array $optParams Optional parameters.
   * @return CapacityCommitment
   */
  public function get($name, $optParams = [])
  {
    $params = ['name' => $name];
    $params = array_merge($params, $optParams);
    return $this->call('get', [$params], CapacityCommitment::class);
  }
  /**
   * Lists all the capacity commitments for the admin project.
   * (capacityCommitments.listProjectsLocationsCapacityCommitments)
   *
   * @param string $parent Required. Resource name of the parent reservation.
   * E.g., `projects/myproject/locations/US`
   * @param array $optParams Optional parameters.
   *
   * @opt_param int pageSize The maximum number of items to return.
   * @opt_param string pageToken The next_page_token value returned from a
   * previous List request, if any.
   * @return ListCapacityCommitmentsResponse
   */
  public function listProjectsLocationsCapacityCommitments($parent, $optParams = [])
  {
    $params = ['parent' => $parent];
    $params = array_merge($params, $optParams);
    return $this->call('list', [$params], ListCapacityCommitmentsResponse::class);
  }
  /**
   * Merges capacity commitments of the same plan into a single commitment. The
   * resulting capacity commitment has the greater commitment_end_time out of the
   * to-be-merged capacity commitments. Attempting to merge capacity commitments
   * of different plan will fail with the error code
   * `google.rpc.Code.FAILED_PRECONDITION`. (capacityCommitments.merge)
   *
   * @param string $parent Parent resource that identifies admin project and
   * location e.g., `projects/myproject/locations/us`
   * @param MergeCapacityCommitmentsRequest $postBody
   * @param array $optParams Optional parameters.
   * @return CapacityCommitment
   */
  public function merge($parent, MergeCapacityCommitmentsRequest $postBody, $optParams = [])
  {
    $params = ['parent' => $parent, 'postBody' => $postBody];
    $params = array_merge($params, $optParams);
    return $this->call('merge', [$params], CapacityCommitment::class);
  }
  /**
   * Updates an existing capacity commitment. Only `plan` and `renewal_plan`
   * fields can be updated. Plan can only be changed to a plan of a longer
   * commitment period. Attempting to change to a plan with shorter commitment
   * period will fail with the error code `google.rpc.Code.FAILED_PRECONDITION`.
   * (capacityCommitments.patch)
   *
   * @param string $name Output only. The resource name of the capacity
   * commitment, e.g., `projects/myproject/locations/US/capacityCommitments/123`
   * The commitment_id must only contain lower case alphanumeric characters or
   * dashes. It must start with a letter and must not end with a dash. Its maximum
   * length is 64 characters.
   * @param CapacityCommitment $postBody
   * @param array $optParams Optional parameters.
   *
   * @opt_param string updateMask Standard field mask for the set of fields to be
   * updated.
   * @return CapacityCommitment
   */
  public function patch($name, CapacityCommitment $postBody, $optParams = [])
  {
    $params = ['name' => $name, 'postBody' => $postBody];
    $params = array_merge($params, $optParams);
    return $this->call('patch', [$params], CapacityCommitment::class);
  }
  /**
   * Splits capacity commitment to two commitments of the same plan and
   * `commitment_end_time`. A common use case is to enable downgrading
   * commitments. For example, in order to downgrade from 10000 slots to 8000, you
   * might split a 10000 capacity commitment into commitments of 2000 and 8000.
   * Then, you delete the first one after the commitment end time passes.
   * (capacityCommitments.split)
   *
   * @param string $name Required. The resource name e.g.,:
   * `projects/myproject/locations/US/capacityCommitments/123`
   * @param SplitCapacityCommitmentRequest $postBody
   * @param array $optParams Optional parameters.
   * @return SplitCapacityCommitmentResponse
   */
  public function split($name, SplitCapacityCommitmentRequest $postBody, $optParams = [])
  {
    $params = ['name' => $name, 'postBody' => $postBody];
    $params = array_merge($params, $optParams);
    return $this->call('split', [$params], SplitCapacityCommitmentResponse::class);
  }
}

// Adding a class alias for backwards compatibility with the previous class name.
class_alias(ProjectsLocationsCapacityCommitments::class, 'Google_Service_BigQueryReservation_Resource_ProjectsLocationsCapacityCommitments');


text:
package org.batfish.coordinator;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.batfish.common.BatfishLogger;
import org.batfish.common.BatfishWorkerService;
import org.batfish.common.BfConsts;
import org.batfish.common.LaunchResult;
import org.batfish.common.Task;

/**
 * {@link WorkExecutor} implementation that delegates directly to a {@link BatfishWorkerService}.
 */
@ParametersAreNonnullByDefault
public class BatfishWorkerServiceWorkExecutor implements WorkExecutor {

  public BatfishWorkerServiceWorkExecutor(
      BatfishLogger logger, Path containersLocation, BatfishWorkerService batfishWorkerService) {
    _logger = logger;
    _containersLocation = containersLocation;
    _batfishWorkerService = batfishWorkerService;
  }

  @Override
  public SubmissionResult submit(QueuedWork work) {
    try {
      return doSubmit(work);
    } catch (Exception e) {
      return SubmissionResult.error(
          String.format("Exception submitting work: %s\n", Throwables.getStackTraceAsString(e)));
    }
  }

  @VisibleForTesting
  @Nonnull
  SubmissionResult doSubmit(QueuedWork work) {
    String taskId = work.getId().toString();
    checkArgument(!Strings.isNullOrEmpty(taskId), "taskId must be non-empty");
    List<String> args = getRunArgs(work, _containersLocation);
    _logger.infof("Will run task %s with args: %s\n", taskId, args);
    LaunchResult lr = _batfishWorkerService.runTask(taskId, args.toArray(new String[] {}));
    return processLaunchResult(lr, taskId, _batfishWorkerService);
  }

  @VisibleForTesting
  static @Nonnull SubmissionResult processLaunchResult(
      LaunchResult lr, String taskId, BatfishWorkerService batfishWorkerService) {
    switch (lr.getType()) {
      case LAUNCHED:
        return SubmissionResult.success(
            () ->
                Optional.ofNullable(batfishWorkerService.getTaskStatus(taskId))
                    .orElse(Task.unknown()));
      case BUSY:
        return SubmissionResult.busy();
      case ERROR:
        return SubmissionResult.error(lr.getMessage());
      default:
        throw new IllegalArgumentException(
            String.format("Invalid LaunchResult.Type: %s", lr.getType()));
    }
  }

  @VisibleForTesting
  static @Nonnull List<String> getRunArgs(QueuedWork work, Path containersLocation) {
    Map<String, String> params = new TreeMap<>(work.resolveRequestParams());
    params.put(BfConsts.ARG_STORAGE_BASE, containersLocation.toAbsolutePath().toString());
    ImmutableList.Builder<String> argsBuilder = ImmutableList.builder();
    params.forEach(
        (k, v) -> {
          argsBuilder.add("-" + k);
          if (!Strings.isNullOrEmpty(v)) {
            argsBuilder.add(v);
          }
        });
    return argsBuilder.build();
  }

  private final @Nonnull Path _containersLocation;
  private final @Nonnull BatfishLogger _logger;
  private final @Nonnull BatfishWorkerService _batfishWorkerService;
}


text:
require 'pullr/scm/scm'

require 'spec_helper'
require 'addressable/uri'
require 'fileutils'
require 'tmpdir'

describe SCM do
  it "should lookup an SCM with a String name" do
    SCM.lookup('git').should == SCM::Git
  end

  it "should lookup an SCM with a Symbol name" do
    SCM.lookup(:git).should == SCM::Git
  end

  it "should raise UnknownSCM when looking up unknown SCM names" do
    lambda {
      SCM.lookup(:bla)
    }.should raise_error(UnknownSCM)
  end

  describe "Git" do
    it "should be known as 'git'" do
      SCM.lookup(:git).should == SCM::Git
    end

    it "should be inferable from the scheme of a URI" do
      uri = Addressable::URI.parse('git://sourceforge.net')

      SCM.infer_from_uri(uri).should == :git
    end

    it "should be inferable from the extension of a URI" do
      uri = Addressable::URI.parse('git@github.com/user/project.git')

      SCM.infer_from_uri(uri).should == :git
    end

    it "should be inferable from the SCM control directory" do
      repo = Dir.mktmpdir('repo')
      FileUtils.mkdir(File.join(repo,'.git'))

      SCM.infer_from_dir(repo).should == :git
    end
  end

  describe "Mercurial" do
    it "should be known as 'mercurial'" do
      SCM.lookup(:mercurial).should == SCM::Mercurial
    end

    it "should also be known as 'hg'" do
      SCM.lookup(:hg).should == SCM::Mercurial
    end

    it "should be inferable from the scheme of a URI" do
      uri = Addressable::URI.parse('hg://sourceforge.net')

      SCM.infer_from_uri(uri).should == :mercurial
    end

    it "should be inferable from the SCM control directory" do
      repo = Dir.mktmpdir('repo')
      FileUtils.mkdir(File.join(repo,'.hg'))

      SCM.infer_from_dir(repo).should == :mercurial
    end
  end

  describe "SubVersion" do
    it "should be known as 'sub_version'" do
      SCM.lookup(:sub_version).should == SCM::SubVersion
    end

    it "should also be known as 'subversion'" do
      SCM.lookup(:subversion).should == SCM::SubVersion
    end

    it "should also be known as 'svn'" do
      SCM.lookup(:svn).should == SCM::SubVersion
    end

    it "should be inferable from the scheme of a URI" do
      uri = Addressable::URI.parse('svn://sourceforge.net')

      SCM.infer_from_uri(uri).should == :sub_version
    end

    it "should be inferable from the 'svn+ssh' URI scheme " do
      uri = Addressable::URI.parse('svn+ssh://sourceforge.net')

      SCM.infer_from_uri(uri).should == :sub_version
    end

    it "should be inferable from the SCM control directory" do
      repo = Dir.mktmpdir('repo')
      FileUtils.mkdir(File.join(repo,'.svn'))

      SCM.infer_from_dir(repo).should == :sub_version
    end
  end

  describe "Rsync" do
    it "should be known as 'rsync'" do
      SCM.lookup(:rsync).should == SCM::Rsync
    end

    it "should be inferable from the scheme of a URI" do
      uri = Addressable::URI.parse('rsync://sourceforge.net')

      SCM.infer_from_uri(uri).should == :rsync
    end
  end
end


text:
var config = {
  ns: 'semantic'
, filename: __filename
}

module.exports = function(app, options) {
  var outConfig = Object.create(config)
  outConfig.styles = options && options.styles ||
    __dirname + '/semantic/build/packaged/css/semantic.min'
  app.createLibrary(outConfig, options)
}


text:
"use strict";

// Gulp specific modules
var gulp = require("gulp");
var sourcemaps = require("gulp-sourcemaps");

// JS related modules
var concat = require("gulp-concat");
var uglify = require("gulp-uglify");

// Common utilities
var utils = require("./gulp-utils.js");

// Local data pased from master gulpfile.js
var config;
var buildMode;
var paths;
var filters;

function jsPipeline(files) {

    var stream = gulp.src(files, { base: paths.src, cwd: paths.src });

    if (buildMode === "release") {
        stream.pipe(sourcemaps.init())
        .pipe(uglify())
        .pipe(sourcemaps.write(".", { addComment: false }))
        .pipe(utils.appendSourcemap(".js"));
    }

    stream.pipe(utils.renamePipeline())
        .pipe(gulp.dest(paths.dest));

    return stream;
}

gulp.task("js", function () {
    return jsPipeline(filters.js);
});

gulp.task("jsbundle", function () {
    return gulp.src(paths.jsBundle, { base: paths.src, cwd: paths.src })
        .pipe(sourcemaps.init())
        .pipe(concat(paths.jsBundleName))
        .pipe(uglify())
        .pipe(sourcemaps.write(".", { addComment: false }))
        .pipe(utils.appendSourcemap(".js"))
        .pipe(gulp.dest(paths.dest + paths.js));
});

var exported = {
    pipeline: jsPipeline
};

module.exports = function (_paths, _filters, _config) {
    paths = _paths;
    filters = _filters;
    config = _config;

    buildMode = config.buildMode;

    return exported;
};


text:
package dev.wonderland.gameDev.utils;

public class DebugVars {
	public static boolean debugMode = true;
}


text:
package org.jetbrains.plugins.gradle.execution.test.runner;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
import com.intellij.execution.testframework.sm.runner.SMTestProxy;
import com.intellij.execution.testframework.sm.runner.states.TestStateInfo;
import com.intellij.execution.testframework.sm.runner.ui.SMRootTestProxyFormatter;
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
import com.intellij.execution.testframework.sm.runner.ui.TestTreeRenderer;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.execution.ExternalSystemExecutionConsoleManager;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo;
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTask;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.service.internal.ExternalSystemExecuteTaskTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.util.XmlXpathHelper;
import org.jetbrains.plugins.gradle.execution.test.runner.events.*;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;

import java.util.Map;

/**
 * @author Vladislav.Soroka
 * @since 2/18/14
 */
public class GradleTestsExecutionConsoleManager implements ExternalSystemExecutionConsoleManager<ExternalSystemRunConfiguration> {
  private static final Logger LOG = Logger.getInstance(GradleTestsExecutionConsoleManager.class);

  private Map<String, SMTestProxy> testsMap = ContainerUtil.newHashMap();
  private StringBuilder myBuffer = new StringBuilder();
  private SMTRunnerConsoleView myExecutionConsole;

  @NotNull
  @Override
  public ProjectSystemId getExternalSystemId() {
    return GradleConstants.SYSTEM_ID;
  }

  public Map<String, SMTestProxy> getTestsMap() {
    return testsMap;
  }

  public GradleUrlProvider getUrlProvider() {
    return GradleUrlProvider.INSTANCE;
  }

  public SMTRunnerConsoleView getExecutionConsole() {
    return myExecutionConsole;
  }

  @NotNull
  @Override
  public ExecutionConsole attachExecutionConsole(@NotNull final ExternalSystemTask task,
                                                 @NotNull final Project project,
                                                 @NotNull final ExternalSystemRunConfiguration configuration,
                                                 @NotNull final Executor executor,
                                                 @NotNull final ExecutionEnvironment env,
                                                 @NotNull final ProcessHandler processHandler) throws ExecutionException {
    final GradleConsoleProperties properties = new GradleConsoleProperties(configuration, executor);
    myExecutionConsole = (SMTRunnerConsoleView)SMTestRunnerConnectionUtil.createAndAttachConsole(
      configuration.getSettings().getExternalSystemId().getReadableName(), processHandler, properties);

    TestTreeRenderer originalRenderer =
      ObjectUtils.tryCast(myExecutionConsole.getResultsViewer().getTreeView().getCellRenderer(), TestTreeRenderer.class);
    if (originalRenderer != null) {
      originalRenderer.setAdditionalRootFormatter(new SMRootTestProxyFormatter() {
        @Override
        public void format(@NotNull SMTestProxy.SMRootTestProxy testProxy, @NotNull TestTreeRenderer renderer) {
          final TestStateInfo.Magnitude magnitude = testProxy.getMagnitudeInfo();
          if (magnitude == TestStateInfo.Magnitude.RUNNING_INDEX) {
            renderer.clear();
            renderer.append(GradleBundle.message(
                              "gradle.test.runner.ui.tests.tree.presentation.labels.waiting.tests"),
                            SimpleTextAttributes.REGULAR_ATTRIBUTES
            );
          }
          else if (!testProxy.isInProgress() && testProxy.isEmptySuite()) {
            renderer.clear();
            renderer.append(GradleBundle.message(
                              "gradle.test.runner.ui.tests.tree.presentation.labels.no.tests.were.found"),
                            SimpleTextAttributes.REGULAR_ATTRIBUTES
            );
          }
        }
      });
    }
    return myExecutionConsole;
  }

  @Override
  public void onOutput(@NotNull String text, @NotNull Key processOutputType) {
    if (StringUtil.endsWith(text, "<ijLogEol/>\n")) {
      myBuffer.append(StringUtil.trimEnd(text, "<ijLogEol/>\n")).append('\n');
      return;
    }
    else {
      myBuffer.append(text);
    }

    String trimmedText = myBuffer.toString().trim();
    myBuffer.setLength(0);

    if (!StringUtil.startsWith(trimmedText, "<ijLog>") || !StringUtil.endsWith(trimmedText, "</ijLog>")) {
      if (text.trim().isEmpty()) return;
      myExecutionConsole.print(text, ConsoleViewContentType.getConsoleViewType(processOutputType));
      return;
    }

    try {
      final XmlXpathHelper xml = new XmlXpathHelper(trimmedText);

      final TestEventType eventType = TestEventType.fromValue(xml.queryXml("/ijLog/event/@type"));
      TestEvent testEvent = null;
      switch (eventType) {
        case CONFIGURATION_ERROR:
          testEvent = new ConfigurationErrorEvent(this);
          break;
        case REPORT_LOCATION:
          testEvent = new ReportLocationEvent(this);
          break;
        case BEFORE_TEST:
          testEvent = new BeforeTestEvent(this);
          break;
        case ON_OUTPUT:
          testEvent = new OnOutputEvent(this);
          break;
        case AFTER_TEST:
          testEvent = new AfterTestEvent(this);
          break;
        case BEFORE_SUITE:
          testEvent = new BeforeSuiteEvent(this);
          break;
        case AFTER_SUITE:
          testEvent = new AfterSuiteEvent(this);
          break;
        case UNKNOWN_EVENT:
          break;
      }
      if (testEvent != null) {
        testEvent.process(xml);
      }
    }
    catch (XmlXpathHelper.XmlParserException e) {
      LOG.error("Gradle test events parser error", e);
    }
  }

  @Override
  public boolean isApplicableFor(@NotNull ExternalSystemTask task) {
    if (task instanceof ExternalSystemExecuteTaskTask) {
      ExternalSystemExecuteTaskTask taskTask = (ExternalSystemExecuteTaskTask)task;
      if (!StringUtil.equals(taskTask.getExternalSystemId().getId(), GradleConstants.SYSTEM_ID.getId())) return false;

      return ContainerUtil.find(taskTask.getTasksToExecute(), new Condition<ExternalTaskPojo>() {
        @Override
        public boolean value(ExternalTaskPojo pojo) {
          return "test".equals(pojo.getName());
        }
      }) != null;
    }
    return false;
  }

  @Override
  public AnAction[] getRestartActions() {
    return new AnAction[0];
  }
}


text:
.. _quickref_:

Quick reference for the WiPy
============================

.. image:: https://raw.githubusercontent.com/wipy/wipy/master/docs/PinOUT.png
    :alt: WiPy pinout and alternate functions table
    :width: 800px

General board control
---------------------

See :mod:`pyb`. ::

    import pyb

    help(pyb) # display all members from the pyb module
    pyb.delay(50) # wait 50 milliseconds
    pyb.millis() # number of milliseconds since boot-up
    pyb.freq() # get the CPU frequency
    pyb.unique_id() # return the 6-byte unique id of the board (the WiPy's MAC address)

Pins and GPIO
-------------

See :ref:`pyb.Pin <pyb.Pin>`. ::

    from pyb import Pin

    # initialize GPIO2 in gpio mode (af=0) and make it an output
    p_out = Pin('GPIO2', af=0, mode=Pin.OUT)
    p_out.high()
    p_out.low()
    p_out.toggle()

    # make GPIO1 an input with the pull-up enabled
    p_in = Pin('GPIO1', af = 0, mode=Pin.IN, type = Pin.STD_PU)
    p_in.value() # get value, 0 or 1

Timers
------

See :ref:`pyb.Timer <pyb.Timer>` and :ref:`pyb.Pin <pyb.Pin>`. ::

    from pyb import Timer
    from pyb import Pin

    tim = Timer(1, mode=Timer.PERIODIC)
    tim_a = tim.channel(Timer.A, freq=1000)
    tim_a.time() # get the value in microseconds
    tim_a.freq(1) # 1 Hz

    p_out = Pin('GPIO2', af=0, mode=Pin.OUT)
    tim_a.callback(handler=lambda t: p_out.toggle())

PWM (pulse width modulation)
----------------------------

See :ref:`pyb.Pin <pyb.Pin>` and :ref:`pyb.Timer <pyb.Timer>`. ::

    from pyb import Timer
    from pyb import Pin

    # assign GPIO25 to alternate function 5 (PWM)
    p_out = Pin('GPIO25', af=9, type=Pin.STD)

    # timer 2 in PWM mode and width must be 16 buts
    tim = Timer(2, mode=Timer.PWM, width=16)

    # enable channel A @1KHz with a 50% duty cycle
    tim_a = tim.channel(Timer.A, freq=1000, duty_cycle=50)

ADC (analog to digital conversion)
----------------------------------

See :ref:`pyb.ADC <pyb.ADC>`. ::

    from pyb import ADC

    adc = ADC(1)
    adc.read() # read value, 0-4095

UART (serial bus)
-----------------

See :ref:`pyb.Pin <pyb.Pin>` and :ref:`pyb.UART <pyb.UART>`. ::

    from pyb import Pin, UART

    # first assign TX and RX to the correct pins
    Pin('GPIO1', af=3, mode=Pin.STD_PU)    # TX
    Pin('GPIO2', af=3, mode=Pin.STD_PU)    # RX

    uart = UART(1, 9600)
    uart.write('hello')
    uart.read(5) # read up to 5 bytes

SPI bus
-------

See :ref:`pyb.Pin <pyb.Pin>` and :ref:`pyb.SPI <pyb.SPI>`. ::

    from pyb import Pin, SPI

    # first assign CLK, MISO, MOSI, CS to the correct pins
    Pin('GPIO14', af=7, mode=Pin.STD)    # CLK
    Pin('GPIO15', af=7, mode=Pin.STD)    # MISO
    Pin('GPIO16', af=7, mode=Pin.STD)    # MOSI
    Pin('GPIO17', af=7, mode=Pin.STD)    # NSS/CS

    # configure the SPI master @ 2MHz
    spi = SPI(1, SPI.MASTER, baudrate=200000, polarity=0, phase=0)
    spi.send('hello')
    spi.recv(5) # receive 5 bytes on the bus
    spi.send_recv('hello') # send a receive 5 bytes

I2C bus
-------

See :ref:`pyb.Pin <pyb.Pin>` and :ref:`pyb.I2C <pyb.I2C>`. ::

    from pyb import Pin, I2C

    # first assign SCL and SDA to the correct pins
    Pin('GPIO23', af=9, mode=Pin.STD_PU)  # SCL
    Pin('GPIO24', af=9, mode=Pin.STD_PU)  # SDA

    # configure the I2C bus
    i2c = I2C(1, I2C.MASTER, baudrate=100000)
    i2c.scan() # returns list of slave addresses
    i2c.send('hello', 0x42) # send 5 bytes to slave with address 0x42
    i2c.recv(5, 0x42) # receive 5 bytes from slave
    i2c.mem_read(2, 0x42, 0x10) # read 2 bytes from slave 0x42, slave memory 0x10
    i2c.mem_write('xy', 0x42, 0x10) # write 2 bytes to slave 0x42, slave memory 0x10

Watchdog timer (WDT)
--------------------

See :ref:`pyb.WDT <pyb.WDT>`. ::

    from pyb import WDT

    # enable the WDT with a timeout of 5s (1s is the minimum)
    wdt = WDT(5000)
    wdt.kick()

Real time clock (RTC)
---------------------

See :ref:`pyb.RTC <pyb.RTC>` and ``pyb.Sleep``. ::

    from pyb import RTC, Sleep

    rtc = pyb.RTC()
    rtc.datetime((2014, 5, 1, 4, 13, 0, 0, 0))
    print(rtc.datetime())

    def some_handler (rtc_obj):
        # trigger the callback again in 30s
        rtc_obj.callback(value=30000, handler=some_handler)

    # create a RTC alarm that expires in 30s
    rtc.callback(value=30000, handler=some_handler, wakes=Sleep.SUSPENDED)

    # go into suspended mode waiting for the RTC alarm to expire and wake us up
    Sleep.suspend()

SD card
-------

See :ref:`pyb.SD <pyb.SD>`. ::

    from pyb import SD

    # SD card pins need special configuration so we pass 'em to the constructor
    # data pin, data af, clock pin, clock af, cmd pin, cmd af
    sd = pyb.SD('GPIO15', 8, 'GPIO10', 6, 'GPIO11', 6)
    sd.enable()

WLAN (WiFi)
-----------

See :ref:`network.WLAN <network.WLAN>` and ``pyb.Sleep``. ::

    from network import WLAN
    from pyb import Sleep

    # configure the WLAN subsystem in station mode (the default is AP)
    wifi = WLAN(WLAN.STA)
    # go for fixed IP settings
    wifi.ifconfig('192.168.0.107', '255.255.255.0', '192.168.0.1', '8.8.8.8')
    wifi.scan()     # scan for available netrworks
    wifi.connect(ssid='mynetwork', security=2, key='mynetworkkey')
    while not wifi.isconnected():
        pass
    print(wifi.ifconfig())
    # enable wake on WLAN
    wifi.callback(wakes=Sleep.SUSPENDED)
    # go to sleep
    Sleep.suspend()
    # now, connect to the FTP or the Telnet server and the WiPy will wake-up

Sleep and power modes control
-----------------------------

See ``pyb.Sleep``. ::

    from pyb import Sleep

    Sleep.idle()        # lowest sleep mode (~12mA), any interrupts wakes it up
    Sleep.suspend()     # everything except for WLAN is powered down (~950uA)
                        # wakes from Pin, RTC or WLAN

    Sleep.hibernate()   # deepest sleep mode, MCU starts from reset. Wakes from Pin and RTC.

Heart beat LED
--------------

See :ref:`pyb.HeartBeat <pyb.HeartBeat>`. ::

    from pyb import HeartBeat

    # disable the heart beat indication (you are free to use this LED connected to GPIO25)
    HeartBeat().disable()
    # enable the heart beat again
    HeartBeat().enable()