#include "Wumbo-Header.h"
#if PLATFORM_IOS
#include "Wumbo-iOSRoot.h"
#include "Wumbo-InputManager.h"
#include "Wumbo-Renderer.h"
#include "Wumbo-Platforms.h"
#include <CoreMotion/CoreMotion.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
	self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    //self.viewController //[[TitleScreenController alloc] init];//
    self.window.rootViewController = [[ViewController alloc] init];//self.viewController;
	//[self.window addSubview:self.window.rootViewController.view];
    [self.window makeKeyAndVisible];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
	// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
	// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
	// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
	// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
	// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
	// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
	// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}



- (CMMotionManager *)motionManager
{
    if (!motionManager) motionManager = [[CMMotionManager alloc] init];
		
		return motionManager;
}

@end

/*
 *
 *
 *
 *
 *
 *
 *
 *
 */


@interface ViewController () {
	Wumbo::Program *program;
	GLuint _program;
}
@property (strong, nonatomic) EAGLContext *context;

- (void)setupGL;
- (void)tearDownGL;

- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
#if USE_VBOS
    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
#else
	self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
#endif
	
    if (!self.context) {
        NSLog(@"Failed to create ES context");
    }
    
    GLKView *view = (GLKView *)self.view;
	
	swipeRecognizerLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
	[swipeRecognizerLeft setDirection:(UISwipeGestureRecognizerDirectionLeft)];
	swipeRecognizerLeft.cancelsTouchesInView = NO;
	[view addGestureRecognizer:swipeRecognizerLeft];

	swipeRecognizerUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
	[swipeRecognizerUp setDirection:(UISwipeGestureRecognizerDirectionUp)];
	swipeRecognizerUp.cancelsTouchesInView = NO;
	[view addGestureRecognizer:swipeRecognizerUp];
	
	swipeRecognizerRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
	[swipeRecognizerRight setDirection:(UISwipeGestureRecognizerDirectionRight)];
	swipeRecognizerRight.cancelsTouchesInView = NO;
	[view addGestureRecognizer:swipeRecognizerRight];
	
	swipeRecognizerDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
	[swipeRecognizerDown setDirection:(UISwipeGestureRecognizerDirectionDown)];
	swipeRecognizerDown.cancelsTouchesInView = NO;
	[view addGestureRecognizer:swipeRecognizerDown];
	
	view.backgroundColor = [UIColor colorWithRed:1 green:1 blue:0 alpha:1];
    view.context = self.context;
	view.drawableColorFormat = GLKViewDrawableColorFormatRGB565;
    view.drawableDepthFormat = GLKViewDrawableDepthFormatNone;
    view.multipleTouchEnabled = YES;
    [self setupGL];
}

- (void)dealloc
{
    [self tearDownGL];
    
    if ([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
	// ARC WHY YOU DO THIS :C
	//[swipeRecognizerLeft dealloc];
	//[swipeRecognizerUp dealloc];
	//[swipeRecognizerRight dealloc];
	//[swipeRecognizerDown dealloc];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
	
    if ([self isViewLoaded] && ([[self view] window] == nil)) {
        self.view = nil;
        
        [self tearDownGL];
        
        if ([EAGLContext currentContext] == self.context) {
            [EAGLContext setCurrentContext:nil];
        }
        self.context = nil;
    }
	
    // Dispose of any resources that can be recreated.
}





extern Wumbo::Program *WUMBOFRAMEWORK_GETNEWPROGRAM();

- (void)setupGL
{
    [EAGLContext setCurrentContext:self.context];
	finch = [Finch alloc];
	[finch init];
	glEnable(GL_SCISSOR_TEST);
    /*glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);*/
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
	
	program = WUMBOFRAMEWORK_GETNEWPROGRAM();
	Wumbo::Platform_iOS::INITIALIZE_VALUES(program);
	program->setAppDelegate((AppDelegate*)[[UIApplication sharedApplication] delegate]);
	program->setGLKViewController(self);
	
	
	program->begin();
	toucher = (Wumbo::TouchInput_iOS*) program->getInputManager()->getTouchState();
	
	
		

	
	//program->getInputManager()->update();
	
	//program->update();
	//program->getRenderer()->resetVirtualRenderTarget();
	//program->render();
}

- (void)tearDownGL
{
    [EAGLContext setCurrentContext:self.context];
	/*if (_program) {
        glDeleteProgram(_program);
        _program = 0;
    }*/
}


- (void)update
{
	//printf("peep\n");
	
	/*program->getInputManager()->update();
	program->update();
	
	program->getRenderer()->resetVirtualRenderTarget();
	program->render();
	glClearColor(1.f, 1.f, 0.f, 1.f);
	glClear(GL_COLOR_BUFFER_BIT);*/
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
	//printf("pop\n");
	glClearColor(1.f, 1.f, 1.f, 1.f);
	glClear(GL_COLOR_BUFFER_BIT);
	
	program->getInputManager()->update();
	program->update();
	
	program->getRenderer()->resetVirtualRenderTarget();
	program->render();
	
	toucher->onEndOfFrame();
	
	//glClearColor(1.f, 1.f, 0.f, 1.f);
	//glClear(GL_COLOR_BUFFER_BIT);
}

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
	UISwipeGestureRecognizer *swiper = (UISwipeGestureRecognizer*)gestureRecognizer;
	//NSLog(@"SWIPE! %i",swiper.direction);
	if (swiper.direction == UISwipeGestureRecognizerDirectionLeft)
		toucher->addSwipe(Wumbo::TouchInput::SWIPE_LEFT);
	if (swiper.direction == UISwipeGestureRecognizerDirectionUp)
		toucher->addSwipe(Wumbo::TouchInput::SWIPE_UP);
	if (swiper.direction == UISwipeGestureRecognizerDirectionRight)
		toucher->addSwipe(Wumbo::TouchInput::SWIPE_RIGHT);
	if (swiper.direction == UISwipeGestureRecognizerDirectionDown)
		toucher->addSwipe(Wumbo::TouchInput::SWIPE_DOWN);
}

// Handles the start of a touch
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSSet *allTouches = [event allTouches];
    for(int i=0;i<[allTouches count];i++)
	{
		UITouch *touch = [[allTouches allObjects] objectAtIndex:i];
		if ([touch phase] == UITouchPhaseBegan)
			toucher->addTouch(touch);
	}
}

