// double_dispatch_ext.C
// ----------------------------------------------------------------
// COMP 290-001: Algorithm Library Design, Lutz Kettner, 01/11/2000
// Example for extendible double dispatch.
// Note, this solution distinguishes between the arguement order of
// the collision calls (ship with station or station with ship). It
// also detects (but at runtime) if collision case is missing. 
// If we had used the symmetrie in the implementation a missing case
// would have send our implementation into an infinite loop (--> stack
// overflow).

#include <iostream>
#include <typeinfo>

using namespace std;

// forward declaration
struct Ship;
struct Station;
struct Asteroid;

struct Game_object {
    virtual void collision( Game_object* other) = 0;
    virtual void collision_rev( Game_object* other) = 0; // reversed arg order
    virtual ~Game_object() {}
};
struct Ship : public Game_object {
    virtual void collision( Game_object* other) {
        // this (of type Ship) collides here with other, 
	// check explicitly for those that we can handle here.
	if ( typeid( Ship) == typeid( *other))
	    cout << "Ship collides with Ship." << endl;
	else
	    other->collision_rev( this);
    }
    virtual void collision_rev( Game_object* other) {
        // error, ship-ship has been tested before. Can occur if
	// some other class missed to implement collision with ships.
	cout << "ERROR: Collision between " << typeid(*other).name()
	     << " and Ship not implemented!" << endl;
    }
};
struct Station : public Game_object {
    virtual void collision( Game_object* other) {
        // this (of type Station) collides here with other, 
	// check explicitly for those that we can handle here.
	if ( typeid( Ship) == typeid( *other))
	    cout << "Station collides with Ship." << endl;
	else if ( typeid( Station) == typeid( *other))
	    cout << "Station collides with Station." << endl;
	else
	    other->collision_rev( this);
    }
    virtual void collision_rev( Game_object* other) {
        // test for collision with ship, otherwise its an error and
	// some other class missed to implement collision with ships.
	if ( typeid( Ship) == typeid( *other))
	    cout << "Ship collides with Station." << endl;
	else 
	    cout << "ERROR: Collision between " << typeid(*other).name() 
		 << " and Station not implemented!" << endl;
    }
};
struct Asteroid : public Game_object {
    virtual void collision( Game_object* other) {
        // this (of type Asteroid) collides here with other, 
	// check explicitly for those that we can handle here.
	if ( typeid( Ship) == typeid( *other))
	    cout << "Asteroid collides with Ship." << endl;
// we missed the test for station on purpose to test the error checking.
	else if ( typeid( Asteroid) == typeid( *other))
	    cout << "Asteroid collides with Asteroid." << endl;
	else
	    other->collision_rev( this);
    }
    virtual void collision_rev( Game_object* other) {
        // test for collision with ship and station, otherwise its an error and
	// some other class missed to implement collision with ships.
	if ( typeid( Ship) == typeid( *other))
	    cout << "Ship collides with Asteroid." << endl;
	else if ( typeid( Station) == typeid( *other))
	    cout << "Station collides with Asteroid." << endl;
	else 
	    cout << "ERROR: Collision between " << typeid(*other).name()
		 << "and Asteroid not implemented!" << endl;
    }
};


int main() {
    Game_object* ship     = new Ship;
    Game_object* asteroid = new Asteroid;
    Game_object* station  = new Station;

    ship->collision( ship);
    ship->collision( station);
    ship->collision( asteroid);
    station->collision( ship);
    station->collision( station);
    station->collision( asteroid);
    asteroid->collision( ship);
    asteroid->collision( station);
    asteroid->collision( asteroid);
}

// EOF //

