// Top level class of the translator tool

package temp;

import java.io.PrintStream;
import java.util.Vector;


public class Model {
	Sim_System system; // each model has a system
	String model_name; // name of the model
	Vector <Sim_System> syst_array; // array used to store the systems of all the subsystems in the model
	int trig_st; // the sample time counter of trigger block. Used to distinguish each triggered subsystems sample time  
	int enab_st; // the enable subsystem counter for the sample time used to distinguish the subsystems sample time and make it unique
	
	public Model(String name)
	{
		trig_st=-55;
		//enab_st=-255;
		enab_st=200;
		this.system = new Sim_System(this.model_name);
		this.model_name = name;
		this.syst_array=new Vector<Sim_System>();
	}
	
	public void addSystem(Sim_System syst)
	{
		this.system=syst;
	}
	
	// Central function of the translation. From this function begins the translation of the tool
	public void translate(PrintStream ps, String basename)
	{
		int i=0;
		SubSystem temp=null;

		// printing in the .bip file the first lines which include also the library to be read (simulinkLib.h)
		ps.println("model "+basename);
		ps.println("use simulinkLib");
		ps.println("header{# #include \"/home/tsiligia/sim/Simulink/simulinkLib.bip.h\" #}");
		ps.println();
		
		system.remove_STA();
		// initializing the triggered subsystems
		system.block_array.addAll(initializeTriggers(system,null));
		// initializing the enabled subsystems
		system.block_array.addAll(initializeEnables(system,null));
		// altering the connections between the subsystems and their triggering blocks.
		initializeLines(this.system,1);
		initializeLines(this.system,2);
		
		// initializing of the sample times of the top level system
		while(this.system.defineSampleTime())
			System.out.println("SYSTEM: "+ system.sys_name+" ITERATION OF DEFINE SAMPLE TIME "+i++);
		i=0;
		
		// storing all the systems of all the subsystems in the syst_array in a sorting by depth way 
		// so when they are translated we begin from the bottom level and go up 
		while(true)
		{
			if(system.block_array.get(i).getClass()==new SubSystem().getClass())
			{
				temp=(SubSystem)system.block_array.get(i);
				//system.block_array.add(trigger(temp.s_system));
				syst_array.add(temp.s_system);
				syst_array.addAll(0,system.find((SubSystem)system.block_array.get(i)));
			}
			if(system.block_array.get(i)==system.block_array.lastElement())
				break;
			i++;
		}
		
		i=0;
		int j=0;

		// Initializing the lines for all the rest of the subsystems and the sample times
		// and translating the subsystems systems.
		while(true & !syst_array.isEmpty())
		{
			syst_array.get(i).remove_STA();
			initializeLines(syst_array.get(i),1);
			initializeLines(syst_array.get(i),2);
			while(syst_array.get(i).defineSampleTime()&syst_array.get(i).type==0)
				System.out.println("SYSTEM: "+ syst_array.get(i).sys_name+" ITERATION OF DEFINE SAMPLE TIME "+j++);
			syst_array.get(i).translateEnable(ps);
			syst_array.get(i).translate(ps);
			if(syst_array.get(i)==syst_array.lastElement())
				break;
			i++;
		}
		
		system.translateEnable(ps);
		// translation of the top level system
		this.system.translate(ps);
		
		// finalizing the .bip file 
		ps.println("component "+basename+"_topLevel"+" "+basename+"topLevel_instance\n");
		ps.println("end");		
	}
	
