import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.lang.*;
import java.util.*;
import java.lang.reflect.Method;
/**
 * Anything that can move around in space and interact (fluctuate, collide, "hit") with other quantum objects.
 * All moving objects in this game have classes derived from QuantumObject.
 * 
 * Unlike in real physics, QuantumObjects have an intrinsic speed that causes them to move around (implemented in act()).
 * They may choose to fall off the screen when reaching the border.
 * 
 * Special coding magic enables it to have real dynamic method dispatch so that collisions with different classes of quantum objects
 * can be implemented.
 * 
 * @author Jannis Andrija Schnitzer, Martin Schend
 * @version 2011-01-17
 */
public class QuantumObject  extends Actor
{
    protected Vector speed;
    
    /**
     * Contains the mantissa that remains from speed calculations (since Greenfoot only allows it to do pixel-based movements).
     * 
     * @see #move()
     */
    protected Vector mantissa;
    
    /**
     *  Whether the quantum object should fall off the world (i. e. be destroyed) when reaching the edge.
     */
    protected boolean disappear;
    
    /**
     * Can be set to us by any caller within a given game turn. The next time this.act() is called after setting,
     * this object destroys itself.
     */
    protected boolean destroysOnNextTurn = false;
    
    public QuantumObject()
    {
          speed = new Vector(0, 0);
          mantissa = new Vector(0, 0);
          disappear = true;
    }
    
    /**
     * A day in the life of a quantum object:
     *  1. Maybe commit suicide (i. e. destroys itself when destroysOnNextTurn is true)
     *  2. Do quantum object interactions with intersecting objects
     *  3. -- Err... are you missing point 3?
     */
    public void act() 
    {
        move();

        if (destroysOnNextTurn) {
            getWorld().removeObject(this);
            return;
        }        
        
        // quantum fluctuations: interact with objects that hit us.
        ArrayList objectsInRange = (ArrayList)this.getIntersectingObjects(QuantumObject.class);
        destroysOnNextTurn = false;
        
        int size = objectsInRange.size();
        for (int i = 0; i < size; i++)
        {
            Actor otherObject = (QuantumObject)objectsInRange.get(i);
            if (!this.hit(otherObject)) // quantum effects caused us to be destroyed
            {
                destroysOnNextTurn = true;
            }
        }
    } 
    
    public void setSpeed(Vector someSpeed)
    {
           speed = someSpeed;
    }
    public Vector getSpeed()
    {
        return speed;
    }
    
    public void setDestroysOnNextTurn(boolean destroys)
    {
        destroysOnNextTurn = destroys;
    }
    public boolean getDestroysOnNextTurn()
    {
        return destroysOnNextTurn;
    }
    
    /**
     * move. Cause the quantum object to move around based on its speed.
     * 
     * Fun fact: speed values can also be non-integer. Because Greenfoot only allows for pixel-based movement, the mantissa of
     * the speed vector is kept seperately and summed up with the mantissa from the previous turn. That way, e. g. a vertical speed of
     * 2.2 causes the quantum object to move 2 pixels every game turn, and 3 pixels every 5th turn, because after each 5 turns the .2
     * mantissae have added up to 1.
     * 
     * @see #mantissa
     */
    public void move()
    {
        int newX, newY;
        double x_speed, y_speed;
        
        // add old mantissa values
        x_speed = speed.getX()+mantissa.getX();
        y_speed = speed.getY()+mantissa.getY();
        
        newX = (int) Math.floor(x_speed);
        newY = (int) Math.floor(y_speed);
        
        // mantissa only gets the difference between the double values and the
        // int values in newX and newY
        mantissa.set(x_speed-(double)newX, y_speed-(double)newY);
        
        newX += getX();
        newY += getY();
        
        if (disappear &&
            newX >= getWorld().getWidth() || newY >= getWorld().getHeight() || newX < 0 || newY < 0)
        {
                destroysOnNextTurn = true;

        } else {           
            setLocation(newX, newY);
        }
    }
    
    /**
     * hit. Base method hit(Actor). Uses java.lang.reflect to dynamically find out which hit() method of the quantum object
     *      to invoke based on which actual class "actor" has. Dynamic dispatch comes true!
     *      
     * @param actor  The object we are interacting with
     * 
     * @return       boolean. True if this quantum object should survive, false otherwise.
     */
    public boolean hit(Actor actor)
    {
        Method hit = null;
        Class c = actor.getClass();
        if (this.getClass() == c)
            return true;
        while (hit == null && c != null)
        {
            try {
                hit = this.getClass().getMethod("hit", new Class[] { c }); // get hit method for c's class
            } catch (NoSuchMethodException ex) {
                c = c.getSuperclass(); // called when there was no such method; check for c's superclass
            }
        }
        
        if (hit == null && c == null)
        {
            // obviously there's no hit method in this object that wants to handle actor
            return true;
        }

        try {
            return (Boolean)hit.invoke(this, new Object[] { actor });
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return true;
    }
    
    /**
     * strange quantum interaction effects occur... not. This method actually is redundant, but still left in place as an example for
     * how to write hit() methods.
     * 
     * @param q     quantum object that we hit.
     */
    public boolean hit(QuantumObject q)
    {
        return true;
    }
}
// poop.