/**
 * 
 */
package dspl.backend.x86;

import dspl.intermediate.variable.Variable;

/**
 * Utility class for various common formatting operations required by the various x86***InstructionProcessors
 * @author David
 */
public class X86AsmFormatter {
	
	 /**
	 * Returns the representation that should be used for the variable in assmebly<p>
	 * The intent on adding this it allow for the future possibility of supporting the different
	 * ASM representations for assembly<br>
	 * Also to allow for changes in exactly how a variable's location, as it should be referenced within
	 * the asm file to change.<p>
	 * For example, if variables stored on the stack are supported, an additional field might be added to variable
	 * to store it's stack offset (i.e. "-4(%ebp)")
	 * @param v
	 * @return
	 */
	public static String getVariableRepresentation(Variable v) {
		if (v.isConstant()) {
			return new String("$" + v.getIdentifier()); 
		}
		else if (v.isElement()) {
			Variable parentVar, indexVar; 
			parentVar = v.getParentVariable();
			indexVar = v.getIndexVariable(); 
			if (!indexVar.isConstant()) { 
				throw new Error("Non-constant array indexing not supported"); 
			}
			else {
				return new String("");
			}
		}
		else {
			return v.getIdentifier();
		}
	}
	
	/**
	 * Returns representation that should be used in an x86 assmebly file
	 * for an integer constant<Br>
	 * Created originally for getting representation for array sizes
	 * which is used in loops. 
	 * @param x
	 * @return
	 */
	public static String getConstantRepresentation(int x) { 
		return "$" + x; 
	}
	
	/**
	 * Returns the string representation of an element within
	 * an array. Uses the from arrayIdentifier(,index,sizeType)
	 * @param v
	 * @param index
	 * @return
	 */
	public static String getArrayElement(Variable v, String index) { 
		return v.getIdentifier() + "(," + index + "," + v.getDataTypeByteSize() + ")"; 
	}
	
	/**
	 * Returns the apprioprate move instruction based on the variable's size<p>
	 * 1 byte  = movb
	 * 2 bytes = movw
	 * 4 bytes = movl 
	 * @param v
	 * @return
	 */
	public static String getMoveInstruction(Variable v) { 
		return getMoveInstruction(v.getDataTypeByteSize());
	}
	
	public static String getMoveInstruction(int size) { 
		switch (size) {
		case 1:
			return "movb";
		case 2:
			return "movw"; //note, no datatype has size 2 right now, but just incase we add shorts later
		case 4:
			return "movl"; 
		default:
			throw new Error("Unable to find proper move instruction for data type of byte size " + size); 
		}
	}
	
	
	/**
	 * Returns the appriopriate add instruction based on the variable's size<p>
	 * 1 byte  = addb
	 * 2 bytes = addw
	 * 4 bytes = addl 
	 * @param v
	 * @return
	 */
	public static String getAddInstruction(Variable v) { 
		switch (v.getDataTypeByteSize()) {
		case 1:
			return "addb";
		case 2:
			return "addw"; //note, no datatype has size 2 right now, but just incase we add shorts later
		case 4:
			return "addl"; 
		default:
			throw new Error("Unable to find proper add instruction for data type of byte size " + v.getByteSize()); 
		}
	}
	
	
	/**
	 * Returns a string representing the instruction for inst + (b/w/l), depending on size of v
	 * @param inst
	 * @param v
	 * @return the post-fixed instruction String correspinding to the size of Variable v
	 */
	public static String getInstr(String inst, Variable v) { 
		return new String(inst + getSizePostfix(v))	; 
	}
	
	
	/**
	 * Returns the appriopriate size postfix (b,w, or l) for (1,2, or 4) byte sized data type
	 * @param v
	 * @return
	 */
	public static String getSizePostfix(Variable v) {
		switch (v.getDataTypeByteSize()) {
		case 1:
			return "b";
		case 2:
			return "w"; //note, no datatype has size 2 right now, but just incase we add shorts later
		case 4:
			return "l"; 
		default:
			throw new Error("Unable to find proper asm size postfix  for data type of byte size " + v.getByteSize()); 
		}
	}
	
	/**
	 * Returns the appriopriate multiply instruction based on the variable's type<p>
	 * This instruction is only valid for int/byte (signed) or uint/ubyte (unsigned). 
	 * @param v
	 * @return
	 */
	public static String getMultiplyInstruction(Variable v) {
		switch (v.getDataType()) {
		case Variable.DATA_TYPE_BYTE:
		case Variable.DATA_TYPE_INT:
			return "imul" + getSizePostfix(v);
		case Variable.DATA_TYPE_UBYTE:
		case Variable.DATA_TYPE_UINT:
			return "mul" + getSizePostfix(v);
		default:
			throw new Error("Error: getMultiplyInstruction not defined variables that are not byte/int or ubyte/uint"); 
		}
	}
	
	/**
	 * Returns the appriopriate subtract instruction based on the variable's type<p>
	 * This instruction is only valid for int/byte (signed) or uint/ubyte (unsigned). 
	 * @param v
	 * @return
	 */
	public static String getSubtractInstruction(Variable v) {
		switch (v.getDataType()) {
		case Variable.DATA_TYPE_BYTE:
		case Variable.DATA_TYPE_INT:
		case Variable.DATA_TYPE_UBYTE:
		case Variable.DATA_TYPE_UINT:
			return "sub" + getSizePostfix(v);
		default:
			throw new Error("Error: getSubtractInstruction not defined variables that are not byte/int or ubyte/uint"); 
		}
	}
	
	/**
	 * Returns the appriopriate divide instruction based on the variable's type<p>
	 * This instruction is only valid for int/byte (signed) or uint/ubyte (unsigned). 
	 * @param v
	 * @return
	 */
	public static String getDivideInstruction(Variable v) {
		switch (v.getDataType()) {
		case Variable.DATA_TYPE_BYTE:
		case Variable.DATA_TYPE_INT:
			return "idiv" + getSizePostfix(v);
		case Variable.DATA_TYPE_UBYTE:
		case Variable.DATA_TYPE_UINT:
			return "div" + getSizePostfix(v);
		default:
			throw new Error("Error: getDivideInstruction not defined variables that are not byte/int or ubyte/uint"); 
		}
	}
}