	// This function is used to make new connections between the extracted trigger or enable block with the triggering 
	// block (ex. a pulse generator)
	private void initializeLines(Sim_System syst,int type) 
	{
		int i=0;
		int j=0;
		SubSystem sub=null;
		
		// when type==1 then it finds the trigger blocks and creates the new connections. for type==2 it
		// runs for enabled blocks.
		
		while(true)
		{
			if(!syst.line_array.get(i).branch_array.isEmpty())
			{
				while(true)
				{
					if(syst.line_array.get(i).d_port.get(j).matches("trigger")&type==1)
					{
						sub=new SubSystem();
						sub=(SubSystem)syst.line_array.get(i).dst.get(j);
						sub.trig_sample_time=syst.line_array.get(i).src.sample_time;
						syst.line_array.get(i).dst.remove(j);
						syst.line_array.get(i).dst.add(j,findTrigger(syst,sub));
					}
					if(syst.line_array.get(i).d_port.get(j).matches("enable")&type==2)
					{
						sub=new SubSystem();
						sub=(SubSystem)syst.line_array.get(i).dst.get(j);
						sub.trig_sample_time=syst.line_array.get(i).src.sample_time;
						syst.line_array.get(i).dst.remove(j);
						syst.line_array.get(i).dst.add(j,findEnable(syst,sub));
					}
					if(syst.line_array.get(i).d_port.get(j)==syst.line_array.get(i).d_port.lastElement())
						break;
					j++;
				}
			}
			else if(syst.line_array.get(i).dst_port.matches("trigger")&type==1)
			{
				sub=(SubSystem)syst.line_array.get(i).dst.get(0);
				sub.trig_sample_time=syst.line_array.get(i).src.sample_time;
				syst.line_array.get(i).dst.remove(0);
				syst.line_array.get(i).dst.add(0,findTrigger(syst,sub));
			}
			else if(syst.line_array.get(i).dst_port.matches("enable")&type==2)
			{
				sub=(SubSystem)syst.line_array.get(i).dst.get(0);
				sub.trig_sample_time=syst.line_array.get(i).src.sample_time;
				syst.line_array.get(i).dst.remove(0);
				syst.line_array.get(i).dst.add(0,findEnable(syst,sub));
			}
			j=0;
			if(syst.line_array.get(i)==syst.line_array.lastElement())
				break;
			i++;
		}
		
	}
	
	// finds the enable block on the syst array corresponding to the subsystem sub 
	private EnablePort findEnable(Sim_System syst, SubSystem sub) 
	{
		EnablePort enab=null;
		int i=0;
		
		while(true)
		{
			if(syst.block_array.get(i).getClass()==new EnablePort().getClass())
			{
				enab=(EnablePort)syst.block_array.get(i);
				if(enab.syst_name.matches(sub.block_name))
				{
					break;
				}
			}
			if(syst.block_array.get(i)==syst.block_array.lastElement())
				break;
			i++;
		}
		return enab;
	}

	// finds the trigger block on the syst array corresponding to the subsystem sub
	private TriggerPort findTrigger(Sim_System syst, SubSystem sub) 
	{
		TriggerPort trig=null;
		int i=0;
		
		while(true)
		{
			if(syst.block_array.get(i).getClass()==new TriggerPort().getClass())
			{
				trig=(TriggerPort)syst.block_array.get(i);
				if(trig.syst_name.matches(sub.block_name))
				{
					System.out.println("%%% "+trig.sample_time);
					break;
				}
			}
			if(syst.block_array.get(i)==syst.block_array.lastElement())
				break;
			i++;
		}
		return trig;
	}
	
	// function used to initialize the enabled subsystems. We extract the enable block from the subsystem and
	// move it to the upper level system. Also sample time for each enabled subsystem is initialized here
	private Vector<EnablePort> initializeEnables(Sim_System syst,SubSystem in_sub) 
	{
		int i=0;
		EnablePort enab=new EnablePort();
		Vector <EnablePort> enab_vect=new Vector<EnablePort>();
		SubSystem sub=null;
		
		while(true)
		{
			if(syst.block_array.get(i).getClass()==new EnablePort().getClass())
			{
				enab=(EnablePort)syst.block_array.get(i);
				
				// define the sample times and remove the enable block from the block_array of the system
				if(enab.syst_name==null)
				{
					enab.syst_name=syst.sys_name;
					modifySampleTimes(syst);
					// e_times is a table that stores the sample times inside an enabled subsystem
					enab.e_times.addAll(findSampleTimes(syst));
					enab_vect.add((EnablePort)syst.block_array.get(i));
					if(enab.states.matches("reset"))
						syst.reset=true;
					syst.block_array.remove(i);
					
				}
				enab_st=enab_st+200;
			}
			// call initializeEnables() in case a subsystem is found.
			else if(syst.block_array.get(i).getClass()==new SubSystem().getClass())
			{
				
				sub=(SubSystem)syst.block_array.get(i);
				if(syst.reset)
					sub.s_system.reset=true;
				
				syst.block_array.addAll(initializeEnables(sub.s_system,sub));
			}
			if(syst.block_array.get(i)==syst.block_array.lastElement())
				break;
			i++;
		}
		
		return enab_vect;
		
	}
	
