float HALFCOURT = 125;

int playerscore; int compscore;
boolean playing = false;

Map font = new TreeMap();
String msg = "";

static float BS = 20;
static float BALLSPEED = 1;
static float PW = 20,PH = 40, PF=20;
static float GRAV  =.4;
static float KICK = 5;
Paddle player = new Paddle("PLAYER ONE",color(255,0,0),HALFCOURT-(PF/2));
Paddle comp = new Paddle("COMPUTER",color(0,0,255),-HALFCOURT+(PF/2));
Ball ball = new Ball();
;

void reset(){
   ball = new Ball();
   playerscore=compscore=0;
   playing = true;
   msg = "";
}

void setup(){
  size(500,500,P3D); 
  rectMode(CENTER);
  frameRate(30);
  setupFont();

}


void draw(){
  background(128);
  stroke(0);
  fontText(10,10,"JOUSTPONG 3D "+player.name+" "+playerscore+" "+comp.name+" "+compscore);
  if(!playing){
    stroke(100,50,50);
     fontText(10,475,msg+" SPACE TO START"); 
  }
  pushMatrix();
  translate(250,250);
  stroke(0,0,255,80);
  fill(200,200,255,40);

  rotateX((250-mouseY)/1000.0);
  rotateY((mouseX-250)/1000.0);

  box(HALFCOURT*2);


  comp.steer();


  player.move();
  comp.move();
  if(playing){
  ball.move();
  }
  player.draw();
  comp.draw(); 
  if(playing){
  ball.draw();
  }

  popMatrix();
}

void keyPressed(){
   if(key == ' '){
    reset();
   }
  if(key == 'z' || key == 'Z'){
     comp.ai = false;
     comp.name = "PLAYER TWO";
      comp.flap(RIGHT);
  } 
  if(key == 'x' || key == 'x'){
     comp.ai = false;
     comp.name = "PLAYER TWO";
      comp.flap(LEFT);   
  } 
}
void keyReleased(){
 comp.unflap(LEFT); comp.unflap(RIGHT); 
}

void mousePressed(){
  player.flap(mouseButton);

}
void mouseReleased(){
  player.unflap(mouseButton);

}

void checkEnd(){
   if(playerscore >= 10){
       msg = player.name+" WINS";
      playing = false;
   }   
   if(compscore >= 10){
       msg = player.name+" WINS";
      playing = false;
   }   
}

class Ball{
  float x,y,z;
  float xs,ys,zs;

  Ball(){
    zs = BALLSPEED;
    if(random(1) < .5){
      zs *= -1;
    }  


  }
  void move(){
    x += xs;
    y += ys;
    z += zs; 


    if(z >= HALFCOURT-BS/2){
      x = comp.x; y = comp.y; z = comp.z * 3/ 4;
        compscore++;
        checkEnd();
    }
    if(z <= -HALFCOURT+BS/2){
      x = player.x; y = player.y; z = player.z * 3 / 4;
        playerscore++;
        checkEnd();
    }

    if(x >= HALFCOURT-BS/2){
      x = HALFCOURT-BS/2;
      xs *= -1;
    }
    if(x <= -HALFCOURT+BS/2){
      x = -HALFCOURT+BS/2;
      xs *= -1;
    }
    if(y >= HALFCOURT-BS/2){
      y = HALFCOURT-BS/2;
      ys *= -1;
    }
    if(y <= -HALFCOURT+BS/2){
      y = -HALFCOURT+BS/2;
      ys *= -1;
    }



    if(z+BS / 2  >= HALFCOURT - PF){
      if(overlap(player.x-PW/2,player.x+PW/2,ball.x-BS/2,ball.x+BS/2  )
        && overlap(player.y-PH/2,player.y+PH/2,ball.y-BS/2,ball.y+BS/2  )){
        z = HALFCOURT - PF - BS/2;
        xs = player.xs;
        ys = player.ys;
        zs = -BALLSPEED; 
      }
    }
    if(z-BS / 2  <= -HALFCOURT + PF){
      if(overlap(comp.x-PW/2,comp.x+PW/2,ball.x-BS/2,ball.x+BS/2  )
        && overlap(comp.y-PH/2,comp.y+PH/2,ball.y-BS/2,ball.y+BS/2  )){

        z = -HALFCOURT + PF + BS/2;
        xs = comp.xs;
        ys = comp.ys;
        zs = BALLSPEED; 
      }
    }




  }
  void draw(){
    //shadow
    noStroke();
    pushMatrix();
    fill(0,80);
    translate(x,HALFCOURT,z);
    box(BS,1,BS);
    popMatrix(); 
    strokeWeight(2);
    //ball
    pushMatrix();
    fill(255,80);
    stroke(100,100,255,80);
    strokeWeight(2);
    translate(x,y,z);
    box(BS);
    popMatrix(); 
  }

}

