import java.io.*;

public class CompiledProgram {

	protected void loadCodFile(String fn) {
			int offs, length, nblabs = 0, nbvars = 0, offsetinfos = 0;
			boolean islab;
			String identif;
			byte tmp[] = new byte[12];
			int i, order[] = new int[4];
			byte type;

			Program = new int[Flash.FlashSize];

			DataInputStream in = null;

			try {
				try {
					in = new DataInputStream(new FileInputStream(fn));
				} catch(FileNotFoundException nfe) {
					System.out.println("File not found !");
				}

				length = 0;
				offsetinfos = 0x200;		// first sector
				for(i = 0 ; i < 4 ; i++)
					order[i] = -1;
				for(i = 0 ; i < 4 ; i++) {
					int k = in.readUnsignedByte();
					if(k != 0) {
						order[k - 1] = i;
						offsetinfos += 0x200;	// one sector more
					}
					in.readByte();
					length += 2;
				}
				offsetinfos += 0x200;		// last sector before infos

				in.skipBytes(0x200 - length);
				length = 0x200;
				for(i = 0 ; i < 4 ; i++) {
					if(order[i] != -1) {
						for(int j = 0 ; j < 0x100 ; j++) {
							int k = in.readUnsignedByte();
							k += (in.readUnsignedByte() << 8);
//System.out.println("["+((order[i] - 1) * 0x100 + j)+"]="+Utils.hex(k,4,4));
							Program[order[i] * 0x100 + j] = k;
						}
						length += 0x200;
					}
/*					else
						in.skipBytes(0x200);*/
				}

				in.skipBytes(offsetinfos - length);

				do {
					length = in.readByte();
					in.readFully(tmp);
					identif = new String(tmp);
					type = in.readByte();
					islab = (type == '.') ? true : false;
					offs = in.readByte();
					offs += in.readByte() << 8;
					if(length != 0) {
						if(islab)
							nblabs++;
						else
							nbvars++;
//						System.out.println((islab ? "Label : " : "Variable : ") + identif + " at offset : " + Utils.hex(offs, 3, 3));
					}
				} while(length != 0);

				try {
					in = new DataInputStream(new FileInputStream(fn));
				} catch(FileNotFoundException nfe) {
					System.out.println("File not found !");
				}

				in.skipBytes(offsetinfos);
				LabelSymbolic = new SymbolicInfos[nblabs];
				VariableSymbolic = new SymbolicInfos[nbvars];
				nblabs = 0;
				nbvars = 0;

				do {
					length = in.readByte();
					in.readFully(tmp);
					identif = new String(tmp);
					identif = identif.substring(0, length);
					type = in.readByte();
					islab = (type == '.') ? true : false;
					offs = in.readUnsignedByte();
					offs += in.readUnsignedByte() << 8;

					if(length != 0) {
						if(islab)
							LabelSymbolic[nblabs++] = new SymbolicInfos_std(identif, offs);
						else
							VariableSymbolic[nbvars++] = new SymbolicInfos_std(identif, offs);
//						System.out.println((islab ? "Label : " : "Variable : ") + identif + " at offset : " + Utils.hex(offs, 3, 3));
					}
				} while(length != 0);

				Info = true;
				Size = Flash.FlashSize;
				LabelSymbolicSize = LabelSymbolic.length;
				VariableSymbolicSize = VariableSymbolic.length;
			} catch(IOException ie) {
				System.out.println("Cannot skip !");
			}
	}

	public CompiledProgram(String filename) {
		if(filename.endsWith(".hex"))
			loadHexFile(filename);
		else
			loadCodFile(filename);
	}

