import distributed.plugin.runtime.*;
import distributed.plugin.runtime.engine.*;
import java.util.List;

public class DeepFirstTraversal extends Entity{
    // 0~5 are suggested to used for states
    public static final int STATE_IDLE = 0;
    public static final int STATE_VISITED = 1;
    public static final int STATE_DONE = 2;
   
    //6~10 are suggested to used for messages
    private static final int MSG_Token = 7;
    private static final int MSG_Back = 8;
   
    private boolean isInit;
    private List<String> unvisitedPorts;
    private String next;
    private String entry;

    /**
     * by default all nodes are set to non-initiator
     */
    public DeepFirstTraversal() {
        super(STATE_IDLE);
        this.isInit = false;
    }

    /**
     * only initiator will execute method init()
     */
    public void init() {
        unvisitedPorts = this.getPorts();
        isInit = true;
        become(STATE_VISITED);
        goDeep();
    }
   
    public void goDeep() {
        if (!unvisitedPorts.isEmpty()) {
            next = (String) unvisitedPorts.get(0);
            unvisitedPorts.remove(next);
            sendTo(next, MSG_Token);           
            //this.printToConsole("unvisited port list: " + unvisitedPorts);
        } else{
            goBack();
        }
    }
   
    public void goBack(){
        if (isInit){
            become(STATE_DONE);
        }else{
            become(STATE_DONE);
            sendTo(entry, MSG_Back);
        }
    }

    @Override
    public void alarmRing() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void receive(String incomingPort, IMessage message) {
        // TODO Auto-generated method stub
        printToConsole("\n");
        if(unvisitedPorts == null){
            unvisitedPorts=this.getPorts();
        }
       
        int msg = (Integer) message.getContent();
       
        printToConsole(this.getName()+ "("+getState()+")receive message " + msg +" from "
                + this.getNeighbourName(incomingPort));
       
        if (this.getState() == STATE_IDLE) {
            if (msg == MSG_Token){
                this.entry = incomingPort;               
                //this.printToConsole("unvisited ports before removing incoming port: " + unvisitedPorts);
                unvisitedPorts.remove(incomingPort);
                //this.printToConsole("unvisited ports after removing incoming port: " + unvisitedPorts);
                become(STATE_VISITED);
                goDeep();
            }else{
                printToConsole("Error: Idle node should only receive Token message.");
            }
        }else if(this.getState() == STATE_VISITED){
            if (msg ==MSG_Back){
                goDeep();   
            }else{
                printToConsole("Error: Visited node should only receive Back message.");
            }
        }       
    }
}