package fr.univParisDiderot.phys;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferStrategy;

/**
 * La classe <code>FramedCanvas</code> permet de créer une fenêtre
 * simple et de dessiner dedans.
 *
 * <P>Voici un exemple d'utilisation de FramedCanvas:
 * <PRE>
 * FramedCanvas dessin = new FramedCanvas(200,200);
 * for (i=0; i<200; i+=4) {
 *   dessin.drawLine(0,i,199,199-i);
 *   dessin.drawLine(i,0,199-i,199);
 *   dessin.repaint();
 * }
 * dessin.discard();
 * </PRE>
 *
 * Noter que les commandes pour dessiner (drawLine, ...) dessinent sur
 * une feuille qui n'est <em>pas</em> visible. Pour l'afficher dans la
 * fenêtre, il faut invoquer la méthode repaint(). De même,
 * clearArea() efface le contenu de la feuille de dessin, mais pas de
 * la fenêtre, ce qui permet de préparer le dessin suivant tout en
 * ayant le dessin précédent à l'écran.
 *
 * @author Adrian Daerr (adrian.daerr'at'univ-paris-diderot.fr),
 * inspiré par du code de Jean-Baptiste Yunes
 * (Jean-Baptiste.Yunes'at'liafa.jussieu.fr)
 * @version 0.1
 */
public class FramedCanvas extends Canvas {
    private JFrame theFrame = null;
    private int width, height;
    private BufferStrategy backing;
    private Graphics backingGraphics;
    private Color fgColor=Color.BLACK, bgColor;
    /** Crée une fenêtre et l'affiche à l'écran.
     *
     * @param width largeur du dessin
     * @param height hauteur du dessin
     */
    public FramedCanvas(int width, int height) {
        setSize(width,height);
        if (theFrame != null) return;
        theFrame = new JFrame("Dessin");
        theFrame.setResizable(false);
        theFrame.getContentPane().add((Canvas)this);
        theFrame.pack();
        theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        theFrame.setVisible(true);
        createBackingStore();
        bgColor = UIManager.getColor("Panel.background");
        if (bgColor == null) bgColor = Color.WHITE;
    }
    /** Crée une fenêtre et l'affiche à l'écran.
     *
     * @param width largeur du dessin
     * @param height hauteur du dessin
     * @param bgColor couleur de fond (utilisée par clearArea())
     */
    public FramedCanvas(int width, int height, Color bgColor) {
        this(width,height);
        this.bgColor = bgColor;
    }
    /** Change la taille de la fenêtre. Change aussi la feuille de dessin.
     *
     * @param width largeur du dessin
     * @param height hauteur du dessin
     */
    public void setSize(int width,int height) {
        super.setSize(width,height);
        this.width = width;
        this.height = height;
        if (isDisplayable()) createBackingStore();
    }
    private void createBackingStore() {
        createBufferStrategy(2);
        backing = getBufferStrategy();
        backingGraphics = backing.getDrawGraphics();
    }
    /** Donne les dimensions actuelles de la feuille de dessin. */
    public Dimension getPreferredSize() {
        return new Dimension(width,height);
    }
    /** Paint la feuille de dession à l'écran; ne pas invoquer
     * directement, utiliser repaint() qui invoquera cette méthode
     * avec le bon paramètre <code>g</code>. */
    public void paint(Graphics g) {
        update(g);
    }
    /** Paint la feuille de dession à l'écran; ne pas invoquer
     * directement, utiliser repaint() qui invoquera cette méthode
     * avec le bon paramètre <code>g</code>. */
    public void update(Graphics g) {
        if (backing == null) createBackingStore();
        // ligne suivante pose pb si backingGraphics est utilisé entre
        // ici et la dernière ligne de la méthode; synchroniser?
        //if (backingGraphics != null) backingGraphics.dispose();
        if (backing.contentsRestored())
            System.err.println("backing buffer contents restored");
        backing.show();
        if (backing.contentsLost())
            System.err.println("backing buffer contents lost");
        backingGraphics = backing.getDrawGraphics();
    }
    /** Dessine une ligne de (x0,y0) à (x1,y1) dans la couleur
     * actuelle. L'origine se situe dans le coin supérieur gauche.
     *
     * @see #setColor
     */
    public void drawLine(int x0,int y0,int x1, int y1) {
        if (backingGraphics==null) return;
        backingGraphics.drawLine(x0,y0,x1,y1);
    }
    /** Donne au point (x,y) la couleur actuelle.
     *
     * @see #setColor
     */
    public void drawPoint(int x,int y) {
        drawLine(x,y,x,y);
    }
    /** Ajuste la couleur actuelle. */
    public void setColor(Color c) {
        fgColor = c;
        if (backingGraphics==null) return;
        backingGraphics.setColor(c);
    }
    /** Ajuste la couleur actuelle au mélange RVB donné.
     *
     * @param c1 intensité du rouge entre 0 (absent) et 255 (saturé)
     * @param c2 intensité du vert
     * @param c3 intensité du bleu
     */
    public void setColor(int c1,int c2,int c3) {
        fgColor = new Color(c1,c2,c3);
        if (backingGraphics==null) return;
        backingGraphics.setColor(fgColor);
    }
    /** Équivaut à un appel de setColor(level, level, level).
     *
     * @see #setColor
     */
    public void setGray(int level) {
        setColor(level, level, level);
    }
    /* Retourne la couleur actuelle. */
    public Color getColor() { return fgColor; }
    /** Efface la feuille de dessin. */
    public void clearArea() {
        if (backingGraphics==null) return;
        //backingGraphics.clearRect(0,0,width,height);
        backingGraphics.setColor(bgColor);
        backingGraphics.fillRect(0,0,width,height);
        backingGraphics.setColor(fgColor);
    }
    /** Renvoie une référence à la feuille de dessin. Permet de faire
     * des manipulations directes au-delà de ce qui est possible à
     * travers les méthodes de FramedCanvas (drawLine etc.). */
    public Graphics getBackingGraphics() { return backingGraphics; };
    /** Détruit la fenêtre et le dessin qu'elle contient. */
    public void discard() {
        if (theFrame==null) return;
        theFrame.setVisible(false);
        theFrame.dispose();
        theFrame=null;
    }
    /** Ne fait rien pendant une durée déterminée. */
    public static void sleep(int millisecondes) {
        try {
            Thread.sleep(millisecondes);
        } catch(InterruptedException ie) {}
    }
}