<!doctype html>
<html>
  <head>
    <title>🏓 Pong</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { height: 100vh; width: 100vw; overflow: hidden; }
      canvas {border: black solid 1px; top:-1px; left:-1px; position: absolute;}
      div { position: fixed; top: 0; right: 0; width: 130px; height: 60px; background: rgba(0, 0, 0, .5); color: white; padding: 5px; font-family: monospace; z-index: 100; }
    </style>
  </head>
  <body>
    <div id="debug">FPS: &nbsp;<fps>...</fps><br/>Ping: <ping>...</ping><br />ID: &nbsp;&nbsp;<id>...</id></div>
    <canvas id="test"></canvas>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      /** Initialize Canvas **/
      var c = document.getElementById("test");
      var ctx = c.getContext("2d");
      c.width = $(window).width();
      c.height = $(window).height();

      /** FPS Vars **/
      var lastCalledTime,
          lastFPSshown,
          fps;

      /** Global Configuration **/
      var localcoop = false, // Local Coop in 1 window CURRENTLY BORKEN [WONTFIX]
          width = $(window).width(),
          height = $(window).height();

      /** Prop-Config **/
      var cube_width = 50,
          cube_height = 200,
          cube_speed = 7,
          ball_size = 15,
          ball_speed = 8,
          ballX = cube_width+ball_size,
          ballY = cube_height/2,
          ball_owner = 1,
          ball_VX = 0,
          ball_VY = 0,
          ball_maxAngle = 5*Math.PI/12;

      /** Game Variables **/
      var uid, // Unique-ID
          pid, // PlayerID 0 == Specator, 1 == Player 1, 2 == Player 2
          pwin = 0,
          pwinanim = 0;
          leftY = 0,
          rightY = 0,
          leftV = 0,
          rightV = 0,
          leftColor = 'red',
          rightColor = 'blue',
          score = [0,0],
          pressedkeys = [],
          loggedkeys = [38, 40, 83, 87],
          gameState = 0; // 0 == Waiting for Players, 1 == Countdown, 2 == inGame, 3 == ??Win??

      var Localization = {
          player        : 'Spieler',
          spectator     : 'Zuschauer',
          spectatormode : 'Zuschauer-Modus',
          countdown     : 'Starte in ',
          countdownsuf  : ' Sekunden',
          waiting       : 'Warte auf Spieler',
          win           : ' hat gewonnen!'
      }

      /** Initalize Socketio **/
      var socket = io();

      /** ############## **/
      /** SERVER EVENTS **/
      /** ############# **/
      socket.on('handshake', function(id, state, c_width, c_height, sp, score, fn){
        $('id').text(id);
        uid = id;
        gameState = state;
        cube_width = c_width;
        cube_height = c_height;
        cube_speed = sp;
        score = score;
        fn($(window).width(), $(window).height());
      });

      socket.on('setpid', function(id){
        pid = id;
      });

      socket.on('size', function(w, h){
        width = w;
        height = h;
        c.width = w;
        c.height = h;
        text =  Localization.countdown + 5 + Localization.countdownsuf;
      });

      socket.on('gamestate', function(state){
        // console.log('new GameState: ' + state);
        gameState = state;
      });

      socket.on('score', function(val){
        score = val;
      });

      /** Serversided Countdown to prevent desync **/
      socket.on('countdown', function(i){
        text = Localization.countdown + i + Localization.countdownsuf;
      });

      socket.on('win', function(id, data){
        pwin = id;
        console.log('Additional Data: ' + data);
      });

      socket.on('paddlepos', function(pos, val, y){
        if(pos == 1){
          leftV = val;
          leftY = y;
        }
        if(pos == 2){
          rightV = val;
          rightY = y;
        }
        (leftY);
      });

      socket.on('ballpos', function(data){
        ballX = data['x'];
        ballY = data['y'];
        ball_VX = data['vx'];
        ball_VY = data['vy'];
        ball_speed = data['speed'];
        ball_owner = data['owner'];
      });

      /** Print text in local console **/
      socket.on('debug', function(arg){
        console.log(arg);
      });


      /** ############ **/
      /** DEBUG TOOLS **/
      /** ########### **/
      /** Meassure Ping and show **/
      setInterval(function(){
        var connTime = Date.now();
        socket.emit('appping', function(data){
          $('ping').text(Date.now()-connTime + 'ms');
        });
      }, 1000);

      var texti = 0,
        nativtext = Localization.waiting,
        text = nativtext;
      function draw() {


        /** FPS Calculation **/
        if(!lastCalledTime) {
           lastCalledTime = Date.now();
           fps = 0;
        }
        delta = (Date.now() - lastCalledTime)/1000;
        lastCalledTime = Date.now();
        fps = 1/delta;

        /** Show FPS in Debug-Window **/
        if(!lastFPSshown) {
            lastFPSshown = Date.now();
            $('fps').text('...');
        }
        if(lastFPSshown+1000 < Date.now()) {
          lastFPSshown = Date.now();
          $('fps').text(Math.round(fps));

          /** Use this timer to display loading indicator **/
          if(gameState == 0)
            switch(texti){
              case 0: text = nativtext; texti++; break;
              case 1 :
              case 2 : text+='.'; texti++; break;
              case 3 : text+='.'; texti = 0; break;
            }
          }

        /** ############## **/
        /** CLIENT RENDER **/
        /** ############# **/
        /** Clear Drawing-Region **/
        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, width, height);


        /** Points **/
        ctx.fillStyle = 'black';
        ctx.textAlign = 'center';
        var scoretxt = score[0] + ' : ' + score[1];
        if(pwin != 0 && gameState == 3){
            ctx.font = '80px monospace';
            ctx.fillText(Localization.player + ' ' + pwin +  Localization.win, width/2, 310);
            ctx.font = '80px monospace';
            ctx.fillText(scoretxt, width/2, 190);
        } else {
          ctx.font = '40px monospace';
          ctx.fillText(scoretxt, width/2, 70);
        }

        if(gameState < 2){
          ctx.fillStyle = 'black';
          ctx.font = '30px monospace';
          ctx.textAlign = 'center';
          ctx.fillText(text, width/2, height/2-30);
        }


        ctx.fillStyle = 'black';
        ctx.font = '15px monospace';
        ctx.textAlign = 'center';
        if(gameState == 2 && pid == 0){
          ctx.fillText(Localization.spectatormode, width/2, 25);
        }
        if(pid == 1 || pid == 2){
          ctx.fillText(Localization.player + ' ' + pid, width/2, 25);
        }


        /** Draw Cube **/
        /** Note: Get position by multiplying it with the "Render-Delta" - for same speed on every system **/
          // console.log(leftV*delta*60*cube_speed);
          // leftY -= leftV*delta*60*cube_speed;
          // rightY -= rightV*delta*60*cube_speed;
          // NOTE Server is calcualating Position -> Client is only drawing


        ctx.fillStyle = leftColor;
        ctx.fillRect(0, leftY, cube_width, cube_height);
        ctx.fillStyle = rightColor;
        ctx.fillRect(width-cube_width, rightY, cube_width, cube_height);


        // NOTE Physics moved to Server
        // /** Move Ball with Paddle if owned by owner (@roundstart) **/
        // if(ball_owner == 1){
        //   ballX = cube_width+ball_size,
        //   ballY = leftY+cube_height/2;
        // }
        // else if(ball_owner == 2){
        //   ballX = width-cube_width-ball_size,
        //   ballY = rightY+cube_height/2;
        // }
        //
        // /** ############ **/
        // /** Ball Physics **/
        // /** ############ **/
        //
        // /** Collide with right Paddle  or get point and respawn **/
        // if(ballX > width - cube_width){
        //   if(ballY < rightY || ballY > rightY+cube_height){
        //     console.log('Point for Left');
        //     ball_VX = 0;
        //     ball_VY = 0;
        //     ball_owner = 1;
        //   } else {
        //       var intersect = ((rightY+cube_height/2)-ballY)/(cube_height/2);
        //       console.log('Intersect with right at:');
        //       console.log(intersect);
        //       console.log('###########################');
        //       ball_VX = -ball_speed*Math.cos(intersect*ball_maxAngle);
        //       ball_VY = -ball_speed*Math.sin(intersect*ball_maxAngle);
        //   }
        // }
        //
        // /** Collide with left Paddle  or get point and respawn **/
        // if(ballX < cube_width){
        //   if(ballY < leftY || ballY > leftY+cube_height){
        //     console.log('Point for Right');
        //     ball_VX = 0;
        //     ball_VY = 0;
        //     ball_owner = 2;
        //   } else {
        //       var intersect = ((leftY+cube_height/2)-ballY)/(cube_height/2);
        //       console.log('Intersect with left at:');
        //       console.log(intersect);
        //       console.log('###########################');
        //       ball_VX = ball_speed*Math.cos(intersect*ball_maxAngle);
        //       ball_VY = ball_speed*Math.sin(intersect*ball_maxAngle);
        //   }
        // }
        //
        //
        // /** Collide with walls **/
        // if(ballY <= 0 || ballY >= height-ball_size){
        //   ball_VY = -ball_VY;
        //   console.log(ballX + ' : ' + ballY);
        // }

        /** Draw Ball **/
        ctx.fillStyle = 'black';
        // ballX += Math.round((1*delta)*60)*ball_VX;
        // ballY += Math.round((1*delta)*60)*ball_VY;
        ctx.beginPath();
        ctx.arc(ballX, ballY, ball_size, 0, 2*Math.PI);
        ctx.fill();

      }

      /** ############## **/
      /** Input Handlers **/
      /** ############## **/

      $(window).on('keydown', function(e) {
        if(localcoop){
          switch(e.keyCode) {
            case 38 : if(leftY <= 0) break; setV(1, 1); break;
            case 40 : if(leftY >= height-cube_height) break; setV(1, -1); break;
            case 87 : if(rightY <= 0) break; setV(2, 1); break;
            case 83 : if(rightY >= height-cube_height) break; setV(2, -1); break;
            case 37 :
            case 39 : if(ball_owner != 1) break; shootBall(); break;
            case 65 :
            case 68 : if(ball_owner != 2) break; shootBall(); break;
          }
        } else if(pid == 1 || pid == 2){
          if(gameState == 3) return;
          if(loggedkeys.indexOf(e.keyCode) > -1 && pressedkeys.indexOf(e.keyCode) < 0)
            pressedkeys.push(e.keyCode);
          switch(e.keyCode) {
            case 38 :
            case 87 : if(pid == 1){
                        if(leftY <= 0) break; setV(1, 1);
                      } else if(pid == 2) {
                        if(rightY <= 0) break; setV(2, 1);
                      }
                      break;
            case 40 :
            case 83 : if(pid == 1){
                        if(leftY >= height-cube_height) break; setV(1, -1);
                      } else if(pid == 2){
                        if(rightY >= height-cube_height) break; setV(2, -1);
                      }
                      break;
            case 37 :
            case 39 :
            case 65 :
            case 68 : if(ball_owner != pid)break; if(ball_owner == 1) socket.emit('ballshoot', leftY); else if(ball_owner == 2) socket.emit('ballshoot', rightY); break;
          }
        }
      });

      $(window).on('keyup', function(e) {
        if(localcoop){
          switch(e.keyCode) {
            case 38 :
            case 40 : setV(1, 0); break;
            case 87 :
            case 83 : setV(2, 0); break;
          }
        }else if(pid == 1 || pid == 2){
          if(gameState == 3) return;
          if(loggedkeys.indexOf(e.keyCode) > -1 && pressedkeys.indexOf(e.keyCode) > -1){
            pressedkeys.splice(pressedkeys.indexOf(e.keyCode), 1);
            if(pressedkeys.length == 0) // Only send v=0 if all keys are up
              setV(pid, 0);
          }
        }
      });


      function setV(pos, val){
        var time = Date.now();
        if(pos == 1){
          leftV = val;
          if(!localcoop)
            socket.emit('vchange', pos, val, leftY, time);
        }
        if(pos == 2){
          rightV = val;
          if(!localcoop)
            socket.emit('vchange', pos, val, rightY, time);
        }
      }

      function shootBall() {
        // NOTE moved to Server
        // console.log('### Ball shoot event ###');
        // /** TODO Tewak and Add MAX_Speed **/
        // if(ball_owner == 1){
        //   ball_VX = ball_speed;
        //   console.log('owner: left  - at:' + leftY);
        //
        // }
        // else if(ball_owner == 2){
        //   ball_VX = -ball_speed;
        //   console.log('owner: right  - at:' + rightY);
        //   if(!localcoop)
        //     socket.emit('ballshoot', rightY);
        // }
        // ball_owner = 0;
      }

      function animloop() {
        draw();
        window.requestAnimationFrame(animloop);
      }
      animloop();
    </script>
  </body>
</html>