package sim.lib.functions;

import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Scanner;

import sim.*;
import sim.engine.*;

import sim.lib.wires.Junction;

public class Add extends Function2fat1thinIn1fat1thinOut {
	/* ==================================================================
	 Creation Part
	 ================================================================= */

	private static Image ICON = GuiFileLink.getImage("sim/lib/functions/AddUnitIcon.gif");

	public Image getIcon() {
		return Add.ICON;
	}

	public String getBubbleHelp() {
		return "Add unit";
	}

	/* ==================================================================
	 GUI part
	 ================================================================= */
	public Add() {
		super();
	}

	protected Wrapper getCopy() {
		Add result = new Add();
		result.changeDelay(this.delay);
		result.setBusSize(this.busSize);
		return result;
	}

	public void paint(Graphics g) {
		// draw if visible
		if (this.isVisible()) {
			int gridGap = CentralPanel.ACTIVE_GRID.getCurrentGridGap();
			int increment = gridGap / 4;
			int tripple = 3 * gridGap;

			g.setColor(this.brush);

			g.drawLine(gridGap + increment, gridGap, 10 * increment, gridGap);
			g.drawLine(14 * increment, gridGap, 19 * increment, gridGap);

			g.drawLine(10 * increment, gridGap, tripple, 6 * increment);
			g.drawLine(14 * increment, gridGap, tripple, 6 * increment);

			g.drawLine(gridGap + increment, tripple, 19 * increment, tripple);

			g.drawLine(gridGap + increment, gridGap, gridGap + increment, tripple);
			g.drawLine(19 * increment, gridGap, 19 * increment, tripple);


			g.drawLine(0, 2 * gridGap, gridGap + increment, 2 * gridGap);
			g.drawLine(19 * increment, 2 * gridGap, 6 * gridGap, 2 * gridGap);
			g.fillRect(2 * gridGap - 1, 0, 3, gridGap);
			g.fillRect(4 * gridGap - 1, 0, 3, gridGap);
			g.fillRect(tripple - 1, tripple, 3, gridGap);

			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 3 * increment));

			FontMetrics fm = g.getFontMetrics(g.getFont());
			g.drawString("+", tripple - fm.stringWidth("+") / 2, 10 * increment);

			g.setFont(new Font(Wrapper.FONT_NAME, Font.PLAIN, 8));
			g.drawString("Co", 6 * increment, 9 * increment);
			g.drawString("Ci", 15 * increment, 9 * increment);
		}
	}

	/* ==================================================================
	 Simulation part
	 ================================================================= */
	public void evaluateOutput(double currentTime, Data[] currentInputs, EnginePeer peer) {
		double time = this.delay + currentTime;

		int loop;
		int x = 0;
		int y = 0;
		int base = 1;
		boolean foundUndefined = false;
		String bitValue;

		for (loop = 0; (loop < this.busSize) && (!foundUndefined); loop++) {
			if (currentInputs[loop].isUndefined() || currentInputs[loop + this.busSize].isUndefined()) {
				foundUndefined = true;
			} else {
				if (currentInputs[loop].getValue()) {
					x = x + base;
				}

				if (currentInputs[loop + this.busSize].getValue()) {
					y = y + base;
				}
			}

			base = 2 * base;
		}

		if (!currentInputs[2 * this.busSize].isUndefined()) {
			if (currentInputs[2 * this.busSize].getValue()) {
				x = x + 1;
			}
		}

		if (foundUndefined) {
			for (loop = 0; loop <= this.busSize; loop++) {
				peer.setOutputPinUndefined(loop, time);
			}
		} else {

			x = x + y;
			bitValue = Integer.toBinaryString(x);

			peer.setOutputPinValue(this.busSize, bitValue.length() > this.busSize, time);

			for (loop = bitValue.length(); loop < this.busSize; loop++) {
				bitValue = "0" + bitValue;
			}

			for (loop = 0; loop < this.busSize; loop++) {
				peer.setOutputPinValue(loop, bitValue.charAt(bitValue.length() - 1 - loop) == '1', time);
			}
		}
	}
	
	public void writeVHDLEntity(String path) {
		VHDLGeneral.write2busto1busFunction(path,getVHDLName(),this.busSize,"f <= std_logic_vector(to_unsigned(to_integer(unsigned(x) + unsigned(y) + unsigned(cin_vect)),f'length));",true,true);
		/*String fileName = "gates.vhd";
		File file = new File(path+"\\"+fileName);
		boolean written = false;
		try {
			Scanner scanner = new Scanner(file);
			while(scanner.hasNextLine()){
				String line = scanner.nextLine();
				if(line.contains(getVHDLName())){
					written = true;
				}
			}
			scanner.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		
		if(!written){
			Writer writer = null;
			try {
				writer = new FileWriter(path+"\\"+fileName,true);
				writer.write("\nlibrary IEEE;\n");
				writer.write("use IEEE.STD_LOGIC_1164.ALL;\n");
				writer.write("use IEEE.numeric_std.all;\n");
				
				writer.write("\nentity ADDbus_ent_" + this.busSize + " is\n");
				//if(this.inputSize == 1){
				writer.write("port( x0: in std_logic;\n");
				for(int x = 1; x < this.busSize; x++){
					writer.write("x" + x + ": in std_logic;\n");
				}
				
				//writer.write("y0: in std_logic;\n");
				for(int x = 0; x < this.busSize; x++){
					writer.write("y" + x + ": in std_logic;\n");
				}
				
				writer.write("ci: in std_logic;\n");
				
				for(int x = 0; x < this.busSize; x++){
					writer.write("F" + x + ": out std_logic;\n");
				}
				
				writer.write("co: out std_logic\n");
				writer.write(");\n");
				
				writer.write("end ADDbus_ent_" + this.busSize +";\n\n");
				
				writer.write("architecture behav of ADDbus_ent_" + this.busSize + " is\n");
				writer.write("signal x: std_logic_vector(" +(this.busSize-1) + " downto 0);\n");
				writer.write("signal y: std_logic_vector(" +(this.busSize-1) + " downto 0);\n");
				writer.write("signal f: std_logic_vector(" +(this.busSize-1) + " downto 0);\n");
				writer.write("begin\n");
				//writer.write("F<=x0");

				for(int x = 0; x < this.busSize; x++){
					writer.write("x(" + x + ") <= x" + x + ";\n");
					writer.write("y(" + x + ") <= y" + x + ";\n");
				}
				writer.write("f <= std_logic_vector(unsigned(x) + unsigned(y));\n");
				for(int x = 0; x < this.busSize; x++){
					writer.write("f" + x + " <= f(" + x + ");\n");
				}
				//writer.write(";\n");
				writer.write("end behav;\n");
				
				writer.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}*/
		
	}
	
	public String getVHDLName() {
		// TODO Auto-generated method stub
		return "ADDbus_ent_" + this.busSize;
	}
}