	protected void loadHexFile(String filename) {
		boolean eof = false;
		Program = new int[Flash.FlashSize];
		Info = false;
	
		Size = 0;
		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
			in.readLine(); // Not the first line
			while(!eof) {
				String s = in.readLine();
				if(s == null)
					eof = true;
				else {
					if(s.charAt(0) == ':') {
						int len = Utils.hex2num(s.substring(1, 3));
						if(len != 0) {
							int r, i, offset = Utils.hex2num(s.substring(3, 7)) / 2;
							r = 9;
							for(i = offset ; i < offset + (len / 2) ; i++) {
								if(Size < i) Size = i;
								Program[i] = Utils.hex2num(s.substring(r + 2, r + 4) + s.substring(r, r + 2));
								r += 4;
							}
						}
					}
				}
			}
		}
		catch (IOException e) {};
		Size++; // Size is the number of instructions, not the highest index
	}

	public CompiledProgram(int prog[], SymbolicInfos Lab[], SymbolicInfos Var[] ) {
		Program = prog;
		LabelSymbolic = Lab;
		VariableSymbolic = Var;
		LabelSymbolicSize = Lab.length;
		VariableSymbolicSize = Var.length;
		Size = prog.length;

		Info = true;
	}

	public CompiledProgram(int prog[]) {
		Program = prog;
		Size = prog.length;
		Info = false;
	}



	public void Save(String filename) {
		int i = 0, Linei = 0, ReadLineIndex = 0, Offset = 0, Line[];
		String Prgm[], Str;
		boolean eofP = false;
		Prgm = new String[131];				//128+2 +1

		Prgm[Linei] = ":020000040000FA";
		Linei++;
		
		while(!eofP) {
			Line = new int[12];
			
			try{
				for(i = 0; i < 8; i++)
					Line[i+2] = Program[i+8*ReadLineIndex];	
				
				Line[0] = 16;
				Line[1] = Offset;
				Line[11] = CalculateCS(Line);
				Offset = Line[1] + Line[0];

			}
			catch (ArrayIndexOutOfBoundsException e) {
				Line[0] = i;
				Line[1] = Offset;
				Line[11] = CalculateCS(Line);
				eofP = true;
			}
			if(eofP)
				break;
			Str = ":";
			for(i = 0; i < (Line[0] + 5); i++){
				if(FormattedLine[i] < 16)
					Str += "0";
				Str += (Integer.toHexString(FormattedLine[i])).toUpperCase();
				Prgm[Linei] = Str;
			}
			Linei++;
			ReadLineIndex++;
		}

		Prgm[Linei] = ":00000001FF";


		try {
			BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));
			out.flush();
			for(i = 0 ; i <= Linei ; i++){ 
				out.write(Prgm[i]);
				out.newLine();
			}
		out.close();
		}
		catch (IOException e) {
		System.out.println(e);
		}


	}




	protected int CalculateCS(int[] Line){		//calcul CS and rearrange list
		int ret = 0, i, j;
		FormattedLine = new int[Line[0] + 5];
		ret = Line[0] + Line[1];
			FormattedLine[0] = (Line[0] & 0xFF);
			FormattedLine[1] = ((Line[1] & 0xFF00) >> 8);
			FormattedLine[2] = (Line[1] & 0xFF);
			FormattedLine[3] = 0;
			for(i = 0; i < (Line[0]/2); i++) {
				j = 2*i+2;
				FormattedLine[(j+2)] = (Line[i+2] & 0xFF);
				FormattedLine[(j+2)+1] = ((Line[i+2] & 0xFF00) >> 8);
				ret += (Line[i+2] & 0xFF) + ((Line[i+2] & 0xFF00) >> 8); 
			//	System.out.println("1:  " + Integer.toHexString((Line[i] & 0xFF)) + "   2:  " + Integer.toHexString((Line[i] & 0xFF00) >> 8) + "    Ret: " + Integer.toHexString(ret));
			}
			FormattedLine[Line[0] + 4] = ((-ret) & 0xFF);
	return	FormattedLine[Line[0] + 4];
	}

	
	public SymbolicInfos[] getLabelSymb() {
		return LabelSymbolic;
	}

	public String[] getLabelsListForDisplay() {
		int i;
		String s[] = new String[LabelSymbolic.length];

		for(i = 0 ; i < LabelSymbolic.length ; i++)
			s[i] = Utils.hex(LabelSymbolic[i].address(), 3, 3) + "     :  " + LabelSymbolic[i].name();

		return s;
	}

	public SymbolicInfos[] getVariableSymb() {
		return VariableSymbolic;
	}
	
	public String[] getVariablesListForDisplay() {
		int i;
		String s[] = new String[VariableSymbolic.length];

		for(i = 0 ; i < VariableSymbolic.length ; i++)
			s[i] = Utils.hex(VariableSymbolic[i].address(), 2, 2) + "      :  " + VariableSymbolic[i].name();

		return s;
	}

	public int[] Program() {
		return Program;
	}


	public int Size() {
		return Size;
	}
	
	public boolean hasInfos() {
		return Info;	
	}	



	protected int Program[], FormattedLine[];
	protected int Size, LabelSymbolicSize, VariableSymbolicSize;
	protected SymbolicInfos[] LabelSymbolic, VariableSymbolic;
	protected boolean Info;
}