guy g;
Set bullets; 
Set badguys;
Set splats;
Set walls;

void setup(){
  size(500,500);
  frameRate(30);
  resetGame();
}

void resetGame(){
  g = new guy();
  g.x = width/2;
  g.y = height/2;
  bullets = new HashSet();
  badguys = new HashSet();
  splats =  new HashSet();
  walls  =  new HashSet();
  for(int i = 0; i < 5; i++){
    badguys.add(new badguy(random(width),random(height)));
  }
  for(int i = 0; i < 5; i++){
    float x = 100; float y = 30;
    if(random(1) > .5){
        y = 100; x = 30;
    }
    walls.add(new wall(random(width),random(height),x,y));
  }
}



void draw(){
  background(0);

  Iterator j;
  Iterator i = bullets.iterator();
  while(i.hasNext()){
    bullet b = (bullet)i.next();
    b.move();
    b.draw(); 

    boolean isgone = false;


    if(b.age()){
      isgone =true;
    } 
    else {

      j = badguys.iterator();
      while(j.hasNext()){
        badguy t = (badguy)j.next();
        if(t.bullethit(b)){
          splats.add(new splat(b,t));
          isgone = true;
        } 
        else {

        }
      }
      if(!isgone){
        j = walls.iterator();
        while(j.hasNext()){
          wall t = (wall)j.next();
          if(bullethit(t,b)){
            splats.add(new splat(b,t));

            isgone = true;
          }
        }

      }

    }  
    if(isgone){
       i.remove(); 
    }
  }

  i = badguys.iterator();
  while(i.hasNext()){

    badguy b = (badguy)i.next();
    b.move();
    b.draw(); 
  }


  i = badguys.iterator();
  while(i.hasNext()){
    badguy b = (badguy)i.next();
    
        j = badguys.iterator();
  while(j.hasNext()){
       badguy b2 = (badguy)j.next();
      if(b.overlap(b2)){
         b.collide(b2);
      } 
  }
  }

  i = badguys.iterator();
  while(i.hasNext()){
    badguy b = (badguy)i.next();
        j = walls.iterator();
      while(j.hasNext()){
         wall w = (wall)j.next();
        if(b.overlap(w)){
          print("!!!!ouch "+b.x);
             b.collide(w);
          println("  "+b.x);


        } 
  }

  
  }


  i = walls.iterator();
  while(i.hasNext()){
    wall w = (wall)i.next();
    if(w.overlap(g)){
      w.collide(g);
    }
    w.draw();
  }

  i = splats.iterator();
  while(i.hasNext()){
    splat s = (splat)i.next();
    s.draw();
  }


  g.move();

  g.testFire();




  g.draw(); 
}


class splat{
  sprat parent;
  float xoff , yoff;
  splat(bullet b, sprat bg){
    parent = bg;
    xoff = b.x - bg.x;
    yoff = b.y - bg.y;

  } 

  void draw(){
    set(round(parent.x+xoff),round(parent.y+yoff),color(255,255,0)); 
  }

}

class bullet{
  float x,y, xspeed, yspeed;
  int lifetime;

  bullet(float px,float py, float pxspeed, float pyspeed){
    lifetime = 30;

    x = px; 
    y = py; 
    xspeed=pxspeed;
    yspeed=pyspeed;

  }

  void draw(){
    set(round(x),round(y),color(255,255,0)); 
  }
  void move(){
    x += xspeed;
    y += yspeed; 
  }
  boolean age(){
    lifetime--;
    if(lifetime < 0) {
      return true; 
    }
    return false;
  } 

}

class wall extends sprat{
  wall(float px, float py, float pw, float ph){
    x = px; 
    y = py ; 
    w = pw; 
    h = ph;
  } 
  void draw(){
    noStroke();
    fill(128);
//    rect(x,y,w,h);    

  }
}