class Paddle{

  float x,y,z,xs,ys;
  color c;
  String name;
  Paddle(String name, color c, float z){
    this.z = z; 
    this.c = c;
    this.name = name;
  }
boolean ai = true;
  void steer(){
    if(ai == false) {
     return; 
    }
if(ball.zs < 0){
    if(ball.y < y){
      int dir = LEFT;
      if(ball.x < x){
        dir = RIGHT;
      }    
      flap(dir);
    }
}
  }
int leftwingdown; int rightwingdown;
  void draw(){
    //println(name+" "+x+" "+y+" "+z);
noStroke();
    pushMatrix();
    fill(0,80);    
    translate(x,HALFCOURT,z);
    box(PW,1,PF);
strokeWeight(2);    
    popMatrix();

    fill(c,80);
    pushMatrix();
    translate(x,y,z);
    box(PW,PH,PF);

pushMatrix();
    if(leftwingdown > 0){
       leftwingdown--;
       translate(-PW/2,0,0);
       box(PW/2,PH/2,PF);
    } else {
       translate(-PW,0,0);
       box(PW,PH/4,PF);
    }
popMatrix();
pushMatrix();
    if(rightwingdown > 0){
       rightwingdown--;
       translate(PW/2,0,0);
       box(PW/2,PH/2,PF);
    } else {
       translate(PW,0,0);
       box(PW,PH/4,PF);
    }
popMatrix();


    popMatrix();
  }  
  void unflap(int way){
    if(way == LEFT){
     rightwingdown  = 0;
    } 
    else {
   leftwingdown = 0;   

    }
    
  }
  void flap(int way){

    ys -= KICK;

    if(way == LEFT){
      xs += KICK/4;   
   rightwingdown = 10;   
    } 
    else {
      xs -= KICK/4;      
      leftwingdown = 10;

    }
  }


  void move(){
    ys = ys + GRAV; 
    y+=ys;
    if(y+(PH/2) > HALFCOURT){
      y = HALFCOURT-(PH/2) ;
      ys *= -.5;
    }      
    if(y-PH < -HALFCOURT){
      y = -HALFCOURT+PH ;
      ys *= -.5;
    }
    x = x+ xs;
    if(x+(PW/2) > HALFCOURT){
      x = HALFCOURT-(PW/2) ;
      xs *= -.5;

    }      
    if(x-PW < -HALFCOURT){
      x = -HALFCOURT+PW ;
      xs *= -.5;
    }

    xs *= .9999;
  }

}





boolean overlap(float line1val1,float line1val2,float line2val1,float line2val2){
  float line1min = min(line1val1,line1val2);
  float line1max = max(line1val1,line1val2);
  float line2min = min(line2val1,line2val2);
  float line2max = max(line2val1,line2val2);

  if(line1min < line2min && line1max < line2min){
    return false; 
  }
  if(line1min > line2max && line1max > line2max){
    return false; 
  }  


  return true;
}

void fontText(float x, float y, String s){
    for(int i = 0; i < s.length();i++){
       drawChar(s.substring(i,i+1),x,y,8,12);
      x += 10; 
    }
}  