	// initialization or sample times in an enabled subsystem. (the sample time of each subsystem
	// is its sample time + enable_st, where the enable_st is reduced by one for each subsystem.
	private void modifySampleTimes(Sim_System syst)
	{
		int i=0;
		SubSystem sub=null;		
		
		while(true)
		{
			syst.et_flag=enab_st;
			if(syst.block_array.get(i).sample_time!=-1 & syst.block_array.get(i).getClass() != new TriggerPort().getClass())
			{
				syst.block_array.get(i).sample_time=syst.block_array.get(i).sample_time+enab_st;
				
			}
			// in case a subsystem is found the modifySampleTimes is called for that system
			if(syst.block_array.get(i).getClass()==new SubSystem().getClass())
			{
				sub=(SubSystem)syst.block_array.get(i);
				if(sub.s_system.type==0 & syst.type==2)
				{
					sub.s_system.et_flag=enab_st;
					modifySampleTimes(sub.s_system);
				}
			}
			if(i==syst.block_array.size()-1)
				break;
			i++;
		}
	}
	
	// finds the sample times in a system and returns a vector with them.
	private Vector<Double> findSampleTimes(Sim_System syst)
	{
		Vector<Double> ret=new Vector<Double>();
		int i=0;
		
		while(true)
		{
			if(ret.isEmpty()& syst.block_array.get(i).getClass()!=new EnablePort().getClass()&syst.block_array.get(i).sample_time!=-1 & syst.block_array.get(i).getClass()!=new SubSystem().getClass()& syst.block_array.get(i).getClass()!=new TriggerPort().getClass())
				ret.add(syst.block_array.get(i).sample_time);
			else if(!ret.contains(syst.block_array.get(i).sample_time) & syst.block_array.get(i).getClass()!=new EnablePort().getClass()&syst.block_array.get(i).sample_time!=-1& syst.block_array.get(i).getClass()!=new SubSystem().getClass()&syst.block_array.get(i).getClass()!=new TriggerPort().getClass())
				ret.add(syst.block_array.get(i).sample_time);
			if(i==syst.block_array.size()-1)
				break;
			i++;
		}
		return ret;
	}

	// function used to initialize the triggerd subsystems. We extract the trigger block from the subsystem and
	// move it to the upper level system. Also sample time for each triggered subsystem is initialized here
	private Vector<TriggerPort> initializeTriggers(Sim_System syst,SubSystem in_sub) 
	{
		int i=0;
		TriggerPort trig=new TriggerPort();
		Vector <TriggerPort> trig_vect=new Vector<TriggerPort>();
		SubSystem sub=null;
		
		while(true)
		{
			if(syst.block_array.get(i).getClass()==new TriggerPort().getClass())
			{
				trig=(TriggerPort)syst.block_array.get(i);
				
				if(trig.syst_name==null)
				{
					trig.syst_name=syst.sys_name;
					trig.sample_time=trig_st;
					if(in_sub!=null)
					{
						in_sub.s_system.s_time=trig_st;
					}
					trig_st--;
					trig_vect.add((TriggerPort)syst.block_array.get(i));
					syst.block_array.remove(i);
				}
			}
			// in case a subsystem is found the initializeTriggers() is called to initialize the trigger blocks
			else if(syst.block_array.get(i).getClass()==new SubSystem().getClass())
			{
				sub=(SubSystem)syst.block_array.get(i);
				syst.block_array.addAll(initializeTriggers(sub.s_system,sub));
			}
			if(syst.block_array.get(i)==syst.block_array.lastElement())
				break;
			i++;
		}
		
		return trig_vect;
		
	}

