//
//  JumpGameMode.m
//  Cart Collect
//
//  Created by Starla Insigna on 8/17/11.
//  Copyright 2011 Four Island. All rights reserved.
//

#import "JumpGameMode.h"
#import "SimpleAudioEngine.h"
#import "FallingObject.h"
#import "Cherry.h"
#import "Bottle.h"
#import "OneUp.h"
#import "Rock.h"
#import "GameOverScene.h"
#import "PointMultiplier.h"

#define kMinimumGestureLength  25

@interface LedgeFactory : NSObject {
    UIImage* leftSprite;
    UIImage* midSprite;
    UIImage* rightSprite;
    UIImage* singleSprite;
}

- (id)init;
- (UIImage*)createLedgeWithWidth:(int)width height:(int)height;

@end

@implementation FallingObject (Flags)

- (BOOL)fellDuringWave
{
    return flags[0];
}

- (void)setFellDuringWave:(BOOL)value
{
    flags[0] = value;
}

@end

@implementation JumpGameMode

- (id)init
{
    self = [super init];
    
    if (nil != self)
    {
        CCSprite* backgroundImage = [CCSprite spriteWithFile:@"SeaBeach.png"];
		backgroundImage.position = ccp(240, 160);
		[self addChild:backgroundImage z:-1];
        
        water = [CCSprite spriteWithFile:@"water.png"];
        water.position = ccp(240, -60);
        [self addChild:water];
        
        cart.sprite.position = ccp(120, 22+64); //86
        cart.falling = YES;
        cart.delegate = self;
        
        self.isTouchEnabled = YES;
        
        waterTick = 0;
        wave = NO;
        
        factory = [[LedgeFactory alloc] init];
        ledges = [[NSMutableSet alloc] init];
        CCTexture2D* texture = [[CCTexture2D alloc] initWithImage:[factory createLedgeWithWidth:6 height:2]];
        CCSprite* ledge = [CCSprite spriteWithTexture:texture];
        ledge.position = ccp(80, 32);
        [self addChild:ledge];
        [ledges addObject:ledge];
        [texture release];
        
        CCSprite* firstLedge = ledge;
        texture = [[CCTexture2D alloc] initWithImage:[factory createLedgeWithWidth:10 height:2]];
        ledge = [CCSprite spriteWithTexture:texture];
        ledge.position = ccp(80 + firstLedge.boundingBox.size.width/2 + ledge.boundingBox.size.width/2+64, 32);
        [self addChild:ledge];
        [ledges addObject:ledge];
        [texture release];

        ledgeScrollSpeed = 0;
        ledgeAccelerationRate = 20.0f;
        addSpeed = 2.5f;
    }
    
    return self;
}

- (void)onEnterTransitionDidFinish
{
    [super onEnterTransitionDidFinish];

    [self schedule:@selector(accelerateGame) interval:ledgeAccelerationRate];
    [self schedule:@selector(randomlyAddObject:) interval:addSpeed];
    [self schedule:@selector(incrementScore) interval:1.0f];
    
    [self scheduleDelayedAction:^{
        wave = YES;
    } delay:60.0f];
}