void setupFont(){
 font.put("0",new int[] {1,2,5,6,9,10,13,14});
font.put("1",new int[] {4,11} );
font.put("2",new int[] {1,6,8,7,9,14} );
font.put("3",new int[] {1,6,8,7,13,14} );
font.put("4",new int[] {2,7,8,6,13} );
font.put("5",new int[] {1,2,7,8,13,14} );
font.put("6",new int[] {1,2,7,8,13,14,9} );
font.put("7",new int[] {1,6,13} );
font.put("8",new int[] {1,2,6,7,8,9,13,14} );
font.put("9",new int[] {1,2,6,7,8,13,14} );
font.put("A",new int[] {9,2,1,6,13,7,8} );
font.put("B",new int[] {1,14,4,6,11,13,8} );
font.put("C",new int[] {1,2,9,14} );
font.put("D",new int[] {1,14,4,11,6,13} );
font.put("E",new int[] {1,2,7,9,14} );
font.put("F",new int[] {1,2,7,9} );
font.put("G",new int[] {1,2,9,14,13,8} );
font.put("H",new int[] {2,9,6,13,7,8} );
font.put("I",new int[] {1,4,11,14} );
font.put("J",new int[] {9,14,13,6} );
font.put("K",new int[] {2,9,7,5,12} );
font.put("L",new int[] {2,9,14} );
font.put("M",new int[] {9,2,3,5,6,13} );
font.put("N",new int[] {9,2,3,12,13,6} );
font.put("O",new int[] {1,2,9,14,13,6} );
font.put("P",new int[] {1,2,6,7,8,9} );
font.put("Q",new int[] {1,2,6,9,14,13,12} );
font.put("R",new int[] {1,2,6,7,8,9,12} );
font.put("S",new int[] {1,2,7,8,13,14} );
font.put("T",new int[] {1,4,11} );
font.put("U",new int[] {2,9,14,13,6} );
font.put("V",new int[] {2,9,10,5} );
font.put("W",new int[] {2,9,14,13,6,11} );
font.put("X",new int[] {3,10,5,12} );
font.put("Y",new int[] {3,5,11} );
font.put("Z",new int[] {1,5,10,14} );

font.put(" ",new int[] {} );
font.put(".",new int[] {0} );
font.put(",",new int[] {10} );
font.put("!",new int[] {4,11,0} );

font.put("?",new int[] {1,2,6,8,11,0} ); 
}

void drawChar(String c, float x, float y, float w, float h){
strokeWeight(2);//  line(0,0,100,100);
   int[] segs = (int[])font.get(c);
   if(segs == null) return;
   for(int i = 0; i < segs.length; i++){

      switch(segs[i]){
         case 1:
         line(x,y,x+w,y); break;
         case 2:
         line(x,y,x,y+(h/2));break;
         case 3:
         line(x,y,x+(w/2),y+(h/2));break;
         case 4:
         line(x+(w/2),y,x+(w/2),y+(h/2));break;
         case 5:
        line(x+w,y,x+(w/2),y+(h/2));  break;       
        case 6:
        line(x+w,y,x+w,y+(h/2));break;
         case 7:
          line(x,y+(h/2),x+(w/2),y+(h/2));break;
          case 8:
          line(x+(w/2),y+(h/2),x+w,y+(h/2));break;
          case 9:
          line(x,y+(h/2),x,y+h);break;
          case 10:
          line(x,y+h,x+w/2,y+h/2);break;
          case 11:
          line(x+w/2,y+h/2,x+w/2,y+h);break;
          case 12:
          line(x+w/2,y+h/2,x+w,y+h);break;
          case 13:
          line(x+w,y+h/2,x+w,y+h);break;
          case 14:
          line(x,y+h,x+w,y+h);break;
          case 0:
          line(x+w/2-w/6,y+h+h/6,x+w/2+w/6,y+h+h/6);break;
          
        default:
       break; 
      }
     
     
   }
   
}