import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics; 
import java.awt.geom.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter; 
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class BasicGameDoubleBuffering {
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
                              }
        });
    }

    private static void createAndShowGUI() {
        System.out.println("Created GUI on EDT? "+
        SwingUtilities.isEventDispatchThread());
        JFrame f = new JFrame("Basic Game");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(new MyPanel());
        f.pack();
        f.setVisible(true);
    } 
}

class MyPanel extends JPanel implements Runnable{

    private int squareX = 50;
    private int squareY = 50;
    private int offsetY = 0;
    private int offsetX = 0;
    private int squareW = 20;
    private int squareH = 20;

    private Thread animator;
    private long numPaintJobs = 0L;
    private Image im = null;
    private Graphics dbg = null;
    
    public MyPanel() {

        setBorder(BorderFactory.createLineBorder(Color.black));
        
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                moveSquare(e.getX(),e.getY());
            }
        });

        addMouseMotionListener(new MouseAdapter() {
            public void mouseDragged(MouseEvent e) {
                moveSquare(e.getX(),e.getY());
            }
        });

        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                moveImage(e);
            }
        });

        setFocusable(true);
        requestFocus();
     
        // facciamo partire il metodo run
   if (animator == null ) {
   animator = new Thread(this);
animator.start();
      }

        }
    
    private void moveSquare(int x, int y) {
       
       if ((squareX!=x) || (squareY!=y)) {
            squareX=x;
            squareY=y;
       }           
        
    }
    
    private void moveImage(KeyEvent ke){
     int kc = ke.getKeyCode();
     System.out.println(kc);
     if ((kc == ke.VK_LEFT)||(kc == ke.VK_KP_LEFT)) {squareX = squareX - 5;}
        if ((kc == ke.VK_RIGHT)||(kc == ke.VK_KP_RIGHT)){squareX = squareX + 5;} 
    }
    
    public Dimension getPreferredSize() {
        return new Dimension(250,200);
    }
    
    private void gameRender(){
    
     if (im == null){
     im = createImage(250, 200);
     if (im == null){
     return;
     } else dbg = im.getGraphics();
     }
    
     // disegno sul contesto grafico
        dbg.setColor(Color.BLUE);
        dbg.fillRect(0,0,250,200);
        dbg.setColor(Color.RED);
        dbg.fillRect(squareX,squareY,squareW,squareH);
        dbg.setColor(Color.WHITE);
        dbg.drawRect(squareX,squareY,squareW,squareH);
        dbg.setColor(Color.BLACK);
        dbg.drawRect(squareX-1,squareY-1,squareW +2,squareH +2 );
        // qualche prova con metodi della classe Graphics2D
        Graphics2D g2 = (Graphics2D) dbg;
        g2.setColor(Color.GREEN);
        g2.draw(new Rectangle2D.Double(10.0,10.0, 50.0,50.0));
        g2.draw(new Line2D.Double(23.0, 12.0, 100.0, 100.0));
        g2.setColor(Color.WHITE);
        g2.drawString("love this",100,100);
        g2.draw(new CubicCurve2D.Double(0.0,0.0,
                                        -10.0, 30.0,
                                         20.0, 90.0,
                                        250.0, 250.0));
        // siccome è un oggetto privato e non può toccarlo
        // nessuno non esiste il rischio che venga toccato
        // quindi non lo butto via alla fine delle operazioni
        // di disegno: me lo tengo e ci disegno sopra tutte
        // le volte
    
     }
    
    
    private void mypaint() {
        Graphics g;
        try{
       g = this.getGraphics();
       g.drawImage(im, 0,0, null);
      
        g.dispose();
        }
        catch(Exception e){
        }
        // stampo tanto per vedere quante volte venga invocato paintComponent
        System.out.println("P #: " + numPaintJobs);
        numPaintJobs++;
    }  
    
    public void run(){
    
     while(true){
     gameUpdate();
     gameRender();
     mypaint();
     try {
     // aspetta 30 ms
        Thread.currentThread().sleep(20L);  
         }catch(InterruptedException ex){}
        }
    
    }
    
    private void gameUpdate(){
     squareX++;
     // prendiamo il resto della divisione intera
     // in modo che squareX non superi 250
     squareX = (squareX)% 250;
     // collochiamo il punto su una parabola
     // che passa per 0,0 e 250,0
     squareY = (int)((-1)*(squareX*(squareX - 250))/150)%200 ;
    }
    
}