/**
 * Pong
 * Copyright (C) 2009 Paul Carduner
 * http://www.carduner.net/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @class HasDimension
 */
function HasDimension(){}
HasDimension.prototype.getWidth = function(){ return this.el.width(); };
HasDimension.prototype.getHeight = function(){ return this.el.height(); };

/**
 * @class HasPosition
 */
function HasPosition(){}
HasPosition.prototype = new HasDimension();
HasPosition.prototype.getX = function(){
  return this.x;
};
HasPosition.prototype.setX = function(newX){
  this.x = newX;
  this.el.css("left", Math.floor(newX));
};
HasPosition.prototype.getY = function(){
  return this.y;
};
HasPosition.prototype.setY = function(newY){
  this.y = newY;
  this.el.css("top", Math.floor(newY));
};

/**
 * @class Ball
 */
function Ball(el, x, y, vX, vY){
  this.el = $(el);
  this.setX(x || 0);
  this.setY(y || 0);
  this.vX = vX || 0;
  this.vY = vY || 0;
  this.timer = null;
}
Ball.prototype = new HasPosition();

Ball.prototype.move = function(dt){
  this.setX( this.vX*dt+this.getX() );
  this.setY( this.vY*dt+this.getY() );
};

Ball.prototype.start = function(dt, callback, scope){
  if (!callback.call(scope))
    return;
  this.move(dt);
  var ball = this;
  this.timer = window.setTimeout(
    function(){
      ball.start(dt, callback, scope);
    },
    dt);
};

Ball.prototype.stop = function(dt){
  if (this.timer !== null){
    window.clearTimeout(this.timer);
    this.timer = null;
  }
};

/**
 * @class Paddle
 */
function Paddle(el, y){
  this.el = $(el);
  this.setY(y);
}
Paddle.prototype = new HasPosition();


/**
 * @class Board
 */
function Board(boardEl, ballEl, leftPaddleEl, rightPaddleEl, pointsEl){
  this.el = $(boardEl);
  this.pointsEl = $(pointsEl);
  this.setPoints(0);
  this.ball = new Ball(ballEl, 150,150,.1,.1);
  this.leftPaddle = new Paddle(leftPaddleEl, 80);
  this.rightPaddle = new Paddle(rightPaddleEl, 150);
  this.started = false;
  this.el.bind("click", {board:this}, this.handleClick);
  this.el.bind("mousemove", {board:this}, this.handleMouseMove);

}

Board.prototype = new HasDimension();

Board.prototype.handleClick = function(event){
  if (!event.data.board.started)
    event.data.board.start();
  else
    event.data.board.stop();
};
Board.prototype.handleMouseMove = function(event){
  var board = event.data.board;
  if (!board.started)
    return;
  var offset = board.el.offset();
  var x = event.pageX-offset.left;
  var y = event.pageY-offset.top;
  var newY = y-board.rightPaddle.getHeight()/2;
  var minY = 0;
  var maxY = board.getHeight()-board.rightPaddle.getHeight()-4;
  if (newY < minY)
    newY = minY;
  else if (newY > maxY)
    newY = maxY;
  board.rightPaddle.setY(newY);
};
Board.prototype.setPoints = function(points){
  this.points = points;
  var s = points.toString();
  while (s.length < 4){
    s = "0"+s;
  }
  this.pointsEl.html(s);
};
Board.prototype.getPoints = function(){
  return this.points;
};
Board.prototype.isBallAboveBottom = function(){
  return this.ball.getY()+this.ball.getHeight() < this.getHeight()-4;
};
Board.prototype.isBallBelowTop = function(){
  return this.ball.getY() > 4;
};
Board.prototype.isBallBeforeRight = function(){
  return this.ball.getX()+this.ball.getWidth() < this.getWidth()-4-this.rightPaddle.getWidth();
};
Board.prototype.isBallAfterLeft = function(){
  return this.ball.getX() > 4;
};
Board.prototype.isBallOverPaddle = function(paddle){
  var ballCenter = this.ball.getY()+this.ball.getHeight()/2;
  var minY = paddle.getY();
  var maxY = paddle.getY()+paddle.getHeight();
  return minY < ballCenter && ballCenter < maxY;
};


Board.prototype.updateBallVelocity = function(){
  if (!this.isBallBeforeRight()){
    if (this.isBallOverPaddle(this.rightPaddle)){
      this.ball.vX *= -1;
      var board = this;
      this.pointsEl.slideUp(
        function(){
          board.setPoints(board.getPoints()+1);
        }).slideDown();
      this.ball.vX *= 1.025;
      this.ball.vY *= 1.025;
    } else {
      this.lose();
      return false;
    }
  }
  if (!this.isBallAfterLeft())
    this.ball.vX *= -1;
  if (!this.isBallBelowTop() || !this.isBallAboveBottom()){
    this.ball.vY *= -1;
  }
  return true;
};

Board.prototype.start = function(){
  this.ball.setX(this.getWidth()/2+this.ball.getWidth()/2);
  this.ball.setY(this.getHeight()/2+this.ball.getHeight()/2);
  this.el.removeClass("lose").removeClass("win");
  this.ball.start(1000/24, this.updateBallVelocity, this);
  this.started = true;
};
Board.prototype.getTwitterMessage = function(){
  return "I got "
    + this.getPoints()
    + " points playing Practice Pong! See if you can beat me: "
    + window.location;
};
Board.prototype.lose = function(){
  this.stop();
  $("#lose-message").html(
    "You got "+this.getPoints()+" points!<br />"
    + "Now "
    + "<a target=\"_new\" href=\"http://twitter.com/home?status="+this.getTwitterMessage()+"&update=update\">"
    + "tell your friends on Twitter!</a>"
  );
  this.el.addClass("lose");
  this.unbind("click");
};
Board.prototype.win = function(){
  this.el.addClass("win");
};

Board.prototype.stop = function(){
  this.ball.stop();
  this.started = false;
};

$(document).ready(
  function(){
    $("#help-text").hide();

    $("#ok").click(
      function(){
        $("#help-text").fadeOut("slow");
      });
    $("#help").fadeTo(0,.2).hover(
      function(){
        $(this).fadeTo("slow", 1);
      },
      function(){
        $(this).fadeTo("slow", .2);
      }).click(
        function(){
          $("#help-text").fadeIn("slow");
        });

    var board = new Board("#board", "#ball", "#left-paddle", '#right-paddle', '#points');

    $("#header").show(
      "slide",
      {direction: "up"},
      "slow",
      function(){
        $("#input-wrap").show(
          "slide",
          {direction:"left"},
          "slow",
          function(){
            $("#footer").fadeIn(3000);
          });
      });


  });

