//
//
//
//  Pong3 - A Modern Example
//
//
//  Copyright (c) 2007-2011 Knute Johnson, all rights reserved
//
//
//

package com.knutejohnson.games.pong;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.net.*;
import javax.swing.*;
import javax.sound.sampled.*;

public class Pong3 extends JPanel implements ActionListener {
    final static double TWOPI = Math.PI * 2.0;
    final static double BALL_DIAMETER = 17.0;
    final static double BALL_RADIUS = BALL_DIAMETER / 2.0;
    final static double PADDLE_WIDTH = 70.0;
    final static double PADDLE_HEIGHT = 10.0;
    final static double PIXELS_PER_TICK = 3.0;

    final javax.swing.Timer timer;

    double ballX, ballY, ballDeltaX, ballDeltaY;
    double velocity;
    double paddleX, paddleY;
    boolean leftKeyIsPressed, rightKeyIsPressed;

    final Ellipse2D.Double ball = new Ellipse2D.Double();
    final Rectangle2D.Double paddle = new Rectangle2D.Double();

    public Pong3() {
        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent ke) {
                int keyCode = ke.getKeyCode();
                if (keyCode == KeyEvent.VK_LEFT)
                    leftKeyIsPressed = true;
                else if (keyCode == KeyEvent.VK_RIGHT)
                    rightKeyIsPressed = true;
                else if (keyCode == KeyEvent.VK_SPACE) {
                    velocity = PIXELS_PER_TICK;
                    ballX = getWidth() / 2.0;
                    ballY = BALL_RADIUS;
                    ballDeltaX = 0.0;
                    ballDeltaY = velocity;
                }
            }
            public void keyReleased(KeyEvent ke) {
                int keyCode = ke.getKeyCode();
                if (keyCode == KeyEvent.VK_LEFT)
                    leftKeyIsPressed = false;
                else if (keyCode == KeyEvent.VK_RIGHT)
                    rightKeyIsPressed = false;
            }
        });

        timer = new javax.swing.Timer(16,this);
        timer.setCoalesce(false);
    }

    public void start() {
        ballX = paddleX = getWidth() / 2.0;  // center paddle
        ++paddleX;  // put paddle very slightly off center
        ballY = BALL_RADIUS;
        velocity = PIXELS_PER_TICK;

        requestFocusInWindow();
        timer.start();
    }

    public void stop() {
        timer.stop();
    }

    public void actionPerformed(ActionEvent ae) {
        calculate();
        paintImmediately(0,0,getWidth(),getHeight());
    }

    void calculate() {
        // get a little faster each time positions are calculated
        velocity += 0.007;

        // move the paddle
        if (leftKeyIsPressed && paddleX > PADDLE_WIDTH / 2.0)
            paddleX -= 8.0;

        if (rightKeyIsPressed && paddleX < getWidth() - PADDLE_WIDTH / 2.0)
            paddleX += 8.0;

        paddle.setFrame(paddleX - PADDLE_WIDTH / 2.0,getHeight() - 20.0,
         PADDLE_WIDTH,PADDLE_HEIGHT);

        // move the ball
        ballX += ballDeltaX;
        ballY += ballDeltaY;

        ball.setFrame(ballX - BALL_RADIUS,ballY - BALL_RADIUS,
         BALL_DIAMETER,BALL_DIAMETER);

        // check for edges
        if (ballX <= BALL_RADIUS || ballX + BALL_RADIUS >= getWidth()) {
            ballDeltaX = -ballDeltaX;
            playSound("sounds/bink.wav");
        }
        if (ballY < BALL_RADIUS) {
            ballDeltaY = -ballDeltaY;
            playSound("sounds/bink.wav");
        }

        // if center of ball is below paddle face
        if (ballY > getHeight() - 20) {
            ballX = getWidth() / 2.0;
            ballY = BALL_RADIUS;
            ballDeltaX = 0.0;
            ballDeltaY = 0.0;
            velocity = PIXELS_PER_TICK;
            playSound("sounds/buhh.wav");
            return;
        }

        // check for ball hits on the top edge of paddle
        if (paddle.intersects(ball.getBounds2D())) {
            ballDeltaY = -ballDeltaY;
            // add a little angle for off center hits on paddle
            double arcTan = Math.atan2(ballDeltaY,ballDeltaX);
            double angle = (ballX - paddleX) / 720.0 * TWOPI;
            ballDeltaX = velocity * Math.cos(arcTan + angle);
            ballDeltaY = velocity * Math.sin(arcTan + angle);
            playSound("sounds/bonk.wav");
        }
    }

    public void paintComponent(Graphics g2d) {
        Graphics2D g = (Graphics2D)g2d;

        // anti-alias the drawing
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);

        // draw the background
        g.setColor(Color.BLACK);
        g.fillRect(0,0,getWidth(),getHeight());

        // draw the ball and paddle
        g.setColor(Color.WHITE);
        g.fill(ball);
        g.fill(paddle);
    }

    void playSound(final String fname) {
        Runnable r = new Runnable() {
            public void run() {
                try {
                    URL url = getClass().getResource(fname);
                    AudioInputStream ais = AudioSystem.getAudioInputStream(url);
                    AudioFormat af = ais.getFormat();
                    SourceDataLine line = AudioSystem.getSourceDataLine(af);
                    line.open(af);
                    line.start();
                    byte[] buf = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = ais.read(buf,0,buf.length)) != -1)
                        line.write(buf,0,bytesRead);
                    line.drain();
                    line.stop();
                    line.close();
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        };
        new Thread(r).start();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                final Frame f = new Frame("Pong3");
                final Pong3 p3 = new Pong3();
                p3.setPreferredSize(new Dimension(600,450));

                f.addWindowListener(new WindowAdapter() {
                    public void windowOpened(WindowEvent we) {
                        p3.start();
                    }
                    public void windowClosing(WindowEvent we) {
                        p3.stop();
                        f.dispose();
                    }
                });

                f.add(p3,BorderLayout.CENTER);
                f.pack();
                f.setVisible(true);
            }
        });
    }
}


