/*
 * cocos2d for iPhone: http://www.cocos2d-iphone.org
 *
 * Copyright (c) 2008-2010 Ricardo Quesada
 * Copyright (c) 2011 Zynga Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

#import <Availability.h>
#import "CCParticleSystemPoint.h"

#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED

// opengl
#import "Platforms/CCGL.h"

// cocos2d
#import "CCTextureCache.h"
#import "ccMacros.h"

// support
#import "Support/OpenGL_Internal.h"
#import "Support/CGPointExtension.h"

@implementation CCParticleSystemPoint

-(id) initWithTotalParticles:(NSUInteger) numberOfParticles
{
	if( (self=[super initWithTotalParticles:numberOfParticles]) ) {

		vertices = malloc( sizeof(ccPointSprite) * totalParticles );

		if( ! vertices ) {
			NSLog(@"cocos2d: Particle system: not enough memory");
			[self release];
			return nil;
		}

#if CC_USES_VBO
		glGenBuffers(1, &verticesID);
		
		// initial binding
		glBindBuffer(GL_ARRAY_BUFFER, verticesID);
		glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*totalParticles, vertices, GL_DYNAMIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
#endif
	}

	return self;
}

-(void) dealloc
{
	free(vertices);
#if CC_USES_VBO
	glDeleteBuffers(1, &verticesID);
#endif
	
	[super dealloc];
}

-(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos
{	
	// place vertices and colos in array
	vertices[particleIdx].pos = (ccVertex2F) {newPos.x, newPos.y};
	vertices[particleIdx].size = p->size;
	ccColor4B color =  { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255 };
	vertices[particleIdx].color = color;
}

-(void) postStep
{
#if CC_USES_VBO
	glBindBuffer(GL_ARRAY_BUFFER, verticesID);
	glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ccPointSprite)*particleCount, vertices);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
#endif
}

-(void) draw
{
	[super draw];

    if (particleIdx==0)
        return;
	
	// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY
	// Unneeded states: GL_TEXTURE_COORD_ARRAY
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	
	glBindTexture(GL_TEXTURE_2D, texture_.name);
	
	glEnable(GL_POINT_SPRITE_OES);
	glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE );
	
#define kPointSize sizeof(vertices[0])

#if CC_USES_VBO
	glBindBuffer(GL_ARRAY_BUFFER, verticesID);

	glVertexPointer(2,GL_FLOAT, kPointSize, 0);

	glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) offsetof(ccPointSprite, color) );

	glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
	glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccPointSprite, size) );
#else // Uses Vertex Array List
	int offset = (int)vertices;
	glVertexPointer(2,GL_FLOAT, kPointSize, (GLvoid*) offset);
	
	int diff = offsetof(ccPointSprite, color);
	glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) (offset+diff));
	
	glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
	diff = offsetof(ccPointSprite, size);
	glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) (offset+diff));
#endif

	BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
	if( newBlend )
		glBlendFunc( blendFunc_.src, blendFunc_.dst );


	glDrawArrays(GL_POINTS, 0, particleIdx);
	
	// restore blend state
	if( newBlend )
		glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST);

	
#if CC_USES_VBO
	// unbind VBO buffer
	glBindBuffer(GL_ARRAY_BUFFER, 0);
#endif
	
	glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
	glDisable(GL_POINT_SPRITE_OES);

	// restore GL default state
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

#pragma mark Non supported properties

//
// SPIN IS NOT SUPPORTED
//
-(void) setStartSpin:(float)a
{
	NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
	[super setStartSpin:a];
}
-(void) setStartSpinVar:(float)a
{
	NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
	[super setStartSpin:a];
}
-(void) setEndSpin:(float)a
{
	NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
	[super setStartSpin:a];
}
-(void) setEndSpinVar:(float)a
{
	NSAssert(a == 0, @"PointParticleSystem doesn't support spinning");
	[super setStartSpin:a];
}

//
// SIZE > 64 IS NOT SUPPORTED
//
-(void) setStartSize:(float)size
{
	NSAssert(size >= 0 && size <= CC_MAX_PARTICLE_SIZE, @"PointParticleSystem only supports 0 <= size <= 64");
	[super setStartSize:size];
}

-(void) setEndSize:(float)size
{
	NSAssert( (size == kCCParticleStartSizeEqualToEndSize) ||
			 ( size >= 0 && size <= CC_MAX_PARTICLE_SIZE), @"PointParticleSystem only supports 0 <= size <= 64");
	[super setEndSize:size];
}
@end

#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
@implementation CCParticleSystemPoint
@end

#endif // __MAC_OS_X_VERSION_MAX_ALLOWED