import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Iterator; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.Map; 
import ddf.minim.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 

public class maingame extends PApplet {

 Resources resources = new Resources();
static int displayScale = 2;
Game game = new Game();
Animation _animation;

enum GameStatus{
  Title, Tutorial, Playing, Gameover, Clear, Endroll
}
int tutocount;
GameStatus gameStatus;

public void setup(){
  _animation=new Animation("operating",8);
  surface.setSize(16*20*displayScale, 16*15*displayScale);
  try{
    resources.minim = new Minim(this);
  }catch(Exception e){}
  
  resources.loop("bgm&se/maingame_bgm.wav");
  gameStatus = GameStatus.Title;
  game.setup();
}

public void draw(){
  println(gameStatus);
  background(0,0,0);
  pushMatrix();
  scale(displayScale, displayScale);
  

  if(gameStatus == GameStatus.Title){
    pushMatrix();
    translate(20*8, 15*10);
    resources.draw("Title.png");
    popMatrix();
  }

  if(gameStatus == GameStatus.Tutorial){
 
    pushMatrix();
    translate(20*8, 15*10.7f);
    resources.draw("operating1.png");
    _animation.draw(tutocount);
    if(tutocount<7){
    tutocount++;
    try{
    Thread.sleep(200);
  }catch(InterruptedException e){ 
e.printStackTrace(); 
} 
    }else{
    tutocount=0;
    try{
    Thread.sleep(200);
  }catch(InterruptedException e){ 
e.printStackTrace(); 
} 
  }
    popMatrix();
  }

  if(gameStatus == GameStatus.Playing){
    game.update();
    game.draw();
   
    if(game.isGameover()){
      gameStatus = GameStatus.Gameover;
      resources.close("bgm&se/maingame_bgm.wav");
    }

    if(game.isGoal()){
      gameStatus = GameStatus.Clear;
      resources.close("bgm&se/maingame_bgm.wav");
    }
  }

  if(gameStatus == GameStatus.Gameover){
    pushMatrix();
    translate(20*8, 15*10.7f);
    resources.draw("Gameover.png");
    popMatrix();
  }

  if(gameStatus == GameStatus.Clear){
    pushMatrix();
    translate(20*8, 15*10.7f);
    resources.draw("clear.png");
    popMatrix();
  }


  if(gameStatus == GameStatus.Endroll){
    pushMatrix();
    translate(20*8, 15*10.7f);
    resources.draw("endroll.png");
    popMatrix();
  }

  popMatrix();
}


public void keyPressed() {
  switch(gameStatus){
    case Title:
      gameStatus = GameStatus.Tutorial;
      break;
    case Tutorial:
      game.setup();
      gameStatus = GameStatus.Playing;
      break;
    case Playing:
      game.keyPressed(key, keyCode);
      break;
    case Gameover:
      gameStatus = GameStatus.Endroll;
      break;
    case Clear:
      gameStatus = GameStatus.Endroll;
      break;
    case Endroll:
      gameStatus = GameStatus.Title;
      resources.loop("bgm&se/maingame_bgm.wav");
      break;
  }
}

public void keyReleased(){
  game.keyReleased(key, keyCode);
}





class AABBNode{
  AABBNode(List<Entity> entities){
    _isLeaf = entities.size() == 1;
    
    if(!isLeaf()){
      sort(entities, sortingAxis(entities));
      generateNexts(entities);
    }else{
      _entity = entities.get(0);
      _min = new PVector(_entity.x()-_entity.width()/2, _entity.y()-_entity.height()/2);
      _max = new PVector(_entity.x()+_entity.width()/2, _entity.y()+_entity.height()/2);
    }
  }
  
  public boolean isLeaf(){
    return _isLeaf;
  }
  
