import processing.core.PApplet;
import processing.core.PImage;
import processing.core.PLibrary;

import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;

import java.util.Hashtable;

public abstract class Game implements Runnable,PLibrary {

  public static final int FRAME_RATE  = 20; // FPS
  public static final int FRAME_DELAY = 1000 / FRAME_RATE;

  protected PApplet a = null;
  
  Thread runThread = null;
  
  public Game() {}
  
  public void setup(PApplet applet) {
    this.a = applet;
    a.noLoop();
    //a.registerCall(this, MOUSE);
    a.registerCall(this, KEY);
    a.registerCall(this, DISPOSE);
    a.registerCall(this, DRAW);
    initialize();
    runThread = new Thread(this);
    runThread.start();
  }
  
  Hashtable cachedImages = new Hashtable();

  public PImage loadImage(String s) {
    Object o = cachedImages.get(s);
    if (o!=null) return (PImage)o;
    PImage i = a.loadImage(s);
    cachedImages.put(s,i);
    return i;
  }

  

  public void initialize() {}
  public abstract void calc();
  public abstract void draw();
  public void key(int id, int code) {}

  public static final int KEY_PRESSED = KeyEvent.KEY_PRESSED;
  public static final int KEY_RELEASED = KeyEvent.KEY_RELEASED;
  public static final int MAX_KEY = 0xFF;
  
  boolean keys[] = new boolean[MAX_KEY];
      
  public void size(int w, int h) {} /* Not used */
  public void pre() {} /* Not used */
  public void post() {} /* Not used */
  public void mouse(MouseEvent me) {} /* Not used */
  public void key(KeyEvent ev) {
    int key = ev.getKeyCode();
    if (key>=0 && key<MAX_KEY) {
      switch (ev.getID()) {
        case KEY_PRESSED:
          keys[key] = true;
          break;
        case KEY_RELEASED:
          keys[key] = false;
          break;
      }
    }
    key(ev.getID(), key);
  }
  
  public void dispose() {
    runThread = null;
  }

  
  
  long lastTime = 0;
  public void run() {
    while (Thread.currentThread() == runThread) {
      long currentTime = System.currentTimeMillis();
      if (lastTime == 0) {
        lastTime = currentTime;
        calc();
        a.redraw();
      }
      long dt = currentTime - lastTime;
      int maxCalc = FRAME_RATE;
      boolean newFrame = false;
      while (dt>=FRAME_DELAY && maxCalc>0) {
        calc();
        newFrame = true;
        dt -= FRAME_DELAY;
        maxCalc--;
      }
      if (newFrame) {
        a.redraw();
        lastTime = currentTime;
      } else if (dt<FRAME_DELAY) {
        a.delay((int)(FRAME_DELAY - dt));
      }
    }
  }
}
