//
//  PaddlesViewController.m
//  Paddles
//
//  Created by Górczyński Robert on 12/01/2012.
//

#import "PaddlesViewController.h"
#import "PaddlesAppDelegate.h"

#define MAX_SCORE 3
#define SOUND_WALL   0
#define SOUND_PADDLE 1
#define SOUND_SCORE  2

#define MAX_SPEED 15

struct CGRect gPlayerBox[] = 
{
    //  x,  y       szerokość, wysokość
    {   40, 40,     320-80,    240-40-32 }, // prostokąt gracza 1
    {   40, 240+33, 320-80,    240-40-32 }  // prostokąt gracza 2
};

// Krążek znajduje się w poniższym prostokącie:
struct CGRect gPuckBox = 
{ // x, y         width, height
    28, 28,      320-56, 480-56
};
// Prostokąty pól bramkowych, do których może się wśliznąć krążek:
struct CGRect gGoalBox[] =
{
    {   102, -20,   116, 49   }, // Pole wygranej pierwszego gracza.
    {   102, 451,   116, 49   }  // Pole wygranej drugiego gracza.
};


@implementation PaddlesViewController
@synthesize viewPaddle1;
@synthesize viewPaddle2;
@synthesize viewPuck;
@synthesize viewScore1;
@synthesize viewScore2;
@synthesize debug;
@synthesize computer;

// Wczytanie efektu dźwiękowego do tablicy dźwięków.
- (void)loadSound:(NSString*)name Slot:(int)slot
{
    if (sounds[slot] != 0) return;
    
    // Utworzenie ścieżki dostępu do pliku dźwięku.
    NSString *sndPath = [[NSBundle mainBundle] pathForResource:name 
                                                        ofType:@"wav" 
                                                   inDirectory:@"/"];
    
    // Utworzenie identyfikatora dla dźwięku znajdującego się w slocie.
    AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:sndPath], &sounds[slot]);
}

- (void)initSounds
{
    [self loadSound:@"wall" Slot:SOUND_WALL];
    [self loadSound:@"paddle" Slot:SOUND_PADDLE];
    [self loadSound:@"score" Slot:SOUND_SCORE];
}

- (void)playSound:(int)slot
{
    AudioServicesPlaySystemSound(sounds[slot]);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Usunięcie wszelkich buforowanych danych, obrazów itd., które nie są obecnie używane.
}

#pragma mark - View lifecycle
- (BOOL)canBecomeFirstResponder
{ 
    return YES; 
}