  public List<Entity> detectedEntities(Entity entity){
    List<Entity> l = new ArrayList<Entity>();
    
    boolean isColliding = 
    entity.x() + entity.width()/2 > _min.x &&
    entity.x() - entity.width()/2 < _max.x &&
    entity.y() + entity.height()/2 > _min.y &&
    entity.y() - entity.height()/2 < _max.y;
    
    if(isColliding){
      if(_isLeaf){
        if(_entity != entity){
          l.add(_entity);
        }
      }else{
        l.addAll(_next.get(0).detectedEntities(entity));
        l.addAll(_next.get(1).detectedEntities(entity));
      }
    }
    return l;
  }
  
  private List<AABBNode> _next;
  private Entity _entity;
  private boolean _isLeaf;
  private PVector _max;
  private PVector _min;
  
  private void generateNexts(List<Entity> entities){
    int size = entities.size();
    _next = new ArrayList<AABBNode>();
    _next.add(new AABBNode(entities.subList(0, size/2)));
    _next.add(new AABBNode(entities.subList(size/2, size)));
  }
  
  private void sort(List<Entity> entities, int axis){
    EntityComparator comparator = new EntityComparator(axis);
    
    Collections.sort(entities, comparator); 
  }
  
  private int sortingAxis(List<Entity> entities){
    PVector min = new PVector(0, 0);
    PVector max = new PVector(0, 0);
    for(Entity entity : entities){
      min.x = min(min.x, entity.x() - entity.width()/2);
      max.x = max(max.x, entity.x() + entity.width()/2);
      
      min.y = min(min.y, entity.y() - entity.height()/2);
      max.y = max(max.y, entity.y() + entity.height()/2);
    }
    
    _min = min;
    _max = max;
    PVector d = max.sub(min);
    return (d.x > d.y)? 0:1;
  }
}



public class EntityComparator implements Comparator<Entity> {
    EntityComparator(int axis){_sortingAxis = axis;}
    //\u6bd4\u8f03\u30e1\u30bd\u30c3\u30c9\uff08\u30c7\u30fc\u30bf\u30af\u30e9\u30b9\u3092\u6bd4\u8f03\u3057\u3066-1, 0, 1\u3092\u8fd4\u3059\u3088\u3046\u306b\u8a18\u8ff0\u3059\u308b\uff09
    public int compare(Entity a, Entity b) {
      if(_sortingAxis == 0){
        boolean bool = a.x() < b.x();
        return bool?1:0;
      }else{
        boolean bool = a.y() < b.y();
        return bool?1:0;
      }
    }
    
    private int _sortingAxis;
}
class Animation{
  //
  Animation(String imagePrefix){
    this(imagePrefix, 1);
  }

  //
  Animation(String imagePrefix, int count){
    _imagePrefix = imagePrefix;
    _maxImages = count;
  }


  public int maxImages(){
    return _maxImages;
  }


  public void draw(){
    draw(0);
  }
  //
  public void draw(int frame){
    resources.draw(getFileName(_imagePrefix, frame));
  }

  //
  public int width(){return resources.width(getFileName(_imagePrefix, 0));}

  //    
  public int height(){return resources.height(getFileName(_imagePrefix, 0));}

  private String _imagePrefix;

  private int _maxImages;

  private String getFileName(String imagePrefix, int count){
    if(_maxImages == 1){
      return imagePrefix + ".png";
    }else{
      return imagePrefix + nf(count+1) + ".png";
    }
  }
}
class Point {
  int x1;
  int y1;