	// function used to print the model in the screen. (not used any more)
	public void printModel(Sim_System syst)
	{
		int i=0;
		int j=0;
		int k=0;
		
		System.out.println(syst.sys_name);
		while(true)
		{
			j=0;
			//System.out.println(i);
			if(syst.block_array.get(i).getClass()==new Constant().getClass())
			{
				Constant c= (Constant) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(c.block_name + " value: " + c.value + " outDataType: "+c.out_data_type);
			}
			if(syst.block_array.get(i).getClass()==new Scope().getClass())
			{
				Scope s=(Scope)syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(s.block_name);
			}
			if(syst.block_array.get(i).getClass()==new Sum().getClass())
			{
				Sum sm= (Sum) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(sm.block_name + " ports: " + sm.ports + " outDataType: "+sm.out_data_type);
			}
			if(syst.block_array.get(i).getClass()==new Gain().getClass())
			{
				Gain g= (Gain) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(g.block_name + " gain value: " + g.gain_value + " outDataType: "+g.out_data_type);
			}
			if(syst.block_array.get(i).getClass()==new DiscretePulseGenerator().getClass())
			{
				DiscretePulseGenerator d= (DiscretePulseGenerator) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(d.block_name + " amplitude: "+d.amplitude + " pulse width: "+d.pulse_width + " sample time:"+ d.sample_time + " period:"+d.period);
			}
			if(syst.block_array.get(i).getClass()==new UnitDelay().getClass())
			{
				UnitDelay u= (UnitDelay) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(u.block_name + " sample time: "+u.sample_time);
			}
			if(syst.block_array.get(i).getClass()==new DiscreteIntegrator().getClass())
			{
				DiscreteIntegrator di= (DiscreteIntegrator) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(di.block_name + " sample time: "+di.sample_time + " ports: "+di.ports + " integration method: " + di.int_method + " initial condition: " + di.init_cond + "out data tupe: " + di.out_data_type + "external reset" + di.ext_reset);
			}
			if(syst.block_array.get(i).getClass()==new SubSystem().getClass())
			{
				SubSystem s_syst= (SubSystem) this.system.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(s_syst.block_name + " ports: "+s_syst.ports);
				System.out.println();
				System.out.println("/----------SUB-SYSTEM-----------/");
				this.printModel(s_syst.s_system);
				System.out.println("/-------------------------------/");
			}
			if(syst.block_array.get(i).getClass()==new InPort().getClass())
			{
				InPort ip= (InPort) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(ip.block_name + " out data tupe: " + ip.out_data_type);
			}
			if(syst.block_array.get(i).getClass()==new TriggerPort().getClass())
			{
				TriggerPort tp= (TriggerPort) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(tp.block_name + " trigger type: " + tp.trig_type + " states when enabling: " + tp.states_enabl );
			}
			if(syst.block_array.get(i).getClass()==new OutPort().getClass())
			{
				OutPort op= (OutPort) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(op.block_name + " out data tupe: " + op.out_data_type );
			}
			if(syst.block_array.get(i).getClass()==new ZeroOrderHold().getClass())
			{
				ZeroOrderHold z= (ZeroOrderHold) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(z.block_name + " sample time: " +z.sample_time );
			}
			if(syst.block_array.get(i).getClass()==new EnablePort().getClass())
			{
				EnablePort en= (EnablePort) syst.block_array.get(i);
				System.out.print("BLOCK: ");
				System.out.println(en.block_name);
			}
			
			if(syst.block_array.lastElement()==syst.block_array.get(i)) 
				{
					System.out.println("");
					break;
				}
			i++;			
		}
		while(true)
		{	
			k=0;
			if(syst.line_array.get(j).branch_array.isEmpty())
			{
				System.out.print("LINE: ");
				System.out.println(syst.line_array.get(j).src_block+" "+syst.line_array.get(j).src_port+"-->"+syst.line_array.get(j).dst_block+" "+syst.line_array.get(j).dst_port);				
			}
			else//(syst.line_array.get(j).branch_array!=null)
			{
				while(true)
				{
					System.out.println("BRANCH");
					System.out.print("	LINE: ");
					System.out.println(syst.line_array.get(j).src_block+"-->"+syst.line_array.get(j).branch_array.get(k).dst_block);
					if(syst.line_array.get(j).branch_array.get(k)==syst.line_array.get(j).branch_array.lastElement()) break;
					k++;
				}
			}
			if(syst.line_array.get(j)==syst.line_array.lastElement()) break;
			j++;
		}
	}
}