// Handles the continuation of a touch.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	/*NSSet *allTouches = [event allTouches];
	if ([allTouches count] > 0)
	//UITouch *herman = ;
	NSLog(@"Position: %f %f",[[[allTouches allObjects] objectAtIndex:0] locationInView:nil].x,[[[allTouches allObjects] objectAtIndex:0] locationInView:nil].y);*/
}

// Handles the end of a touch event.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSSet *allTouches = [event allTouches];
    for(int i=0;i<[allTouches count];i++)
	{
		UITouch *touch = [[allTouches allObjects] objectAtIndex:i];
        if ([touch phase] == UITouchPhaseEnded)
            toucher->removeTouch(touch);
	}
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	NSSet *allTouches = [event allTouches];
    for(int i=0;i<[allTouches count];i++)
	{
		UITouch *touch = [[allTouches allObjects] objectAtIndex:i];
        if ([touch phase] == UITouchPhaseCancelled)
            toucher->removeTouch(touch);
	}
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
	bool ret = false;
	ret |= (supportedOrientations[UIDeviceOrientationLandscapeLeft] && interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
	ret |= (supportedOrientations[UIDeviceOrientationPortrait] && interfaceOrientation == UIInterfaceOrientationPortrait);
	ret |= (supportedOrientations[UIDeviceOrientationLandscapeRight] && interfaceOrientation == UIInterfaceOrientationLandscapeRight);
	ret |= (supportedOrientations[UIDeviceOrientationPortraitUpsideDown] && interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
	return ret;
    //return (interfaceOrientation == UIInterfaceOrientationLandscapeUp || interfaceOrientation == UIInterfaceOrientationLandscapeUp || interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

- (void)addSupportedOrientation:(UIDeviceOrientation) orient
{
	if (orient > 0 && orient <= 4)
		supportedOrientations[orient] = YES;
}
- (void)removeSupportedOrientation:(UIDeviceOrientation) orient
{
	if (orient > 0 && orient <= 4)
		supportedOrientations[orient] = NO;
}

#pragma mark -  OpenGL ES 2 shader compilation

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;
    
    // Create shader program.
    _program = glCreateProgram();
    
    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }
    
    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
	//NSLog(@"%s\n",[fragShaderPathname UTF8String]);
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }
    
    // Attach vertex shader to program.
    glAttachShader(_program, vertShader);
    
    // Attach fragment shader to program.
    glAttachShader(_program, fragShader);
    
    // Bind attribute locations.
    // This needs to be done prior to linking.
    glBindAttribLocation(_program, GLKVertexAttribPosition, "position");
	glBindAttribLocation(_program, GLKVertexAttribColor, "color");
	glBindAttribLocation(_program, GLKVertexAttribTexCoord0, "texcoord0");
	
	//glGetUniformLocation(_program, "lolo");
    //glBindAttribLocation(_program, GLKVertexAttribNormal, "normal");
    
    // Link program.
    if (![self linkProgram:_program]) {
        NSLog(@"Failed to link program: %d", _program);
        
        if (vertShader) {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader) {
            glDeleteShader(fragShader);
            fragShader = 0;
		}
        if (_program) {
            glDeleteProgram(_program);
            _program = 0;
        }
        
        return NO;
    }
    
    // Get uniform locations.
    //uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
    //uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(_program, "normalMatrix");
    
    // Release vertex and fragment shaders.
    if (vertShader) {
        glDetachShader(_program, vertShader);
		glDeleteShader(vertShader);
	}
    if (fragShader) {
        glDetachShader(_program, fragShader);
        glDeleteShader(fragShader);
    }
    NSLog(@"Shader success");
    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
    GLint status;
    const GLchar *source;
    
    source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String];
    if (!source) {
        NSLog(@"Failed to load vertex shader");
        return NO;
    }
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif
    
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
	
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }
    
    return YES;
}

- (BOOL)linkProgram:(GLuint)prog
{
    GLint status;
    glLinkProgram(prog);
    
#if defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
    }
#endif
    
    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0) {
        return NO;
    }
    
    return YES;
}

- (BOOL)validateProgram:(GLuint)prog
{
    GLint logLength, status;
    
    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }
    
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        return NO;
    }
	return YES;
}
@end
#endif