  Point(int ix, int iy) {
    x1=ix;
    y1=iy;
  }
}

// PImage[] img, imgE;
//
// Point[] player=new Point[4];
// Point[] enemy=new Point[4];
//
// int cell=2;
// int cellE=2;
// int number=0;
// int speed=10;
// int x=0;
// int y=0;
// int change=0;
// int changeE=0;
// boolean atari=false;

// void setup() {
//   size(1024, 768);
//
//   player[0]=new Point(0, 0);
//   player[1]=new Point(0, 500);
//   player[2]=new Point(500, 0);
//   player[3]=new Point(500, 500);
//
//   enemy[0]=new Point(700, 0);
//   enemy[1]=new Point(700, 417);
//   enemy[2]=new Point(1000, 0);
//   enemy[3]=new Point(1000, 417);
//
//   img = new PImage[cell];
//   imgE=new PImage[cellE];
//   for (int i=0; i<img.length; i++) {
//     img[i]=loadImage(i+".png");
//   }
//   for (int j=0; j<imgE.length; j++) {
//     imgE[j]=loadImage("E"+j+".png");
//   }
//   frameRate(30);
// }
//
// void draw() {
//   background(255);
//   image(imgE[changeE], 700, 0);
//   image(img[change], x, y);
//   boolean r1=isCollisionSide(player[2], player[3], enemy[0], enemy[1]);
//   boolean r2=isCollisionSide(enemy[0], enemy[1], player[2], player[3]);
//   boolean r3=isCollisionSide(enemy[0], enemy[1], player[0], player[1]);
//   boolean r4=isCollisionSide(player[0], player[1], enemy[0], enemy[1]);
//   player[0]=new Point(0+x, 0);
//   player[1]=new Point(0+x, 500);
//   player[2]=new Point(500+x, 0);
//   player[3]=new Point(500+x, 500);
//   if ( r1 == true && r2 == true ) {
//     changeE=1;
//   } else if (r3==true&&r4==true) {
//     changeE=0;
//   }
// }
//
// void keyPressed() {
//   if (key == CODED) {
//     if (keyCode == RIGHT) {
//       x+=speed;
//       change=0;
//     }
//     if (keyCode == LEFT) {
//       x-=speed;
//       change=0;
//     }
//     if (keyCode == UP) {
//       change=1;
//     }
//   }
// }
//
// boolean isCollisionSide(Point r1, Point r2, Point p1, Point p2) {
//   float t1, t2;
//
//   t1 = (r1.x1-r2.x1)*(p1.y1-r1.y1)+(r1.y1-r2.y1)*(r1.x1-p1.x1);
//   t2 = (r1.x1-r2.x1)*(p2.y1-r1.y1)+(r1.y1-r2.y1)*(r1.x1-p2.x1);
//
//   if (t1*t2<0||t1>=0||t2>=0) {
//     return(true);
//   } else {
//     return(false);
//   }
// }
//
// void Punch() {
// }
//
// void Kick() {
// }
//
// void Wave() {
// }
class Boss{
  Animation _boss;
  boolean _enableToJumped = false;
  float speed;
  float _x,_y;
  Boss(float xpos,float ypos,float sp){
    _x=xpos;
    _y=ypos;
    speed=sp;
    _boss=new Animation("boss_stand0",2);
  }
  
   private Stage _stage;
  public void setStage(Stage s){
    _stage = s;
  };
  
  public void  draw(){
    _boss.draw(0);
  }
  
  float _fy = 0.1f, _vy = 0;

  public void update(){
  if(_stage.fieldTileType((int)((_x+8)/16.0f), (int)((_y+8.0f+8.0f)/16.0f)) != TileType.Block){
      _enableToJumped = false;
      _vy += 0.3f;
    }else{
      _enableToJumped = true;
      _vy = 0; 
    }
    _y+=_vy;
  }
}
class CollisionDetector{
  CollisionDetector(){}
  
  public void update(List<Entity> entities){
    generateTree(entities);
    updateEntities(entities);
  }
  
  private void updateEntities(List<Entity> entities){
    for(Entity from: entities){
      for(Entity to: _tree.detectedEntities(from)){
        from.callCollidingEvent(to.type());
      }
    }
  }
  
  // private Entity[2][] _detectedPair;
  private List<Entity[]> _detectedPairs;
  
  private void generateTree(List<Entity> entities){
    _tree = new AABBNode(entities);
  };
  
  // private void generateTreeRec(){}
  