boolean bullethit(sprat s, bullet b){
  if(isin(b.x,s.x,s.x+s.w) && isin(b.y,s.y,s.y+s.h)){
    return true;
  }
  return false; 
}


class badguy extends sprat {
  float h = 20;
  float w = 20;

  boolean bullethit(bullet b){
    if(isin(b.x,x,x+w) && isin(b.y,y,y+h)){
      return true;
    }
    return false; 
  }

  badguy(float px,float py){
    x = px;
    y = py;
  }
  float MAXSPEED = 1; 
  void draw(){
    noStroke();
    fill(20);
//    rect(x,y,w,h); 
  }
  void move(){
    if(x < g.x){
      xs += .1;
    }  
    else {
      xs -= .1;
    }
    if(y < g.y){
      ys += .1;
    } 
    else {
      ys -= .1;
    }

    x += xs;
    y+=ys;
    xs = bound(xs,-MAXSPEED,MAXSPEED);
    ys = bound(ys,-MAXSPEED,MAXSPEED);
  }
}


float bound(float val, float min, float max){
  if(val < min) val = min;
  if(val > max) val = max;
  return val;
}
boolean isin(float val, float min, float max){
  if(val >= min && val <= max)return true;
  return false;
}


class guy extends sprat {
  int xdir;
  int ydir;
  float speedmul = 3;
  guy(){
    w = 20;
    h = 20;
  }
  //random speed offset
  float r(){
    float r = random(-0.5,0.5); 
    return r;
  }

  void testFire(){
    if(mousePressed){
      if(! (xdir == 0 && ydir ==0)){


        bullets.add(new bullet(x+13,y+10,(xdir*speedmul*2)+r(),(ydir*speedmul*2)+r()));
      }
    }  

  }


  void move(){
    xdir=0;
    ydir = 0;
    if((x + (w/2))-mouseX < -2){
      xdir = 1;
    } 
    if((x + (w/2))- mouseX > 2){
      xdir = -1;
    } 
    if((y + (h/2))- mouseY <-2){
      ydir = 1;
    } 
    if((y + (h/2)) - mouseY> 2){
      ydir = -1;
    } 

    x+=xdir*speedmul;
    y+=ydir*speedmul;
  }

  void draw(){
    noStroke();
    //fill(128);
    //rect(x,y,20,20);
    fill(255);
    rect(x+2,y+7,6,6);   
    rect(x+12,y+7,6,6);   
    fill(0);
    rect(x+3+xdir,y+8+ydir,4,4);   
    rect(x+13+xdir,y+8+ydir,4,4);   

  }


}











HashMap keysNow = new HashMap();

void keyPressed(){
  if (key=='r'){
    resetGame(); 
  }

  keysNow.put(new Character(key), new Boolean(true));
}
void keyReleased(){
  keysNow.put(new Character(key), new Boolean(false));  
}
boolean isKeyDown(char pc){
  Character c =  new Character(pc);
  Boolean b = (Boolean)keysNow.get(c);
  if(b == null){
    return false;
  }
  return b.booleanValue();
}












void spratinit(float pgrav){
  rectMode(CORNER);
  spratGrav = pgrav;
}
float spratGrav = 0;

class sprat {
  sprat(){
  }
  float x,y,w,h,mode;
  float xs,ys;
  boolean wallbounce = true; 
  boolean floorbounce = true;
  boolean ceilingbounce = true;
  float rebound = .5; //how much it rebounds, 1.0 = elastic
  float lateralfriction = .8;


