#import "glview.h"
#include "game.h"
#include "screen.h"
#include "logger.h"

unsigned int default_fbo;

static GLView *view;

void request_redisplay()
{
	// TODO
}

void swap_buffers()
{
	if(view) {
		[view swap_buffers];
	}
}


@implementation GLView

@synthesize animating;
@dynamic anim_frame_interval;

// You must implement this method
+ (Class)layerClass
{
	return [CAEAGLLayer class];
}


- (id)initWithFrame: (CGRect) frame
{
	if((self = [super initWithFrame: frame]))
	{
		// Get the layer
		CAEAGLLayer *eaglLayer = (CAEAGLLayer*)self.layer;

		//self.contentScaleFactor = 2.0;

		eaglLayer.opaque = TRUE;
		eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:FALSE],
				kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8,
				kEAGLDrawablePropertyColorFormat, nil];

		// initialize OpenGL ES context
		context = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
		if(!context || ![EAGLContext setCurrentContext: context]) {
			[self release];
			return self;
		}

		// create the framebuffer
		glGenFramebuffers(1, &fbo);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo);

		glGenRenderbuffers(1, &rbuf_color);
		glBindRenderbuffer(GL_RENDERBUFFER, rbuf_color);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbuf_color);

		glGenRenderbuffers(1, &rbuf_depth);
		glBindRenderbuffer(GL_RENDERBUFFER, rbuf_depth);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_depth);

		default_fbo = fbo;

		animating = FALSE;
		display_link_supported = FALSE;
		anim_frame_interval = 1;
		display_link = nil;
		anim_timer = nil;

		// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer
		// class is used as fallback when it isn't available.
		NSString *reqSysVer = @"3.1";
		NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
		if([currSysVer compare: reqSysVer options: NSNumericSearch] != NSOrderedAscending) {
			display_link_supported = TRUE;
		}

		self.multipleTouchEnabled = 1;

		if(!game_init()) {
			exit(1);
		}
	}

	view = self;
	return self;
}

- (void)drawView: (id)sender
{
	game_display();
}

- (void)swap_buffers
{
	glBindRenderbuffer(GL_RENDERBUFFER, rbuf_color);
	[context presentRenderbuffer: GL_RENDERBUFFER];
}

- (BOOL)resize: (CAEAGLLayer*)layer
{
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);

	glBindRenderbuffer(GL_RENDERBUFFER, rbuf_color);
	[context renderbufferStorage: GL_RENDERBUFFER fromDrawable: layer];
	glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &xsz);
	glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &ysz);

	glBindRenderbuffer(GL_RENDERBUFFER, rbuf_depth);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, xsz, ysz);

	if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
		error_log("resize failed, framebuffer incomplete\n");
		return FALSE;
	}
	assert(glGetError() == GL_NO_ERROR);

	game_reshape(xsz, ysz);
	return TRUE;
}

- (void)layoutSubviews
{
	[self resize: (CAEAGLLayer*)self.layer];
	[self drawView:nil];
}

- (NSInteger)anim_frame_interval
{
	return anim_frame_interval;
}

- (void)setAnimationFrameInterval: (NSInteger)frameInterval
{
	// Frame interval defines how many display frames must pass between each time the
	// display link fires. The display link will only fire 30 times a second when the
	// frame internal is two on a display that refreshes 60 times a second. The default
	// frame interval setting of one will fire 60 times a second when the display refreshes
	// at 60 times a second. A frame interval setting of less than one results in undefined
	// behavior.
	if(frameInterval >= 1) {
		anim_frame_interval = frameInterval;

		if(animating) {
			[self stopAnimation];
			[self startAnimation];
		}
	}
}

- (void)startAnimation
{
	if(!animating) {
		if(display_link_supported) {
			// CADisplayLink is API new to iPhone SDK 3.1. Compiling against earlier versions will result in a warning, but can be dismissed
			// if the system version runtime check for CADisplayLink exists in -initWithCoder:. The runtime check ensures this code will
			// not be called in system versions earlier than 3.1.

			display_link = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView:)];
			[display_link setFrameInterval: anim_frame_interval];
			[display_link addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
		} else {
			anim_timer = [NSTimer scheduledTimerWithTimeInterval: (NSTimeInterval)((1.0 / 60.0) * anim_frame_interval)
					target: self selector: @selector(drawView:) userInfo: nil repeats: TRUE];
		}

		animating = TRUE;
	}
}

- (void)stopAnimation
{
	if(animating) {
		if(display_link_supported) {
			[display_link invalidate];
			display_link = nil;
		} else {
			[anim_timer invalidate];
			anim_timer = nil;
		}

		animating = FALSE;
	}
}

- (void)dealloc
{
	game_cleanup();

	if(fbo) {
		glDeleteFramebuffers(1, &fbo);
		fbo = 0;
		default_fbo = 0;
	}
	if(rbuf_color) {
		glDeleteRenderbuffers(1, &rbuf_color);
		rbuf_color = 0;
	}
	if(rbuf_depth) {
		glDeleteRenderbuffers(1, &rbuf_depth);
		rbuf_depth = 0;
	}

	if([EAGLContext currentContext] == context) {
		[EAGLContext setCurrentContext: nil];
	}
	[context release];
	context = nil;

	[super dealloc];
}

#define MOUSEX(x)	((int)((x) * xsz / 320.0))
#define MOUSEY(y)	((int)((y) * ysz / 480.0))
static bool bn_pressed;

- (void)touchesBegan: (NSSet*)touches withEvent: (UIEvent*)event
{
	UITouch *touch = [[touches allObjects] objectAtIndex: 0];
	CGPoint pt = [touch locationInView: self];

	int x = MOUSEX(pt.x);
	int y = MOUSEY(pt.y);
	current_screen()->button(0, true, x, y);

	bn_pressed = true;
}

- (void)touchesMoved: (NSSet*)touches withEvent: (UIEvent*)event
{
	UITouch *touch = [[touches allObjects] objectAtIndex: 0];
	CGPoint pt = [touch locationInView: self];

	int x = MOUSEX(pt.x);
	int y = MOUSEY(pt.y);
	current_screen()->motion(x, y, bn_pressed);
}

- (void)touchesEnded: (NSSet*)touches withEvent: (UIEvent*)event
{
	UITouch *touch = [[touches allObjects] objectAtIndex: 0];
	CGPoint pt = [touch locationInView: self];

	int x = MOUSEX(pt.x);
	int y = MOUSEY(pt.y);
	current_screen()->button(0, false, x, y);
	bn_pressed = false;
}

- (void)touchesCancelled: (NSSet*)touches withEvent: (UIEvent*)event
{
	[self touchesEnded: touches withEvent: event];
}

@end