  private AABBNode _tree;
  
}
enum EnemyType{
  Bird,Dog,Boss;
}

class Enemy implements Entity{
  Animation _animation ;
  boolean _enableToJumped = false;
  float _x,_y;
  float _life;
  float speed;
  int direction=-1;
  EnemyType _enemytype;
  boolean _shouldDie;
  Enemy(float xpos,float ypos,EnemyType t){
    _x=xpos;
    _y=ypos;
    _enemytype=t;
    

    _shouldDie = false;
    if(_enemytype==EnemyType.Bird){ 
      _animation = new Animation("Bird",2);
      speed=1;
      _life = 10;
    }else if(_enemytype==EnemyType.Dog){
      _animation = new Animation("Dog",2);
      speed=0.5f;
      _life = 20;
    }else if(_enemytype==EnemyType.Boss){
      _animation=new Animation("boss_stand0",2);
      speed=1;
      _life = 100;
    }
    
  }

  private Stage _stage;
  public void setStage(Stage s){
    _stage = s;
  };
  
  public void draw(){
    _animation.draw(0);
  }
  
  float _fy = 0.1f, _vy = 0;

  public void update(){
    
    if(_stage.fieldTileType((int)((_x+8)/16.0f), (int)((_y+8.0f+8.0f)/16.0f)) != TileType.Block){
      _enableToJumped = false;
      _vy += 0.3f;
    }else{
      _enableToJumped = true;
      _vy = 0; 
    }
    _x+=(speed*direction);
    if(_y>(20*16)){

      direction*=-1;
    }
    if(_enemytype==EnemyType.Dog){
    _y+=_vy;
    }

    if(_life <= 0){
      _shouldDie = true;
    }
  }

  public boolean shouldDie(){return _shouldDie;}
  public int width(){return 16;}
  public int height(){return 16;};
  public float x(){return _x;}
  public float y(){return _y;}
  public EntityTypes type(){return EntityTypes.Enemy;}
  //TODO
  public void callCollidingEvent(EntityTypes type){
    if(type == EntityTypes.Shock){
      _life -= 2;
    }
  };
}
interface Entity{
  public void update();
  public void draw();
  public boolean shouldDie();
  public void setStage(Stage s);
  public int width();
  public int height();

  public float x();
  public float y();
  
  public EntityTypes type();
  public void callCollidingEvent(EntityTypes type);
}
enum EntityTypes{
  Hero, Enemy, Shock
}

class Game {
  Stage _stage;
  ;

  private CollisionDetector _collisionDetector;
  private List<Entity> _entities = new ArrayList<Entity>();
  private List<Entity> _effects  = new ArrayList<Entity>();
  private List<Entity> _enemies  = new ArrayList<Entity>();
  private Hero _hero;
  private boolean _isGameover;
  private boolean _isGoal;
  Animation _hpback, _hpfront;

  public void setup() {
    _isGameover = false;
    _isGoal = false;

    _collisionDetector = new CollisionDetector();

    List<Entity> loadedEnemies = new ArrayList<Entity>();
    _stage = (new StageLoader()).loadStage("stage.png", loadedEnemies);

    setupEntities(loadedEnemies);
   
    _hpback=new Animation("HPIro", 5);
    _hpfront=new Animation("HPCabar", 6);
  }

  public void setupEntities(List<Entity> enemyList) {
    _entities.clear();
    _enemies.clear();
    _effects.clear();

    _hero = new Hero();
    
    _entities.add(_hero);
    _enemies.addAll(enemyList);
    _entities.addAll(_enemies);
    for (Entity entity : _entities) {
      entity.setStage(_stage);
    }
  }

  public void update() {
    updateEntities();
  }