  sprat(float px, float py, float pw, float ph){
    x = px;
    y = py;
    w = pw;
    h = ph;

  }
  void draw(){
    fill(255); 
    stroke(0); 
    strokeWeight(1);
    rect(x,y,w,h); 
  }
  void move(){
    ys += spratGrav;
    x += xs;
    y += ys; 
    bounds(); 
  }
  //place sprat at side it hit with rebound speed multiplier thing
  void bounds(){
    if(wallbounce && x < 0) {
      x = 0;
      xs = abs(xs) * rebound;
      ys *= lateralfriction;
    } 
    if(wallbounce && x+w > width) {
      x = width-w;
      xs = abs(xs) * -rebound;
      ys *= lateralfriction;
    } 
    if(ceilingbounce && y < 0) {
      y = 0;
      ys = abs(ys) * rebound;
      xs *= lateralfriction;
    } 
    if(floorbounce && y+h > height) {
      y = height-h;
      ys = abs(ys) * -rebound;
      xs *= lateralfriction;
    } 

  }


  void collide(sprat o){
    float temp;
    temp = o.xs; 
    o.xs =  xs;
    xs = temp;
    temp = o.ys; 
    o.ys =  ys;
    ys = temp;

    //figure out which overlap is less, and make that the point where they push out       
    float hlap = spratOverlapHoriz(o).magnitude();
    float vlap = spratOverlapVert(o).magnitude();

stroke(255,0,0);
  //      line(0,0,x,y);
    //    line(0,0,o.x,o.y);



    if(hlap < vlap){
      if(x < o.x){
        println("!!!a!!!");
        
        
        x -= hlap;// / 2.0; 
        o.x += hlap;// / 2.0; 
      } 
      else {
        println("!!!b!!!");
        x += hlap;// / 2.0; 
        o.x -= hlap;// / 2.0;             
      }
    } 
    else {
      if(y < o.y){
                println("!!!c!!!");

        y -= hlap;// / 2.0; 
        o.y += hlap;// / 2.0; 
      } 
      else {
                println("!!!d!!!");

        y += hlap;// / 2.0; 
        o.y -= hlap;// / 2.0;             
      }

    }
stroke(0,255,0);
//        line(0,0,x,y);
  //      line(0,0,o.x,o.y);

  }



  boolean overlap(sprat other){
    floatRange hOverlap = spratOverlapHoriz(other);
    if(hOverlap == null) return false;
    floatRange vOverlap = spratOverlapVert(other);
    if(vOverlap == null){
      return false;
    }
    //pixel check would go here
    return true;
  }

  // to see if things overlap on one dimension
  // we sort the 4 points. if both points of 
  // one thing are lesser than both points of the other,
  // they can't be overlapping..., otherwise the overlap
  // is the 2nd and 3rd point
  floatRange spratOverlapHoriz(sprat b){
    sprat a = this;
    floatWithRef vals[] = new floatWithRef[4];
    vals[0] = new floatWithRef(a.x,a);
    vals[1] = new floatWithRef((a.x+a.w),a);
    vals[2] = new floatWithRef(b.x,b);
    vals[3] = new floatWithRef((b.x+b.w),b);
    Arrays.sort(vals);
    if (vals[0].src == vals[1].src){
      return null; 
    }
    return new floatRange(vals[1].val,vals[2].val);
  }

  floatRange spratOverlapVert(sprat b){
    sprat a = this;
    floatWithRef vals[] = new floatWithRef[4];
    vals[0] = new floatWithRef(a.y,a);
    vals[1] = new floatWithRef((a.y+a.h),a);
    vals[2] = new floatWithRef(b.y,b);
    vals[3] = new floatWithRef((b.y+b.h),b);
    Arrays.sort(vals);
    if (vals[0].src == vals[1].src){
      return null; 
    }
    return new floatRange(vals[1].val,vals[2].val);

  }






}



class floatRange{
  float min, max;
  floatRange(float p1, float p2){
    min = p1<p2?p1:p2; 
    max = p1>p2?p1:p2; 
  }
  float magnitude(){
    return abs(max-min) ;
  }

}

class floatWithRef implements Comparable{
  float val; 
  Object src;
  floatWithRef(float pval,Object psrc){
    val = pval; 
    src = psrc;
  } 
  public int compareTo(Object o){
    return int(round(val - ((floatWithRef)o).val));
  }

}