- (void)viewDidUnload
{
    // Usunięcie obiektów Paddle.
    [paddle1 release];
    [paddle2 release];
    [puck release];
    
    [self setViewPaddle1:nil];
    [self setViewPaddle2:nil];
    [self setViewPuck:nil];
    [self setViewScore1:nil];
    [self setViewScore2:nil];
    [self setDebug:nil];
    [super viewDidUnload];
    // Usunięcie wszystkich "przytrzymanych" podwidoków widoku głównego,
    // na przykład self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self becomeFirstResponder];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self resignFirstResponder];
	[super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
	[super viewDidDisappear:animated];
}

/*
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Zwrot wartości YES dla obsługiwanych układów.
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
*/

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Iteracja przez elementy dotknięcia.
    for (UITouch *touch in touches)
    {
        // Pobranie miejsca dotknięcia palcem w widoku.
        CGPoint touchPoint = [touch locationInView:self.view];
        
        // Sprawdzenie, która połowa ekranu została dotknięta i przypisanie
        // dotknięcia do określonej paletki, jeśli nie zostało jeszcze przypisane.
        if (paddle1.touch == nil && touchPoint.y < 240 && computer == 0) 
        {
            touchPoint.y += 48;
            paddle1.touch = touch;
            [paddle1 move:touchPoint];
        }
        else if (paddle2.touch == nil)
        {
            touchPoint.y -= 32;
            paddle2.touch = touch;
            [paddle2 move:touchPoint];
        }
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Iteracja przez elementy dotknięcia.
    for (UITouch *touch in touches)
    {
        // Pobranie miejsca dotknięcia palcem w widoku.
        CGPoint touchPoint = [touch locationInView:self.view];
        
        // Jeżeli dotknięcie jest przypisane paletce, zmieniamy położenie paletki.
        if (paddle1.touch == touch) 
        {
            touchPoint.y += 48;
            [paddle1 move:touchPoint];
        }
        else if (paddle2.touch == touch)
        {
            touchPoint.y -= 32;
            [paddle2 move:touchPoint];
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Iteracja przez elementy dotknięcia.
    for (UITouch *touch in touches)
    {
        if (paddle1.touch == touch) paddle1.touch = nil;
        else if (paddle2.touch == touch) paddle2.touch = nil;
    }
}

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

- (void)reset
{
    // Wyzerowanie stanu gracza-komputera.
    state = 0;
    
    // Wyzerowanie paletek i krążka.
    [paddle1 reset];
    [paddle2 reset];
    [puck reset];
}

- (void)start
{
    if (timer == nil)
    {
        // Utworzenie stopera animacji.
        timer = [[NSTimer scheduledTimerWithTimeInterval:1.0/60.0
                                                  target:self 
                                                selector:@selector(animate) 
                                                userInfo:NULL 
                                                 repeats:YES] retain];
    }
    
    // Wyświetlenie piłeczki.
    viewPuck.hidden = NO;
}

- (void)stop
{
    if (timer != nil)
    {
        [timer invalidate];
        [timer release];
        timer = nil;
    }
    
    // Ukrycie piłeczki.
    viewPuck.hidden = YES;
}

- (void)displayMessage:(NSString*) msg
{
    // Jednocześnie może być wyświetlony tylko jeden komunikat.
    if (alert) return;
    
    // Wstrzymanie stopera animacji.
    [self stop];
    
    // Utworzenie i wyświetlenie okna komunikatu.
    alert = [[UIAlertView alloc] initWithTitle:@"Gra" 
                                       message:msg
                                      delegate:self 
                             cancelButtonTitle:@"OK" 
                             otherButtonTitles:nil];
    [alert show];
    [alert release];
}

- (void)newGame
{
    [self reset];
    
    // Wyzerowanie punktacji.
    viewScore1.text = [NSString stringWithString:@"0"];
    viewScore2.text = [NSString stringWithString:@"0"];
    
    // Wyświetlenie komunikatu pozwalającego na rozpoczęcie gry.
    [self displayMessage:@"Gotowi do gry?"];
}

- (int)gameOver
{
    if ([viewScore1.text intValue] >= MAX_SCORE) return 1;
    if ([viewScore2.text intValue] >= MAX_SCORE) return 2;
    return 0;
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    // Okno zostało zamknięte więc można wyzerować grę i rozpocząć odtwarzanie animacji.
    alert = nil;
    
    // Sprawdzenie czy należy rozpocząć nową grę?
    if ([self gameOver])
    {
        PaddlesAppDelegate *app = (PaddlesAppDelegate*)[UIApplication sharedApplication].delegate;
        [app showTitle];
        return;
    }
    
    // Wyzerowanie rundy.
    [self reset];
    
    // Rozpoczęcie animacji.
    [self start];
}

- (BOOL)checkGoal
{
    // Sprawdzenie, czy piłeczka wykroczyła poza ekran. Jeśli tak, zerujemy rundę.
    if (puck.winner != 0) 
    {
        // Pobranie wartości liczbowej z etykiet punktacji.
        int s1 = [viewScore1.text intValue];
        int s2 = [viewScore2.text intValue];
        
        // Przydzielenie punktu odpowiedniemu graczowi.
        if (puck.winner == 2) ++s2; else ++s1;
        
        // Uaktualnienie etykiet punktacji.
        viewScore1.text = [NSString stringWithFormat:@"%u", s1];
        viewScore2.text = [NSString stringWithFormat:@"%u", s2];
        
        // Sprawdzenie zwycięzcy.
        if ([self gameOver] == 1)
        {
            // Ogłoszenie zwycięzcy.
            [self displayMessage:@"Wygrał gracz numer 1!"];
        }
        else if ([self gameOver] == 2)
        {
            // Ogłoszenie zwycięzcy.
            [self displayMessage:@"Wygrał gracz numer 2!"];
        }
        else
        {
            // Wyzerowanie rundy.
            [self reset];
        }
        
        // Zwrócenie wartości TRUE w przypadku zdobycia punktu.
        return TRUE;
    }
    
    // Brak punktu.
    return FALSE;
}

- (void)computerAI
{
    if (state == AI_START)
    {
        debug.text = @"START";
        
        if (paddle2.speed > 0 || (arc4random() % (100/computer)) == 1)
        {
            state = AI_WAIT;
        }
    }
    else if (state == AI_WAIT)
    {
        // Rozwiązanie problemu zakleszczenia krążka w rogu stołu do gry.
        if ([paddle1 intersects:viewPuck.frame])
        {
            // Przejście do stanu znudzenia, aby paletka została umieszczona w losowo wybranym położeniu.
            state = AI_BORED;
            return;
        }
        
        // Zaczekaj aż do zatrzymania paletki.
        if (paddle1.speed == 0)
        {
            debug.text = @"WAIT";
            
            paddle1.maxSpeed = MAX_SPEED;
            
            // Wybierz losową liczbę z zakresu od 0 do 9.
            int r = arc4random() % ((4 - computer) *4);
            
            // Jeżeli wybrana została liczba 1 wówczas następuje przejście do nowego stanu.
            if (r == 1)
            {
                // Jeżeli krążek znajduje się na naszej połowie i nie porusza zbyt szybko wówczas przejdź do stanu ataku.
                // Jeżeli krążek porusza się do góry ze znaczną prędkością, przejdź do stanu obrony.
                // W przeciwnym razie przejdź do stanu znudzony.
                if (puck.center.y <= 240 && puck.speed < computer)
                {
                    if (computer == 1) state = AI_OFFENSE2;
                    else state = AI_OFFENSE;
                }

                if (puck.speed >= 1 && puck.dy < 0)
                {
                    state = AI_DEFENSE;
                }
                else
                {
                    state = AI_BORED;
                }
            }
        }
    }
    else if (state == AI_OFFENSE)
    {
        debug.text = @"OFFENSE";
        
        if (computer < 3) paddle1.maxSpeed = MAX_SPEED / 2;
        
        // Wybór nowego położenia X w odległości od -64 do + 64 od centrum krążka.
        float x = puck.center.x - 64 + (arc4random() % 129);
        float y = puck.center.y - 64 - (arc4random() % 64);
        [paddle1 move:CGPointMake(x,y)];
        state = AI_OFFENSE2;
    }
    else if (state == AI_OFFENSE2)
    {
        debug.text = @"OFFENSE2";
        
        if (paddle1.speed == 0)
        {
            if (computer == 1) paddle1.maxSpeed = MAX_SPEED / 2;
            else if (computer == 2) paddle1.maxSpeed = MAX_SPEED * 3 / 4;
            
            // Uderzenie krążka.
            [paddle1 move:puck.center];
            state = AI_WAIT;
        }
    }
    else if (state == AI_DEFENSE)
    {
        debug.text = @"DEFENSE";
        
        // Przeniesienie krążka do pozycji X i zmniejszenie odległości od bramki.
        float offset = ((puck.center.x - 160.0) / 160.0) * 40.0;
        [paddle1 move:CGPointMake(puck.center.x - offset, puck.center.y / 2)];
        if (puck.speed < 1 || puck.dy > 0) 
        {
            state = AI_WAIT;
        }
        if (computer == 1)
        {
            paddle1.maxSpeed = MAX_SPEED / 3;
        }
        else if (computer == 2)
        {
            paddle1.maxSpeed = MAX_SPEED * 2/5;
        }
        else if (computer == 3)
        {
            paddle1.maxSpeed = MAX_SPEED / 2;
        }
    }
    // Komputer jest znudzony i przesuwa paletkę do losowo wybranego położenia.
    else if (state == AI_BORED)
    {
        if (paddle1.speed == 0)
        {
            debug.text = @"BORED";
            
            // Zmiana prędkości paletki na podstawie poziomu trudności.
            paddle1.maxSpeed = 3 + computer;
            
            // Zmiana wielkości obszaru: (20) na poziomie średnik i (40) na trudnym.
            int inset = (computer - 1) * 20;
            
            // Przesunięcie paletki paddle1 do losowo wybranego położenia na połowie gracza 1.
            float x = (gPlayerBox[0].origin.x + inset) + arc4random() % (int)(gPlayerBox[0].size.width - inset*2);
            float y = gPlayerBox[0].origin.y + arc4random() % (int)(gPlayerBox[0].size.height - inset);
            [paddle1 move:CGPointMake(x,y)];
            state = AI_WAIT;
        }
    }
}


// Animacja krążka i wykrywanie kolizji.
- (void) animate
{
    // Sprawdzenie, czy w grze uczestniczy komputer.
    if (computer) 
    {
        [self computerAI];
    }
    
    // Przesunięcie paletek.
    [paddle1 animate];
    [paddle2 animate];
    
    // Obsługa kolizji z paletkami, kolizja powoduje zwrot wartości true.
    if ([puck handleCollision:paddle1] || [puck handleCollision:paddle2])
    {
        // Odtworzenie dźwięku zderzenia z paletką.
        [self playSound:SOUND_PADDLE];
    }
    
    // Animacja krążka, uderzenie w ścianę zwraca wartość true.
    if ([puck animate])
    {
        [self playSound:SOUND_WALL];
    }
    
    // Sprawdzenie, czy gracz zdobył gola.
    if ([self checkGoal])
    {
        [self playSound:SOUND_SCORE];
    }
}

- (void)pause
{
    [self stop];
}

- (void)resume
{
    // Wyświetlenie komunikatu z pytaniem o wznowienie gry.
    [self displayMessage:@"Gra jest wstrzymana"];
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event 
{
    if (event.type == UIEventSubtypeMotionShake) 
    {
        // Wstrzymanie gry, a następnie jej wznowienie w celu wyświetlenia komunikatu.
        [self pause];
        [self resume];
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self initSounds];
    
    /*
     // Kod wyświetlający prostokąty, w których mogą poruszać się paletki graczy.
     for (int i = 0; i < 2; ++i)
     {
     UIView *view = [[UIView alloc] initWithFrame:gPlayerBox[i]];
     view.backgroundColor = [UIColor redColor];
     view.alpha = 0.25;
     [self.view addSubview:view];
     [view release];
     }
     
     // Kod rysujący prostokąty ograniczające w polach bramkowych.
     for (int i = 0; i < 2; ++i)
     {
     UIView *view = [[UIView alloc] initWithFrame:gGoalBox[i]];
     view.backgroundColor = [UIColor greenColor];
     view.alpha = 0.25;
     [self.view addSubview:view];
     [view release];
     }
     
     // Kod rysujący prostokąt ograniczający dla krążka.
     UIView *view = [[UIView alloc] initWithFrame:gPuckBox];
     view.backgroundColor = [UIColor grayColor];
     view.alpha = 0.25;
     [self.view addSubview:view];
     [view release];
     */
    
    debug.hidden = NO;
    
    // Utworzenie obiektów Paddle i Puck.
    paddle1 = [[Paddle alloc] initWithView:viewPaddle1 
                                  Boundary:gPlayerBox[0] 
                                  MaxSpeed:MAX_SPEED];
    paddle2 = [[Paddle alloc] initWithView:viewPaddle2 
                                  Boundary:gPlayerBox[1] 
                                  MaxSpeed:MAX_SPEED];
    puck = [[Puck alloc] initWithPuck:viewPuck 
                             Boundary:gPuckBox 
                                Goal1:gGoalBox[0] 
                                Goal2:gGoalBox[1] 
                             MaxSpeed:MAX_SPEED];
    
    [self newGame];
}

- (void)dealloc {
    // Usunięcie dźwięków.
    for (int i = 0; i < 3; ++i) 
    {
        AudioServicesDisposeSystemSoundID(sounds[i]);
    }
    
    // Usunięcie obiektów Paddle.
    [paddle1 release];
    [paddle2 release];
    [puck release];
    
    [viewPaddle1 release];
    [viewPaddle2 release];
    [viewPuck release];
    [viewScore1 release];
    [viewScore2 release];
    [debug release];
    [super dealloc];
}
@end