  public void updateEntities() {
    _collisionDetector.update(_entities);
    for (Entity entity : _entities) {
      entity.update();
    }

    killEntities();

    //\u30b2\u30fc\u30e0\u30aa\u30fc\u30d0\u306e\u6761\u4ef6\u30c1\u30a7\u30c3\u30af
    //\u4f53\u529b\u5207\u308c
    if (_hero.hitPoint() <= 0) {
      _isGameover = true;
    }
    //\u753b\u9762\u4e0b\u306b\u843d\u4e0b\u3057\u305f\u5834\u5408
    if (_hero.y() > 15*16) {
      _isGameover = true;
    }

    //\u30af\u30ea\u30a2\u6761\u4ef6
    //stage\u306e\u53f3\u7aef\u306b\u305f\u3069\u308a\u7740\u304f
    if (_stage.width()*16 < _hero.x()) {
      _isGoal = true;
    }
  }

  public void killEntities(){
    List<Entity> newList = new ArrayList<Entity>();
    for(Entity entity: _entities){
      if(!entity.shouldDie()){
        newList.add(entity);
      }
    }
    _entities = newList;
  }

  public void draw(){
    pushMatrix();
    translate(20*8, 15*8);
    resources.draw("back3.png");
    popMatrix();

    pushMatrix();
    translate(PApplet.parseInt(-_hero.x()+16*10), 0);
    _stage.draw();
    drawEntities();
    popMatrix();
    drawStatus();
   if (_hero.hitPoint()>=50) {
      translate(64, 8);
      _hpback.draw(0);
      _hpfront.draw(0);
    }
    if (_hero.hitPoint()<50 && _hero.hitPoint()>=40) {
      translate(64, 8);
      _hpback.draw(1);
      _hpfront.draw(1);
    }
    if (_hero.hitPoint()<40 && _hero.hitPoint()>=30) {
      translate(64, 8);
      _hpback.draw(2);
      _hpfront.draw(2);
    }
    if (_hero.hitPoint()<30&&_hero.hitPoint()>=20) {
      translate(64, 8);
      _hpback.draw(3);
      _hpfront.draw(4);
    }
    if (_hero.hitPoint()<20) {
      translate(64, 8);
      _hpback.draw(4);
      _hpfront.draw(5);
    }
  }

  public void drawStatus() {
    //TODO
  }

  public void drawEntities() {
    for (Entity entity : _entities) {
      pushMatrix();
      translate(PApplet.parseInt(entity.x()), PApplet.parseInt(entity.y()));
      entity.draw();
      popMatrix();
    }
  }

  public void keyPressed(char k, int kCode) {
    _hero.keyPressed(k, kCode);
    if(k == 'z'){
      _entities.add(new Shock(_hero.x()+8, _hero.y()+8));
    }
  }

  public void keyReleased(char k, int kCode) {
    _hero.keyReleased(k, kCode);
  }

  public boolean isGameover() {
    return _isGameover;
  }
  public boolean isGoal() {
    return _isGoal;
  }
}
// Hero hero;

// void setup(){
// hero= new Hero();
//  size(1024,768);
// hero.setup();
// }
// void draw(){
// background(255);
// hero.draw();
// }
//
// void keyRressed(){
// hero.keyPressed();
// }

class Hero implements Entity{
  boolean _leftKey,_rightKey,_panchKey,w,_kickKey,_jumpKey,lr=true;
  boolean _enableToJumped = false;

  Animation _animationRun;
  Animation _animationPanch;
  Animation _animationKick;
  int _animationCounter;

  Hero(){
    _animationRun   = new Animation("player_run", 4);
    _animationPanch = new Animation("player_panch", 2);
    _animationKick  = new Animation("player_kick", 2);
    _animationCounter = 0;
  }

  private int _hitPoint = 50;

  public int hitPoint(){return _hitPoint;}

  public void setup(){
  }

  private Stage _stage;
  public void setStage(Stage s){
    _stage = s;
  };

  float _fy = 0.1f, _vy = 0;