- (void)tick:(ccTime)dt
{
    NSMutableSet* discardedSet = [NSMutableSet set];
    int rightmost = 0;
    int rightwidth = 0;
    
    for (CCSprite* sprite in ledges)
    {
        sprite.position = ccp(sprite.position.x - ledgeScrollSpeed, sprite.position.y);
        
        if ((sprite.position.x + sprite.boundingBox.size.width/2) < 0)
        {
            [discardedSet addObject:sprite];
            [self removeChild:sprite cleanup:YES];
        }
        
        if (sprite.position.x > rightmost)
        {
            rightmost = sprite.position.x;
            rightwidth = sprite.boundingBox.size.width/2;
        }
    }
    
    [ledges minusSet:discardedSet];
    
    NSMutableSet* discardedObjects = [NSMutableSet set];
    for (FallingObject* object in objects)
    {
        if (object.sprite.position.y < (64+object.sprite.boundingBox.size.height/2))
        {
            for (CCSprite* ledge in ledges)
            {
                CGSize first = [object.sprite boundingBox].size;
                CGSize second = [ledge boundingBox].size;
                
                if (object.sprite.position.x > (ledge.position.x - second.width/2 - first.width/2))
                {
                    if (object.sprite.position.x < (ledge.position.x + second.width/2 + first.width/2))
                    {
                        if (object.sprite.position.y > (ledge.position.y - second.height/2 - first.height/2))
                        {
                            if (object.sprite.position.y < (ledge.position.y + second.height/2 + first.height/2))
                            {
                                [discardedObjects addObject:object];
                            }
                        }
                    }
                }
            }
        }
        
        if (object.fellDuringWave)
        {
            object.sprite.position = ccp(object.sprite.position.x, MAX(object.sprite.position.y, water.position.y+80+11));
        } else {
            object.sprite.position = ccp(object.sprite.position.x-ledgeScrollSpeed, object.sprite.position.y);
        }
    }
    
    [objects minusSet:discardedObjects];
    
    if (rightmost <= 480)
    {
        int ledgeWidth = arc4random() % 9 + 1;
        int ledgeDistance = arc4random() % (ledgeScrollSpeed*ledgeScrollSpeed+1)*3/2;
        CCTexture2D* texture = [[CCTexture2D alloc] initWithImage:[factory createLedgeWithWidth:ledgeWidth height:2]];
        CCSprite* ledge = [CCSprite spriteWithTexture:texture];
        ledge.position = ccp(rightmost + rightwidth + ledge.boundingBox.size.width/2+64+ledgeDistance, 32);
        [self addChild:ledge];
        [ledges addObject:ledge];
        [texture release];
    }
    
    if ([self cartIsObstructed:cart])
    {
        cart.sprite.position = ccp(cart.sprite.position.x-ledgeScrollSpeed, cart.sprite.position.y);
    }
    
    [super tick:dt];
    
    if ((cart.sprite.position.y < 86) && (cart.boundedByScreen))
    {
        cart.boundedByScreen = NO;
    } else if ((cart.sprite.position.y >= 86) && (!cart.boundedByScreen))
    {
        cart.boundedByScreen = YES;
    }
    
    if (cart.sprite.position.y == (0-cart.sprite.boundingBox.size.height/2))
    {
        [self setLives:self.lives-1];
        cart.boundedByScreen = YES;
        
        [[SimpleAudioEngine sharedEngine] playEffect:[[NSBundle mainBundle] pathForResource:@"Damage1" ofType:@"wav"]];
        
        if (cart.sprite.position.x < cart.sprite.boundingBox.size.width/2)
        {
            cart.sprite.position = ccp(cart.sprite.boundingBox.size.width, 320 + cart.sprite.boundingBox.size.height/2);
        } else {
            cart.sprite.position = ccp(cart.sprite.position.x, 320 + cart.sprite.boundingBox.size.height/2);
        }
    }
    
    if (lives <= 0)
	{
        [self unscheduleAllSelectors];
        
        [[CCDirector sharedDirector] replaceScene:[CCTransitionSlideInT transitionWithDuration:1.5f scene:[GameOverScene sceneWithScore:score gameMode:@"Jump"]]];
	}
    
    if (wave)
    {
        waterTick++;
        
        water.position = ccp(240, 140 * sin(waterTick / (36 * M_PI)) - 60);
        
        if (waterTick == 180)
        {
            wave = NO;
            
            [self scheduleDelayedAction:^{
                wave = YES;
            } delay:10.0f];
        } else if (waterTick == 360)
        {
            wave = NO;
            waterTick = 0;
            
            [self scheduleDelayedAction:^{
                wave = YES;
            } delay:60.0f];
        }
    }
    
    if (jump)
    {
        jumpTick++;
        
        cart.sprite.position = ccp(cart.sprite.position.x, MAX(100 * sin(jumpTick / (2 * M_PI)) + 86, water.position.y+80+11));
        
        if (jumpTick == 20)
        {
            jump = NO;
            jumpTick = 0;
            cart.falling = YES;
        }
    }
}

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ((!isPaused) && (!isGesturing))
    {
        UITouch* touch = [touches anyObject];
        gestureStartPoint = [touch locationInView:nil];
        isGesturing = YES;
    }
}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (isGesturing)
    {
        UITouch* touch = [touches anyObject];
        CGPoint gestureCurrentPosition = [touch locationInView:nil];
        CGFloat angle = atan2f(gestureCurrentPosition.y - gestureStartPoint.y, gestureCurrentPosition.x - gestureStartPoint.x);
        CGFloat distance = sqrt(powf((gestureCurrentPosition.x - gestureStartPoint.x),2) + powf((gestureCurrentPosition.y - gestureStartPoint.y),2));
        
        if ((distance >= kMinimumGestureLength) && (angle >= expectedAngle - M_PI_4) && (angle <= expectedAngle + M_PI_4) && ((cart.sprite.position.y >= 80) && (cart.sprite.position.y <= 90)))
        {
            jump = YES;
            cart.falling = NO;
        }
    }
}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    isGesturing = NO;
}

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    [super accelerometer:accelerometer didAccelerate:acceleration];
    
    expectedAngle = acceleration.y*M_PI_2;
}

