import java.util.Random;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

public class StackedObject extends MobileSimObject 

{
    // OVERVIEW: A StackedObject is a simulator object that moves in a constant
    // downward direction in addition to moving left and right.
    private boolean inTurn;
    private boolean isFalling;
    private boolean isStacked;
    private boolean isSquare;
    private FallingObject block1, block2, block3;
    
    public StackedObject (FallingObject block1, FallingObject block2, FallingObject block3) 
    //@ensures !isInitialized;
    {
        // will create attached objects here
        isFalling=true;
        inTurn=true;
        isStacked = false;
        isSquare = false;
        this.block1 = block1;
        this.block2 = block2;
        this.block3 = block3;
    }
    
    public Color getColor()
    {
        if (isFalling) {
            return Color.cyan;
        } else {
            return Color.blue;
        }
    } //@nowarn NonNullResult // ESC/Java doesn't know Color constants are not null
    
    public boolean canExecuteTurn(Direction direct) throws RuntimeException
    // Note: requires isInitialized is inherited from SimObject
    // Modifies: this
    // EFFECTS: Moves south.
    //          If the move is successful, return true. If the move fails
    //          because the spot being attempted to move into is already
    //          occupied then return false.
    {
        if (isStacked) {
            return true;
        }
        Grid thisgrid = getGrid ();
        inTurn = true;
        
        int newrow = getRow () + direct.northerlyDirection ();
        int newcol = getColumn () + direct.easterlyDirection ();
        
        if (getGrid ().validLocation (newrow, newcol) ) {
            synchronized (getGrid ()) {
                if (getGrid().isSquareEmpty (newrow, newcol))
                {
                    return true;
                }
                return false;
            }
        }
        return false;
    }
    
    public void executeTurn() throws RuntimeException
    // Note: requires isInitialized is inherited from SimObject
    // Modifies: this
    // EFFECTS: Moves south.
    //          If the move is successful, return true. If the move fails
    //          because the spot being attempted to move into is already
    //          occupied then return false.
    {
        if (isSquare) {
            squareExecuteTurn();
        }
        else {
            Grid thisgrid = getGrid ();
            inTurn = true;
            
            Direction dir = thisgrid.UserDirection();
            
            int newrow = getRow () + dir.northerlyDirection ();
            int newcol = getColumn () + dir.easterlyDirection ();
            if (canExecuteTurn(dir) && block1.canExecuteTurn(dir) && block2.canExecuteTurn(dir) && block3.canExecuteTurn(dir)) {
                block1.executeTurn();
                block2.executeTurn();
                block3.executeTurn();
                setLocation (newrow, newcol);
            }
            else if (canExecuteTurn(new Direction(newrow,getColumn())) && block1.canExecuteTurn(new Direction(newrow,getColumn())) && block2.canExecuteTurn(new Direction(newrow,getColumn())) && block3.canExecuteTurn(new Direction(newrow,getColumn()))) {
                thisgrid.setUserDirection(new Direction(dir.northerlyDirection(), 0));
                block1.executeTurn();
                block2.executeTurn();
                block3.executeTurn();
                setLocation (newrow, 0);
            }
            else {
                isFalling = false;
                block1.isFalling = false;
                block2.isFalling = false;
                block3.isFalling = false;
            }
            if (!isFalling) {
                getGrid().pauseObjects();
                if (getGrid().isSquareEmpty(0,getGrid().numColumns()/2)) {
                    getGrid().setObjectAt(0,getGrid().numColumns()/2, new StackedObject(new FallingObject(), new FallingObject(),new FallingObject()));
                    getGrid().startObjects();
                } else {System.err.println("gameOVER");}
            }
            getGrid().setUserDirection(new Direction(1,0) );
            inTurn = false;
        }
    }
    public boolean isFalling () {
        return isFalling;
    }
    
    public void run ()
    // REQUIRES: this has been initialized

    //    We use also_requires instead of requires, because we are adding a precondition
    //    to an inherited method.  This violates the substitution principle --- subtypes
    //    should make preconditions weaker, not stronger.
    // EFFECTS: The object thread.  If the object is not paused, executes one turn by calling
    //    the executeTurn method, and sleeps for a time and repeats.  If the object is paused,
    //    does nothing (until the object is unpaused).
    {
        //isPaused ()= false;
        setRandomObjects ();
        while (isFalling) {
            if (!isPaused()) {
                executeTurn ();
                delay (100);
            }
        }
    }
    
    public void setRandomObjects (){
        Random random = new Random ();
        int a=0,b=0,c=0,d=0,e=0,f = 0;
        int dir = random.nextInt (2);
        
        switch (dir) {
            //case 0: {a = 0; b = -1; c = 1; d = 0; e = 1; f = 1; isStacked = true; break;}
          //  case 1: {a = 1; b = -1; c = 1; d = 0; e = 0; f = 1; isStacked = true; break;}
            case 0: {a = 0; b = -1; c = 0; d = 1; e = 0; f = 2; isStacked = true;break;}
            case 1: {a = 1; b = 0; c = 0; d = 1; e = 1; f = 1; isStacked = true;isSquare = true;break;}
        }
        
        getGrid().setObjectAt(getRow()+a, getColumn()+b , block1);
        getGrid().setObjectAt(getRow()+c, getColumn()+d , block2);
        getGrid().setObjectAt(getRow()+e, getColumn() +f , block3);
    }
    private void squareExecuteTurn(){
        Grid thisgrid = getGrid ();
        inTurn = true;
        
        Direction dir = thisgrid.UserDirection ();
        int newrow = getRow () + dir.northerlyDirection ();
        int newcol = getColumn () + dir.easterlyDirection ();
        
        if ( block1.canExecuteTurn(dir)&& block3.canExecuteTurn(dir)) {
            block1.executeTurn();
            block3.executeTurn();
            block2.executeTurn();
            setLocation (newrow, newcol);
        }
        else if (block1.canExecuteTurn(new Direction(newrow,getColumn())) && block3.canExecuteTurn(new Direction(newrow,getColumn()))) {
            thisgrid.setUserDirection(new Direction(dir.northerlyDirection(), 0));
            block1.executeTurn();
            block3.executeTurn();
            block2.executeTurn();
            setLocation (newrow, 0);
        }
        else {
            isFalling = false;
            block1.isFalling = false;
            block2.isFalling = false;
            block3.isFalling = false;
        }
        if (!isFalling) {
            getGrid().pauseObjects();
            if (getGrid().isSquareEmpty(0,getGrid().numColumns()/2)) {
                getGrid().setObjectAt(0,getGrid().numColumns()/2, new StackedObject(new FallingObject(), new FallingObject(),new FallingObject()));
                
                getGrid().startObjects();
                
            } else {System.err.println("gameOVER");}
        }
        getGrid().setUserDirection(new Direction(1,0) );
        inTurn = false;
        

    }
}