
package spacewar;

import java.util.Vector;
import java.util.Enumeration;
import java.awt.Graphics;
import java.awt.Canvas;
import java.awt.Image;
import java.awt.event.KeyListener;

/**
 * The display aspects capture the look and feel of the Game in modular
 * pluggable units.
 *
 * The model is that constructing a concrete subclass of Display attaches that
 * kind of display to the game.  It will Display the game as it goes along.
 * A game can have any number of displays.  Any of the displays will accept
 * keyboard input.
 *
 */

class Display  extends Canvas {

    private static Vector DISPLAYS = new Vector(2);
    private static Vector PLAYERS  = new Vector(2);
    private static Pilot  pilot1, pilot2;

    Game     game;
    SWFrame  frame;
    Image    offImage;
    Graphics offGraphics;

    Game  getGame()   { return game; }
    static Pilot getPilot1() { return pilot1; }
    static Pilot getPilot2() { return pilot2; }

    Display(Game g) {
        super();
        game = g;

        frame = new SWFrame(game, this);
        DISPLAYS.addElement(this);
    }


    void noticeSizeChange() {
        initializeOffImage();
    }

    private void initializeOffImage () {
        int w = getSize().width;
        int h = getSize().height;
        if ( w > 0 & h > 0) {
            offImage = createImage(w, h);
            offGraphics = offImage.getGraphics();
        }
    }

    /*
     * In our double buffering scheme, painting just means copying the buffer
     * to the screen.  The Display aspect draws into the buffer.
     */
    public void paint(Graphics g) {
        if (offImage != null)
            g.drawImage(offImage, 0, 0, null);
    }

    public void update(Graphics g) {
        /*
         * There are 4 steps to this:
         *  - clear the double buffer
         *  - paint the objects into the double buffer
         *  - paint the status into the double buffer
         *  - paint the doublebuffer into the buffer
         */
        offGraphics.setColor(getBackground());
        offGraphics.fillRect(0, 0, getBounds().width, getBounds().height);
        paintObjects(offGraphics);
        paintStatus(offGraphics);
        g.drawImage(offImage, 0, 0, null);
    }

    void paintObjects(Graphics g) { }
    void paintStatus(Graphics g) {}

    static aspect DisplayAspect {

        after (String mode) returning (Game game): call(Game+.new(String)) && args(mode) {
            new Display1(game);
//            new Display2(game);

            if ( mode.equals("1") ) {
                pilot1 = game.newPlayer(1);
            }
            else if ( mode.equals("2") ) {
                pilot1 = game.newPlayer(1);
                pilot2 = game.newPlayer(2);
            }
            else if (mode. equals("demo")) {
                pilot1 = game.newRobot(1);
                pilot2 = game.newRobot(2);
            } else {
                game.error("Invalid mode: " + mode);
                game.quit();
            }
        }


        /*
         * I'm not really sure this belongs here.
         *
         * Being here what it does is makes the Display aspect
         * responsible for having the Players couple up to it.  That's
         * kind of nice, but its a bit incomplete, since Player is
         * really part of the GUI, not part of the core Game.
         *
         * In a future re-factoring this will get worked out better.
         * What will happen is that GUI will be an aspect that has the
         * core GUI.  Each of the different kinds of displays will be
         * aspects that tie themselves in.
         */
        after () returning (Player player): call(Player+.new(..)) {
            Enumeration elements = DISPLAYS.elements();
            while ( elements.hasMoreElements() ) {
                Display display = (Display)elements.nextElement();
                display.addKeyListener(player);
            }
        }

        after() returning (Display display): call(Display+.new(..)) {
            display.noticeSizeChange();
        }

        after(Display display) returning (): call(void setSize(..)) && target(display) {
            display.noticeSizeChange();
        }

        after(): call(void Game.clockTick()) {
            Enumeration elements = DISPLAYS.elements();
            while ( elements.hasMoreElements() ) {
                Display display = (Display)elements.nextElement();
                display.repaint();
            }
        }
    }
}
