/*

Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.

Use and copying of this software and preparation of derivative works based
upon this software are permitted.  Any distribution of this software or
derivative works must comply with all applicable United States export control
laws.

This software is made available AS IS, and Xerox Corporation makes no warranty
about the software, its performance or its conformity to any specification.

|<---            this code is formatted to fit into 80 columns             --->|
|<---            this code is formatted to fit into 80 columns             --->|
|<---            this code is formatted to fit into 80 columns             --->|

Debug.java
Part of the Spacewar system.

*/

package spacewar;

import java.awt.Menu;
import java.awt.CheckboxMenuItem;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.Dimension;

/**
 * This aspect specifies debugging information to be output to the
 * information window.
 *
 * When the debug aspect is compiled in the Frame menu has several checkbox
 * items that can be used to control the amount of tracing information
 * displayed.  (By default the first three are off, because they generate
 * so much information.)
 *
 * There are two reasons to gather all this debugging code into an aspect
 * like this:
 *
 *   (1) It makes it easier to understand when it is all in one place.
 *
 *   (2) It means that we can "plug and debug".  We can enable/disable
 *       the debugging code simply by weaving or not weaving this
 *       aspect in.
 *
 * All in all, this is a lot better than the usual practice of writing
 * complex debugging code and then deleting it when the bug is found,
 * only to regret it a month later when a related bug surfaces.  (Or even
 * the same bug!)
 *
 * This file also defines a class InfoWin, which it uses to display all the
 * debugging information.
 */
aspect Debug {

    private static InfoWin infoWin = new InfoWin();

    private static Menu menu = new Menu("Debug");

    private static CheckboxMenuItem traceConstructors =
        new CheckboxMenuItem("trace constructors", false);
    private static CheckboxMenuItem traceInitializations =
        new CheckboxMenuItem("trace initializations", false);
    private static CheckboxMenuItem traceMethods =
        new CheckboxMenuItem("trace methods", false);
    private static CheckboxMenuItem traceClockTick =
        new CheckboxMenuItem("trace clock tick", false);
    private static CheckboxMenuItem traceRegistry =
        new CheckboxMenuItem("trace registry", true);
    private static CheckboxMenuItem traceFireCollideDamage =
        new CheckboxMenuItem("trace fire, collide, damage", true);

    after() returning (SWFrame frame): call(SWFrame+.new(..)) {
        menu.add(traceConstructors);
        menu.add(traceInitializations);
        menu.add(traceMethods);
        menu.add(traceClockTick);
        menu.add(traceRegistry);
        menu.add(traceFireCollideDamage);
        frame.getMenuBar().add(menu);
    }

    /*
     * all constructors
     */
    pointcut allConstructorsCut(): 
	call((spacewar.* && !(Debug+ || InfoWin+)).new(..));

    before(): allConstructorsCut() {
        if (traceConstructors.getState()) {
            infoWin.println("begin constructing " + thisJoinPoint.getSignature());
        }
    }

    after() returning: allConstructorsCut() {
        if (traceConstructors.getState()) {
            infoWin.println("done constructing " + thisJoinPoint.getSignature());
        }
    }

    /*
     * All dynamic initializations
     */
    pointcut allInitializationsCut(): 
	initialization((spacewar.* && !(Debug+ || InfoWin+)).new(..));

    before(): allInitializationsCut() {
        if (traceConstructors.getState()) {
            infoWin.println("begin initializing " + thisJoinPoint.getSignature());
        }
    }
    after() returning : allInitializationsCut() {
        if (traceConstructors.getState()) {
            infoWin.println("done initializing " + thisJoinPoint.getSignature());
        }
    }

    /*
     * all methods
     */
    pointcut allMethodsCut(): 
        execution(* (spacewar.* && !(Debug+ || InfoWin+)).*(..));

    before(): allMethodsCut() {
        if (traceMethods.getState()) {
            infoWin.println("entering " + thisJoinPoint.getSignature());
        }
    }
    after() returning : allMethodsCut() {
        if (traceMethods.getState()) {
            infoWin.println("exiting " + thisJoinPoint.getSignature());
        }
    }

    /*
     * clock ticks
     */
    after(Object obj) returning :
        (target(obj) && (target(Game) ||
			 target(Registry) ||
			 target(SpaceObject)))
        && call(void clockTick()) {
        if (traceClockTick.getState())
            infoWin.println("ticking " + obj);
    }

    /*
     * registry contents
     */
    after(Registry registry) returning :
        target(registry) && (call(void register(..)) ||
			     call(void unregister(..))) {
        if (traceRegistry.getState())
            infoWin.println(registry.getTable().size() +
                            " space objects in the registry.");
    }

    /*
     * fire, collide, damage
     */
    after() returning : call(void Ship.fire()) {
        if (traceFireCollideDamage.getState())
            infoWin.println("firing");
    }

    after(Ship ship, SpaceObject obj) returning :
	call(void Ship.handleCollision(SpaceObject)) && target(ship) && args(obj) {
        if (traceFireCollideDamage.getState())
            infoWin.println(ship + " collides with " + obj);
    }

    after(Ship shipA, Ship shipB) returning :
        execution(void Ship.bounce(Ship, Ship)) && args(shipA, shipB) {
        if (traceFireCollideDamage.getState())
            infoWin.println(shipA + " bounces with " + shipB);
    }

    before(Ship ship, double amount):
	call(void Ship.inflictDamage(double)) && target(ship) && args(amount) {
        if (traceFireCollideDamage.getState())
            if (amount > 0)
                infoWin.println(ship + "gets " +
                                amount + " damage (" +
                                ship.getDamage() + ")");
    }

}

class InfoWin {
    private Frame    frame;
    private TextArea info;

    InfoWin() {
        frame = new Frame("debugging info for spacewar game");
        info = new TextArea();
        info.setEditable(false);

        Dimension screenSize = frame.getToolkit().getScreenSize();
        frame.setSize(250, 600);
        frame.setLocation(screenSize.width - 250, 0);
        frame.add(info);
        frame.show();
        frame.toFront();
    }

    void clear() {
        info.setText("");
    }

    void println(String line) {
        info.append(line + "\n");
    }

    void print(String line) {
        info.append(line);
    }
}