- (int)cartShouldFall:(Cart *)m_cart
{
    int bottom = 0-m_cart.sprite.boundingBox.size.height/2;
    
    for (CCSprite* sprite in ledges)
    {
        CGSize first = [m_cart.sprite boundingBox].size;
        CGSize second = [sprite boundingBox].size;
        
        if (m_cart.sprite.position.x > (sprite.position.x - second.width/2 - first.width/2))
        {
            if (m_cart.sprite.position.x < (sprite.position.x + second.width/2 + first.width/2))
            {
                bottom = sprite.position.y + second.height/2 + first.height/2;
                break;
            }
        }
    }
    
    if (waterTick > 0)
    {
        bottom = MAX(water.position.y+80+11, bottom);
    }
    
    return bottom;
}

- (BOOL)cartIsObstructed:(Cart *)m_cart
{
    for (CCSprite* sprite in ledges)
    {
        CGSize first = [m_cart.sprite boundingBox].size;
        CGSize second = [sprite boundingBox].size;
        
        if (m_cart.sprite.position.x > (sprite.position.x - second.width/2 - first.width/2))
        {
            if (m_cart.sprite.position.x < (sprite.position.x + second.width/2 + first.width/2))
            {
                if (m_cart.sprite.position.y > (sprite.position.y - second.height/2 - first.height/2))
                {
                    if (m_cart.sprite.position.y < (sprite.position.y + second.height/2 + first.height/2))
                    {
                        return YES;
                    }
                }
            }
        }
    }
    
    return NO;
}

- (void)accelerateGame
{
    [self unschedule:@selector(accelerateGame)];
    [self unschedule:@selector(randomlyAddObject:)];
    
    ledgeScrollSpeed += 2;
    ledgeAccelerationRate *= 2;
    addSpeed *= 0.8f;
    
    [self schedule:@selector(randomlyAddObject:) interval:addSpeed];
    [self schedule:@selector(accelerateGame) interval:ledgeAccelerationRate];
}

