import java.io.*;

public abstract class Assembler {

	public static CompiledProgram Assemble(String[] ProgToASM) {
		return Assemble(ProgToASM, false);
	}

	public static CompiledProgram Assemble(String[] ProgToASM, boolean Info) {
		int i = 0, Offset = 0, j = 0, ret = 0, ArrayOfEquivalents[], Prgm[];
		String ArrayOfSymbols[], Str;
		CompiledProgram Ret;
		org = 0;
		Prgm = new int[1024];

		PreprocessedProgram pre = new PreprocessedProgram(ProgToASM, Info);

		String ArrayOfProgToASM[] = pre.PreprocessedSource();

		String ArrayOfProgVariables[] = pre.Variables();
		int ArrayOfProgAddr[] = pre.Addresses();

		String ArrayOfProgEQUs[] = pre.EQUs();
		int ArrayOfProgEquValues[] = pre.EquValues();

		String ArrayOfProgLabels[] = pre.Labels();
		int ArrayOfProgOffsets[] = pre.Offsets();
		
		
		ArrayOfSymbols = MergeStr(ArrayOfProgVariables, ArrayOfProgEQUs, pre.VarSize(), pre.EQUSize());
		ArrayOfEquivalents = MergeInt(ArrayOfProgAddr, ArrayOfProgEquValues, pre.VarSize(), pre.EQUSize());

		ArrayOfSymbols = MergeStr(ArrayOfSymbols, ArrayOfProgLabels, ArrayOfSymbols.length, pre.LabelSize());
		ArrayOfEquivalents = MergeInt(ArrayOfEquivalents, ArrayOfProgOffsets, ArrayOfEquivalents.length, pre.LabelSize());

		boolean eof;

		eof = false;
		
		while(!eof) {	
			
				Str = ArrayOfProgToASM[i];
				ret = Assemble(Str, ArrayOfSymbols, ArrayOfEquivalents);
					switch(ret){
						case -1 : eof = true; break;	//end
						case -2 : org--; break;		//org 
						case -3 : return null; //"Line " + i +"  Illegal Opcode: " + BadOpcode; break; 
						default	: Prgm[org] = ret;/* System.out.println(Integer.toHexString(ret));*/ break;
					}
				org++;
				i++;
		}
		
		if(Info) 
			return new CompiledProgram(Prgm, pre.getLabelComplete(), pre.getVariableComplete());
		else
			return new CompiledProgram(Prgm);		
	}


	public static CompiledProgram Assemble(SourceCode ProgToASM) {
		return Assemble(ProgToASM.Source(), false);
	}

	public static CompiledProgram Assemble(SourceCode ProgToASM, boolean Info) {
		return Assemble(ProgToASM.Source(), Info);
	}




	public static int Assemble(String Instruction, String[] Var, int[] Equ) {
		int i, j, k, OpcodeAssembled = 0;
		String mn, attribut, attf = "", attd = "";

		i = Instruction.indexOf('\t');
		j = Instruction.indexOf(' ');
		if((i != -1) && (i < j)) {
			mn = (Instruction.substring(0, i)).toLowerCase();
			attribut = Instruction.substring(i, Instruction.length());	
		}
		else if(j != -1) {
			mn = (Instruction.substring(0, j)).toLowerCase();
			attribut = Instruction.substring(j, Instruction.length());
		}
		else if(i != -1) {
			mn = (Instruction.substring(0, i)).toLowerCase();
			attribut = Instruction.substring(i, Instruction.length());
		}
		else {
			mn = Instruction.toLowerCase();
			attribut = "";
		}
		attribut = attribut.trim();
		attf = attribut;
		i = attribut.indexOf(',');
		if(i != -1) {
			attf = attribut.substring(0, i);
			attd = attribut.substring(i+1, attribut.length());
		}
			
		InstructionInformation Info = InstructionSet.Find(mn);
		
		if(Info != null ) {
			OpcodeAssembled = Info.Opcode();

			if(Info.NbOperandes() > 0) {
				switch(Info.CodeAndDecodeType()) {
					case OpcodeType.FileAndDest	: OpcodeAssembled = ((OpcodeAssembled & Info.Mask()) | CodeDest(attd, Var, Equ) | CodeFile(attf, Var, Equ) ); break;
					case OpcodeType.File		: OpcodeAssembled = ((OpcodeAssembled & Info.Mask()) | CodeFile(attf, Var, Equ)); break;
					case OpcodeType.FileAnd3BitsConst :  OpcodeAssembled = ((OpcodeAssembled & Info.Mask()) | Code3BitsConst(attd, Var, Equ) | CodeFile(attf, Var, Equ) ); break;
					case OpcodeType.Literal8Bits	: OpcodeAssembled = ((OpcodeAssembled & Info.Mask()) | CodeLiteral8Bits(attribut, Var, Equ)); break;
					case OpcodeType.Literal11Bits	: OpcodeAssembled = ((OpcodeAssembled & Info.Mask()) | CodeLiteral11Bits(attribut, Var, Equ)); break;
					default				: break;
				}	
			}			
		}
		else {
			if(mn.equalsIgnoreCase("org") == true){
				OpcodeAssembled = -2;		//-2 = org		
				org =  FindSymbol(attf, Var, Equ);
			}
			else 
				if(mn.equalsIgnoreCase("end") == true)
					OpcodeAssembled = -1;		//-1 = End
			else {
				OpcodeAssembled = -3;		//-3 = Error "Illegal opcode: "
			//	BadOpcode = mn;
				}			
		}
		return OpcodeAssembled;
	}	


	protected static int FindSymbol(String Src, String[] Var, int[] Equ) {
		Integer j;
		try {
			j = Integer.decode(Src);
			return	j.intValue(); 
		}
		catch (NumberFormatException e) {
			int i;
			for(i = 0; i < Var.length; i++) {
				if(Src.compareTo(Var[i]) == 0)
					return Equ[i]; 
			}		
			return	0x666;		// not found <=> 0x666  bof....
		
		}
	}



	protected static String[] MergeStr(String[] One, String[] Two, int dim1, int dim2) {
		int  i;
		String Array[] = new String[dim1 + dim2];

		for(i = 0 ; i < dim1 ; i++) {
			Array[i] = One[i];	
		}
		for(i = 0 ; i < dim2 ; i++) {
			Array[i + dim1] = Two[i];			
		}
		return Array;
	}	


	protected static int[] MergeInt(int[] One, int[] Two, int dim1, int dim2) {
		int  i, Array[];
		Array = new int[dim1 + dim2];
		for(i = 0; i < dim1; i++)
			Array[i] = One[i];	

		for(i = 0; i < dim2 ; i++)
			Array[i + dim1] = Two[i];			
		
		return Array;
	}	



	protected static int CodeFile(String at, String[] Var, int[] Equ) {
		return FindSymbol(at, Var, Equ) & 0x7F;
	}

	protected static int CodeDest(String at, String[] Var, int[] Equ) {
		return (FindSymbol(at, Var, Equ) & 1) << 7;
	}

	protected static int Code3BitsConst(String at, String[] Var, int[] Equ) {
		return (FindSymbol(at, Var, Equ) & 7) << 7;
	}

	protected static int CodeLiteral8Bits(String at, String[] Var, int[] Equ) {
		return FindSymbol(at, Var, Equ) & 0xFF;
	}

	protected static int CodeLiteral11Bits(String at, String[] Var, int[] Equ) {
		return FindSymbol(at, Var, Equ) & 0x7FF;
	}
	

	protected static int org;
//	protected static String BadOpcode;
	
}