  public void update(){
    if(_stage.fieldTileType((int)((_x+8)/16.0f), (int)((_y+8.0f+8.0f)/16.0f)) != TileType.Block){
      _enableToJumped = false;
      _vy += 0.2f;
    }else{
      _enableToJumped = true;
      _vy = 0; 
    }

    if(_rightKey==true){
      if(_stage.fieldTileType((int)((_x+16.0f)/16), (int)(_y+8)/16) != TileType.Block){
        _x+=2;
      }
    }
    if(_leftKey==true){
      if(_stage.fieldTileType((int)((_x)/16), (int)(_y+8)/16) != TileType.Block){
        _x-=2;
      }
    }
    if(_panchKey==true){
      // if(lr==true){
      //   image(hito4,_x,313);
      // }else if(lr == false){
      //   image(hito5,_x,313);
      // }
    }


    if(_jumpKey && _enableToJumped){
      if(_stage.fieldTileType((int)(_x/16), (int)(_y-16)/16) != TileType.Block){
        _vy -= 3;
      }else{
        _vy = 0;
      }
    }
    _y+=_vy;
    // else if(w==true){
    //   // if(lr==true){
    //   //   image(hito6,_x,463);
    //   // }else if(lr == false){
    //   //   image(hito7,_x,463);
    //   // }
    // }else if(_kickKey==true){
    //   if(lr==true){
    //     image(hito8,_x,463);
    //   }else if(lr == false){
    //     image(hito9,_x,463);
    //   }
    // }else if(_jumpKey==true){
    //   if(lr==true){
    //     image(hito10,_x,_y);
    //   }else if(lr == false){
    //     image(hito11,_x,_y);
    //   }
    // }
    // else { 
    //   image(hito1,_x,463);
    // }
  }

  public boolean shouldDie(){return false;}

  public int width(){return 16;}
  public int height(){return 16;};

  private float _x = 0, _y = 16*5;

  public float x(){return _x;}
  public float y(){return _y;}

  public EntityTypes type(){return EntityTypes.Hero;}
  public void callCollidingEvent(EntityTypes type){
    if(type == EntityTypes.Enemy){
      _hitPoint -= 1;
    }
  };

  public void draw() {
    
    if(_rightKey || _leftKey){
      _animationCounter += 1;
    }

    if(_leftKey){
      pushMatrix();
      scale(-1, 1);
      _animationRun.draw((_animationCounter/4)%4);
      popMatrix();
    }else{
      _animationRun.draw((_animationCounter/4)%4);
    }

    // if(_rightKey==true){
    //   _x+=4;
    //   image(hito2,_x,463);
    // }else if(_leftKey==true){
    //   _x-=4;
    //   image(hito3,_x,463);
    // }else if(_panchKey==true){
    //   if(lr==true){
    //     image(hito4,_x,313);
    //   }else if(lr == false){
    //     image(hito5,_x,313);
    //   }
    // }
    // else if(w==true){
    //   if(lr==true){
    //     image(hito6,_x,463);
    //   }else if(lr == false){
    //     image(hito7,_x,463);
    //   }
    // }else if(_kickKey==true){
    //   if(lr==true){
    //     image(hito8,_x,463);
    //   }else if(lr == false){
    //     image(hito9,_x,463);
    //   }
    // }else if(_jumpKey==true){
    //   if(lr==true){
    //     image(hito10,_x,_y);
    //   }else if(lr == false){
    //     image(hito11,_x,_y);
    //   }
    // }
    // else { 
    //   image(hito1,_x,463);
    // }
  }

  public void keyPressed(char k, int kCode) {
    if (k == CODED) {      // \u30b3\u30fc\u30c9\u5316\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u304c\u62bc\u3055\u308c\u305f
      if (kCode == RIGHT) {    // \u30ad\u30fc\u30b3\u30fc\u30c9\u3092\u5224\u5b9a
        _rightKey=true;
      } else if (kCode == LEFT) {
        _leftKey=true;
      }
      if (kCode == DOWN) {
        _kickKey=true;
      }else if(kCode == UP){
        _jumpKey=true;
      }
    }

    if(k =='x'||k=='X'){ 
      _rightKey=false;
      _leftKey=false;
      _panchKey=true;
    }else if(k=='z'||k=='Z'){
      _rightKey=false;
      _leftKey=false;
      _panchKey=false;
      w=true;
    }
  }

