package sds.assemble.controlflow;

import sds.classfile.bytecode.BranchOpcode;
import sds.classfile.bytecode.OpcodeInfo;
import sds.classfile.bytecode.Opcodes;
import sds.classfile.bytecode.SwitchOpcode;

import static sds.classfile.bytecode.MnemonicTable.monitorenter;
import static sds.classfile.bytecode.MnemonicTable.monitorexit;

/**
 * This enum class is for type of
 * {@link CFNode <code>CFNode</code>}.
 * @author inagaki
 */
public enum CFNodeType {
	Normal,
	Entry,
	Exit,
	Switch,
	StringSwitch,
	SynchronizedEntry,
	SynchronizedExit,
	LoopEntry,
	LoopExit,
	End;
	
	/**
	 * returns type of control flow node.
	 * @param opcodes opcode sequence
	 * @param end end of opcode
	 * @return node type
	 */
	public static CFNodeType getType(Opcodes opcodes, OpcodeInfo end) {
		if(hasSomeSwitchOpcode(opcodes)) {
			return StringSwitch;
		}

		CFNodeType type;
		if((type = searchType(end)) != Normal) {
			return type;
		}
		
		// processing for for-each statement.
		int index = 0;
		int[] keys = opcodes.getKeys();
		while((index < keys.length) && (type = searchType(opcodes.get(keys[index]))) == Normal) {
			index++;
		}
		return type;
	}

	private static boolean hasSomeSwitchOpcode(Opcodes opcodes) {
		int count = 0;
		for(OpcodeInfo info : opcodes.getAll()) {
			if(info instanceof SwitchOpcode) {
				count++;
			}
		}
		return count > 1;
	}
	
	private static CFNodeType searchType(OpcodeInfo op) {
		if(op instanceof BranchOpcode) {
			BranchOpcode branch = (BranchOpcode)op;
			switch(branch.getOpcodeType()) {
				case jsr:
				case jsr_w:
					// todo
					break;
				case _goto:
				case goto_w:
					if(branch.getBranch() > 0) return Exit;
					else                       return LoopExit;
				default:
					return Entry;
			}
		} else if(op instanceof SwitchOpcode) {
			return Switch;
		} else if(op.getOpcodeType() == monitorenter) {
			return SynchronizedEntry;
		} else if(op.getOpcodeType() == monitorexit) {
			return SynchronizedExit;
		}
		return isReturn(op) ? End : Normal;
	}
	
	private static boolean isReturn(OpcodeInfo op) {
		switch(op.getOpcodeType()) {
			case _return:
			case areturn:
			case ireturn:
			case freturn:
			case lreturn:
			case dreturn: return true;
			default:      return false;
		}
	}
}

@@ -1450,312 +1450,36 @@
 %0A%09%09%09
-BranchOpcode branch = (BranchOpcode)op;%0A%09%09%09switch(branch.getOpcodeType()) %7B%0A%09%09%09%09case jsr:%0A%09%09%09%09case jsr_w:%0A%09%09%09%09%09// todo%0A%09%09%09%09%09break;%0A%09%09%09%09case _goto:%0A%09%09%09%09case goto_w:%0A%09%09%09%09%09if(branch.getBranch() %3E 0) return Exit;%0A%09%09%09%09%09else                       return LoopExit;%0A%09%09%09%09default:%0A%09%09%09%09%09return Entry;%0A%09%09%09%7D%0A%09%09%7D else 
+searchBranchType(op);%0A%09%09%7D%0A%09%09
 if(o
@@ -1524,30 +1524,27 @@
  Switch;%0A%09%09%7D
- else 
+%0A%09%09
 if(op.getOpc
@@ -1608,14 +1608,11 @@
 %0A%09%09%7D
- else 
+%0A%09%09
 if(o
@@ -1715,24 +1715,346 @@
 : Normal;%0A%09%7D
+%0A%0A%09private static CFNodeType searchBranchType(OpcodeInfo op) %7B%0A%09%09BranchOpcode branch = (BranchOpcode)op;%0A%09%09switch(branch.getOpcodeType()) %7B%0A%09%09%09case jsr:%0A%09%09%09case jsr_w:%0A%09%09%09%09// todo%0A%09%09%09%09break;%0A%09%09%09case _goto:%0A%09%09%09case goto_w:%0A%09%09%09%09if(branch.getBranch() %3E 0) %7B%0A%09%09%09%09%09return Exit;%0A%09%09%09%09%7D%0A%09%09%09%09return LoopExit;%0A%09%09%7D%0A%09%09return Entry;%0A%09%7D
 %0A%09%0A%09private 

add method for searching branch type, and delete else block.