- (void)randomlyAddObject:(ccTime)dt
{
	FallingObject* object;
    
    int randomval = arc4random()%100;
    int pmChance = (5-pointMultiplier)*5 + 30;
    
    if (randomval < 30)
    {
        object = [[OneUp alloc] init];
    } else if (randomval < pmChance)
    {
        object = [[PointMultiplier alloc] init];
    } else {
        object = [[Rock alloc] init];
    }
	
	int objectX;
    
    CGSize first = [cart.sprite boundingBox].size;
    CGSize second = [object.sprite boundingBox].size;
    
    for (;;)
    {
        objectX = arc4random()%448+16;
        
        if (waterTick == 0)
        {
            break;
        }
        
        if (cart.sprite.position.x > (objectX - second.width/2 - first.width/2))
        {
            if (cart.sprite.position.x < (objectX + second.width/2 + first.width/2))
            {
                continue;
            }
        }
        
        break;
    }
    
	object.sprite.position = ccp(objectX, 360);
	object.sprite.scale = 1;
    object.fellDuringWave = waterTick > 0;
	[self addChild:object.sprite];
	
	[objects addObject:object];
    [object release];
	
	if (score >= 240)
	{
		if (arc4random() % 100 > 80)
		{
			object = [[Rock alloc] init];
			
			for (;;)
            {
                objectX = arc4random()%448+16;
                
                if (waterTick == 0)
                {
                    break;
                }
                
                if (cart.sprite.position.x > (objectX - second.width/2 - first.width/2))
                {
                    if (cart.sprite.position.x < (objectX + second.width/2 + first.width/2))
                    {
                        continue;
                    }
                }
                
                break;
            }
            
			object.sprite.position = ccp(objectX, 360);
			object.sprite.scale = 1;
            object.fellDuringWave = waterTick > 0;
			[self addChild:object.sprite];
			
			[objects addObject:object];
            [object release];
		}
	}
	
	if (score >= 480)
	{
		if (arc4random() % 100 > 80)
		{
			object = [[Rock alloc] init];
			
			for (;;)
            {
                objectX = arc4random()%448+16;
                
                if (waterTick == 0)
                {
                    break;
                }
                
                if (cart.sprite.position.x > (objectX - second.width/2 - first.width/2))
                {
                    if (cart.sprite.position.x < (objectX + second.width/2 + first.width/2))
                    {
                        continue;
                    }
                }
                
                break;
            }
            
			object.sprite.position = ccp(objectX, 360);
			object.sprite.scale = 1;
            object.fellDuringWave = waterTick > 0;
			[self addChild:object.sprite];
			
			[objects addObject:object];
            [object release];
		}
	}
}

- (void)incrementScore
{
    [self setScore:self.score+pointMultiplier];
}

- (void)setLives:(int)m_lives
{
    int oldLives = lives;
    
    [super setLives:m_lives];
    
    if (oldLives > lives)
    {
        [self setPointMultiplier:1];
    }
}

@end

@implementation LedgeFactory

- (id)init
{
    self = [super init];
    
    CGImageRef framestuff = [[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ledge" ofType:@"png"]] CGImage];
    CGImageRef leftRef = CGImageCreateWithImageInRect(framestuff, CGRectMake(0, 0, 32, 32));
    CGImageRef midRef = CGImageCreateWithImageInRect(framestuff, CGRectMake(32, 0, 32, 32));
    CGImageRef rightRef = CGImageCreateWithImageInRect(framestuff, CGRectMake(64, 0, 32, 32));
    CGImageRef singleRef = CGImageCreateWithImageInRect(framestuff, CGRectMake(96, 0, 32, 32));
    leftSprite = [[UIImage alloc] initWithCGImage:leftRef];
    midSprite = [[UIImage alloc] initWithCGImage:midRef];
    rightSprite = [[UIImage alloc] initWithCGImage:rightRef];
    singleSprite = [[UIImage alloc] initWithCGImage:singleRef];
    CGImageRelease(leftRef);
    CGImageRelease(midRef);
    CGImageRelease(rightRef);
    CGImageRelease(singleRef);
    
    return self;
}

- (UIImage*)createLedgeWithWidth:(int)width height:(int)height
{
    NSAssert(width > 0, @"Ledge width must be greater than 0");
    NSAssert(height > 0, @"Ledge height must be greater than 0");
    
    UIGraphicsBeginImageContext(CGSizeMake(width*32, height*32));
    
    for (int y=0; y<height; y++)
    {
        if (width == 1)
        {
            [singleSprite drawInRect:CGRectMake(0, y*32, 32, 32)];
        } else {
            [leftSprite drawInRect:CGRectMake(0, y*32, 32, 32)];
            
            for (int x=1; x<(width-1); x++)
            {
                [midSprite drawInRect:CGRectMake(x*32, y*32, 32, 32)];
            }
            
            [rightSprite drawInRect:CGRectMake((width-1)*32, y*32, 32, 32)];
        }
    }
    
    UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return result;
}

@end