  public void keyReleased(char k, int kCode){
    if (k == CODED) {      // \u30b3\u30fc\u30c9\u5316\u3055\u308c\u3066\u3044\u308b\u30ad\u30fc\u304c\u62bc\u3055\u308c\u305f
      if (kCode == RIGHT) {    // \u30ad\u30fc\u30b3\u30fc\u30c9\u3092\u5224\u5b9a
        _rightKey=false;
      } else if (kCode == LEFT) {
        _leftKey=false;
      }
      if (kCode == DOWN) {
        _kickKey=false;
      }else if(kCode == UP){
        _jumpKey=false;
      }
    }
  }
}
  


class Resources{
  Minim minim;
  
  public void draw(String name){
    if(!_images.containsKey(name)){
      _images.put(name, loadImage(name));
    }
    
    PImage image = _images.get(name);
		int shiftX = -image.width/2;
		int shiftY = -image.height/2;
    image(_images.get(name), shiftX, shiftY);
  };
  
  public void draw(String name, int x, int y){
    if(!_images.containsKey(name)){
      _images.put(name, loadImage(name));
    }
    
    PImage image = _images.get(name);
    image(_images.get(name), x, y);
  };
  
  public int width(String name){
    if(!_images.containsKey(name)){
      _images.put(name, loadImage(name));
    }
    return _images.get(name).width;
  }
  
  public int height(String name){
    if(!_images.containsKey(name)){
      _images.put(name, loadImage(name));
    }
    return _images.get(name).height;
  }
  
  private HashMap<String, PImage> _images = new HashMap<String, PImage>();
  
  public void loop(String name){
    try{
      if(!_sounds.containsKey(name)){
        _sounds.put(name, minim.loadFile(name, 2048));
      }
      _sounds.get(name).loop();
    }catch(Exception e){
      
    }
  }
  public void play(String name){
    try{
      if(!_sounds.containsKey(name)){
        _sounds.put(name, minim.loadFile(name, 2048));
      }
      _sounds.get(name).play();
    }catch(Exception e){
      
    }
  }
  
  public void close(String name){
    try{
      if(!_sounds.containsKey(name)){
        _sounds.put(name, minim.loadFile(name, 2048));
      }
      _sounds.get(name).close();
      _sounds.remove(name);
    }catch(Exception e){
      
    }
  }
  
  public void trigger(String name){
    try{
    if(!_samples.containsKey(name)){
      _samples.put(name, minim.loadSample(name, 2048));
    }
      _samples.get(name).trigger();
    }catch(Exception e){
      
    }
  }
  
  private HashMap<String, AudioSample> _samples = new HashMap<String, AudioSample>();
  private HashMap<String, AudioPlayer> _sounds = new HashMap<String, AudioPlayer>();
  
  public void close(){
    try{
      for (Map.Entry o : _sounds.entrySet()) {
        _sounds.get(o.getKey()).close();
      }

      minim.stop();
      
    }catch(Exception e){

    }
  }
}
class SampleEntity implements Entity{
  private Stage _stage;
  public void setStage(Stage s){
    _stage = s;
  };
  public void update(){};
  public void draw(){};
  public boolean shouldDie(){return false;}
  public int width(){return 16;}
  public int height(){return 16;};
  public float x(){return 0;}
  public float y(){return 0;}
  public EntityTypes type(){return EntityTypes.Hero;}
  public void callCollidingEvent(EntityTypes type){};

}
class Shock implements Entity{
  private Animation _animation;
  private float _x, _y;
  private float _age;

