/*
 * Machine: This class keeps track of the current state of the machine 
 *          and performs transitions between states based on the 
 *          input from the tape.
 */

import java.util.*;

public class Machine {
	
    final int HISTORY_MAX_SIZE = 1000;
    
    public static final int NOT_HALTED = 0;
    public static final int HALTED = 1;
    public static final int ACCEPT = 2;
    public static final int REJECT = 3;

    public Vector vertices; 
    public Vector edges;
    public int steps;

    private String currentSymbol = null;

    private Vertex currentVertex = null;
    
    private Edge lastEdge = null; //the edge taken to enter the current state
    private Edge nextEdge = null;    //the edge that will be taken next
    private TuringIOProcessor iop = new TuringIOProcessor();

    private Tape tape;

    private LinkedList history;

    public Machine(Vector v1, Vector v2, Tape t) {
	vertices = v1;
	edges = v2;
	tape = t;

	currentVertex = (Vertex)vertices.get(0);
	currentSymbol = tape.getCurrentSymbol();
	nextEdge = findEdge(currentVertex, t.getCurrentSymbol());
	history = new LinkedList();
	steps = 0;
    }	

    public Vertex getCurrentVertex() {
	return currentVertex;
    }
    
    public Edge getNextEdge() {
	if(nextEdge == null) {
	    return new Edge(currentVertex.getName(), currentVertex.getName(), 
			    currentSymbol, currentSymbol, 0);
	}
	return nextEdge;
    }

    public void setTape(Tape t) {
	tape = t;
    }
    
    private Vertex findVertex(String name) {
	int i;
	int vsize = vertices.size();
	Vertex v;
	for(i = 0; i < vsize; i++) {
	    v = (Vertex)vertices.get(i);
	    if(name.equals(v.getName())) {
		return v;
	    }
	}
	System.err.println("Error: " + name + " is not a state.");
	return null;
    }
    
    private Edge findEdge(Vertex v, String symbol) {
	int i;
	int esize = edges.size();
	Edge ed;
	for(i = 0; i < esize; i++) {
	    ed = (Edge)edges.get(i);
	    if(v.getName().equals(ed.getOldState()) 
	       && symbol.equals(ed.getOldSymbol())) {
		return ed;
	    }
	}
	return null;
    }
		
    public void resetMachine() {
	currentVertex = (Vertex)vertices.get(0);
	lastEdge = null;
	currentSymbol = tape.getCurrentSymbol();
	nextEdge = findEdge(currentVertex, currentSymbol);
	
	history = new LinkedList();
	TuringMain.repaintMachineArea();
	TuringMain.repaintTapeArea();
	steps = 0;
    }
    
    /* stepForward: This method will perform one transition
     *             based on the current state and tape symbol
     *
     */
    
    
    public void stepForward() {
	boolean symbolChanged;
	
	String state = new String(currentVertex.getName());
	steps++;
	
	currentSymbol = (String)tape.getCurrentSymbol();
	lastEdge = nextEdge;
		
	if(nextEdge != null) {
	    state = nextEdge.getNewState();
	    tape.writeSymbol(nextEdge.getNewSymbol());
	    history.addLast(nextEdge);
	    
	} else {
	    history.addLast(new Edge(currentVertex.getName(), currentVertex.getName(), 
				     currentSymbol, 
				     currentSymbol, 
				     currentVertex.getDirection()));
	    symbolChanged = false;
	}
	
	if(history.size() > HISTORY_MAX_SIZE) {
	    history.removeFirst();
	}
	
	currentVertex = findVertex(state);
    }
	
    public int shiftAndGetHaltStatus() {
	if(currentVertex.getDirection() == Vertex.RIGHT) {
	    tape.shiftRight();
	} else if(currentVertex.getDirection() == Vertex.LEFT) {
	    tape.shiftLeft();
	} else {
	    //in this case, halt the machine
	    nextEdge = null;
	    switch(currentVertex.getHaltStatus()) {
	    case Vertex.ACCEPT: 
		return ACCEPT;	
	    case Vertex.REJECT: 
		return REJECT;
	    default:
		return HALTED;
	    }
	}
	return NOT_HALTED;
    }

    public void updateMachine() {
	currentSymbol = tape.getCurrentSymbol();
	nextEdge = findEdge(currentVertex, currentSymbol);
    }

    
    /* stepBack: restores the machine and tape to the state they were in
     *           in the last step.  Returns false if the stepBack was
     *           unsuccessful (because the history was empty).
     */

    public boolean stepBack() {
	steps--;
	if(steps < 0) {
	    steps = 0;
	}


	//If the current cell was added to the end of the
	//original tape, it is now deleted
	if(history.size() > 0) {
	    Edge ed = (Edge)history.removeLast();   

	    if((tape.getCurrentPosition() == 0  || 
	    	tape.getCurrentPosition() == tape.getSize() - 1) && 
	        tape.getCurrentSymbol().equals(tape.getFillSymbol()) && 
	        tape.getSize() > TuringMain.originalTape.getSize()) {
		
		tape.deleteCell();
	    
	    } else if(currentVertex.getDirection() == Vertex.RIGHT) {
		tape.shiftLeft();
	    } else if(currentVertex.getDirection() == Vertex.LEFT) {
		tape.shiftRight();
	    }
	    
	    tape.writeSymbol(ed.getOldSymbol());
	    
	    currentVertex = findVertex(ed.getOldState());
	    
	    nextEdge = lastEdge;
	    if(history.size() > 0) {
		lastEdge = (Edge)history.getLast();
	    } else {
		lastEdge = null;
	    }
	    return true;
	} else {
	    return false;
	}
    }
}	