  Shock(float x, float y){
    _x = x;
    _y = y;
    _age = 8;
    _animation = new Animation("effect_", 8);
  }

  private Stage _stage;
  public void setStage(Stage s){
    _stage = s;
  };

  public void update(){
    _x += 10;
    if(_age <= 0){
      _shouldDie = true;
    }
    _age--;
  };

  public void draw(){
    pushMatrix();
    scale(-1, 1);
    _animation.draw((int)(7-(_age)));
    popMatrix();
  };

  private boolean _shouldDie = false;
  public boolean shouldDie(){return _shouldDie;}
  public int width(){return 16;}
  public int height(){return 16;};
  public float x(){return _x;}
  public float y(){return _y;}
  public EntityTypes type(){return EntityTypes.Shock;}
  public void callCollidingEvent(EntityTypes type){};
}
//

class Stage{
  private int _cw = 20;
  private int _ch = 15;

  Stage(int w, int h){
    _cw = w;
    _ch = h;
    _field = new Tile[_ch][_cw];
    init();
  };

  //
  private Tile[][] _field;

  public void fieldTileType(int x, int y, TileType t){
    if(x<0 || _cw <= x || y<0 || _ch <= y){
      return;
    }
    _field[y][x].type = t;
  }

  public TileType fieldTileType(int x, int y){
    if(x<0 || _cw <= x || y<0 || _ch <= y){
      return TileType.Empty;
    }
    return _field[y][x].type;
  }

  public int width(){return _cw;}
  public int height(){return _ch;}

  float vx = 3,vy = 0;
  boolean mouseReleased=true;
  public void setup(){
  }

  public void draw(){
    for(int y = 0; y < _ch; y++){
      for(int x = 0;x < _cw; x++){
        pushMatrix();
        translate(x*16, y*16);
        _field[y][x].draw();
        popMatrix();
      }
    }
  }

  public void init(){
    for(int y = 0; y < _ch; y++){
      for(int x = 0;x<_cw;x++){
          _field[y][x] = new Tile(TileType.Empty);
      }
    }
  }
}
class StageLoader{
  public Stage loadStage(String path, List<Entity> enemyList){
    PImage img = loadImage(path);
    Stage stage = new Stage(img.width, img.height);

    for(int y = 0; y < img.height; y++){
      for(int x = 0;x < img.width; x++){
        int c = img.get(x, y);
        
        if(c == 0xff000000){
          stage.fieldTileType(x, y, TileType.Block);
        }else if(c == 0xffFFFFFF){
        }else{
          //Enemy
          if(c == 0xffFF0000){
            enemyList.add(new Enemy(x*16, y*16, EnemyType.Dog));
          }
          if(c == 0xff00FF00){
            enemyList.add(new Enemy(x*16, y*16, EnemyType.Bird));
          }
        }


        //TODO add dog 
        //TODO add bird
        //TODO add human
        //TODO add recovery
      }
    }
    return stage;
  }
}
enum TileType{
  Empty,
  Block
}

class Tile{
  TileType type;
  private Animation _animation;

  Tile(TileType t){
    type = t;
    //TODO \u8907\u6570\u306e\u30bf\u30a4\u30eb\u306b\u5bfe\u5fdc
    _animation = new Animation("floor2");
  }

  public void draw(){
    //TODO \u8907\u6570\u306e\u30bf\u30a4\u30eb\u306b\u5bfe\u5fdc
    if(type == TileType.Block){
      _animation.draw();
    }
  }
}
class Vibrator{
  float mass = 1f;
  float spring = 1f;
  float damper = 1f;
  float deltaT= 1f/30f;
  float x = 0;
  float v = 0;
  
  public void addForce(float f){
    v = v + f/mass*deltaT;
  }
  
  public void update(){
    addForce(-x * spring);
    addForce(-v * damper);
    x = x + v*deltaT;
  };
}
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "maingame" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
