using UnityEngine;
using System.Collections;
using System;
using MiMic.CsApi;
using MiMic.CsApi.LPC1769;

public class TestAllNonBlockScript : MonoBehaviour
{
	private Mcu _mcu;
	// Use this for initialization
	void Start ()
	{
		this.setGuiProc(this.onGuiInit);
	}
	public delegate void GuiProc();
	public GuiProc _gui_proc;
	public int _gui_proc_tc;
	/**
	 * MiMic Event callback
	 */
	public void _mcuCallback()
	{
		//call the periodic proc here.
		this._mcu.periodicProc();
	}
	/**
	 * GRI switch
	 */
	private void setGuiProc(GuiProc i_proc)
	{
		this._gui_proc=i_proc;
		this._gui_proc_tc=0;
	}
	void OnGUI()
	{
		this._gui_proc();
	}
	//
	// Properties
	//
	private string _mcu_ip_addr="192.168.128.39";
	
	/**
	 * show the connection box.
	 */ 
	void onGuiInit()
	{
		GUI.BeginGroup (new Rect (Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 100));
		GUI.Box(new Rect (0,0,200,100), "Input your MiMic IP Address");
		string ts=GUI.TextField (new Rect (20,40,160,20), this._mcu_ip_addr,19);
		if(ts!=this._mcu_ip_addr){
			this._mcu_ip_addr=ts;
		}
		if(GUI.Button (new Rect (100-40,65,80,30), "Connect")){
			//change state
			this.setGuiProc(this.onGuiTryConnection);
		}
		GUI.EndGroup ();
	}
	/**
	 * Try to connection with synchronized call.
	 */
	void onGuiTryConnection()
	{
		switch(this._gui_proc_tc){
		case 0:
			GUI.Box(new Rect (Screen.width / 2 - 100, Screen.height / 2 - 100,200,50), "Connect to "+this._mcu_ip_addr+" ...");
			this._gui_proc_tc++;
			break;
		case 1:
			try{
				GUI.Box(new Rect (Screen.width / 2 - 100, Screen.height / 2 - 100,200,50), "Connect to "+this._mcu_ip_addr+" ...");
				//The constructor throw an exception when failed.
				this._mcu=new Mcu(this,"_mcuCallback",this._mcu_ip_addr,true);
				this.setGuiProc(onGuiTest);
			}catch(Exception e){
				this.setGuiProc(this.onGuiReturnInit);
			}
			break;
		default:
			break;
		}
	}
	/**
	 * show return dialog box.
	 * This window is shown when the connection was disconnected. etc.
	 */
	void onGuiReturnInit()
	{
		GUI.BeginGroup (new Rect (Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 100));
		GUI.Box(new Rect (0,0,200,100), "Can not connect to "+this._mcu_ip_addr);
		if(GUI.Button (new Rect (100-40,65,80,30), "Return")){
			//change state
			this.setGuiProc(this.onGuiInit);
		}
		GUI.EndGroup ();		
	}
	/**
	 * Gpio test case
	 */
	class GpioTest
	{
		private int tc=0;
		private GpioPin gpiopin;
		private GpioPort gpioport;
		public UInt32 pinv=0;
		public UInt32 portv=0;
		public GpioTest(Mcu i_mcu)
		{
			//Create a gpio pin, 4 bit gpio port.
			this.gpiopin=(GpioPin)i_mcu.getPin(LPCXpresso1769.P0[22],"GPIO");
			this.gpiopin.setOpt(new GpioPin.Option(1,new Pin.Option(null,1,0)));
			this.gpioport=(GpioPort)i_mcu.getPort(
				new PinId[]{LPCXpresso1769.P0[0],LPCXpresso1769.P0[1],LPCXpresso1769.P0[18],LPCXpresso1769.P0[17]},"GPIO");
			this.gpioport.setOpt(new GpioPin.Option(1,new Pin.Option(null,1,0)));			
		}
		public void update()
		{
			
			GUI.Label(new Rect(0,Screen.height-100,200,100),
				"GPIO output test.\n"+
				"TC:"+this.c+"/"+this.tc+"\n"+
			    "Pin:  P0[22](LED):"+this.pinv+" \n"+
			    "Port: P0[0],P0[1],P0[18],P0[17]:"+this.portv+"\n");
			this.tc++;
			int mode=1;
			if(mode==1){
				switch(this._pin_state){
				case 0:
					this.c++;
					this.nbr=this.gpiopin.setValueNB(c%2);
					this._pin_state++;
					break;
				case 1:
					if(this.nbr.isDone){
						this.nbur=this.gpiopin.getValueNB();
						this._pin_state=2;
					}
					break;
				case 2:
					if(this.nbur.isDone){
						this.pinv=this.nbur.getValue();
						this.c++;
						this.nbr=this.gpiopin.setValueNB(c%2);
						this._pin_state=1;
					}
					break;
				}
			}else{
				//port test
				switch(this._pin_state){
				case 0:
					this.c++;
					this.nbr=this.gpioport.setValueNB(c);
					this._pin_state=1;
					break;
				case 1:
					if(this.nbr.isDone){
						this.c++;
						this.nbr=this.gpioport.setValueNB(c);
						this._pin_state=2;
					}
					break;
				case 2:
					if(this.nbr.isDone){
						this.nbur=this.gpioport.getValueNB();
						this._pin_state=3;
					}
					break;
				case 3:
					if(this.nbur.isDone){
						this.portv=this.nbur.getValue();
						this.c++;
						this.nbr=this.gpioport.setValueNB(c);
						this._pin_state=2;
					}
					break;
				}
			}
		}
		private UInt32 c=0;
		private NBResult nbr;
		private NBUIntResult nbur;
		private int _pin_state=0;
	}
	/**
	 * Adc test case
	 */
	class AdcTest
	{
		private int tc=0;
		private int ac=0;
		private AdcPin adcpin;
		private AdcPort adcport;
		public UInt32 pinv;
		public UInt32[] portv=new UInt32[2];
		public AdcTest(Mcu i_mcu)
		{
			this.adcpin=(AdcPin)i_mcu.getPin(LPCXpresso1769.P0[23],"AD");
			this.adcport=(AdcPort)i_mcu.getPort(new PinId[]{LPCXpresso1769.P0[24],LPCXpresso1769.P0[25]},"AD");
		}
		public void update()
		{
			GUI.Label(new Rect(0,Screen.height-100,200,100),
				"AD input test."+this.ac+"/"+this.tc+"\n"+
			    "Pin:  P0[23]"+this.pinv+"\n"+
			    "Pin:  P0[23] P0[24]"+this.portv[0]+","+this.portv[1]);
			int mode=1;
			if(mode==0){
				if(this._rets==null){
					this._rets=this.adcport.getValuesNB();
					this.ac++;
				}else{
					if(this._rets.isDone){
						UInt32[] r=this._rets.getValues();
						this.portv[0]=(UInt32)r[0];
						this.portv[1]=(UInt32)r[1];
						this._rets=null;
					}
				}
			}else{
				if(this._ret==null){
					this._ret=this.adcpin.getValueNB();
					this.ac++;
				}else{
					if(this._ret.isDone){
						UInt32 r=this._ret.getValue();
						this.pinv=(UInt32)r;
						this._ret=null;
					}
				}				
			}
			this.tc++;
		}
		private NBUIntArrayResult _rets=null;
		private NBUIntResult _ret=null;
	}
	/**
	 * Pwm test case
	 */
	class PwmTest
	{
		private int c=0;
		private int tc=0;
		private float pwm1=0f;
		private float pwm2=0f;
		private PwmPin pwmpin;
		private PwmPort pwmport;
		const int mode=0;
		public PwmTest(Mcu i_mcu)
		{
			Pwm pwm=(Pwm)i_mcu.getPeripheral("PWM",new Pwm.Option(100,null));
			if(mode==0){
				this.pwmpin =pwm.getPin(LPCXpresso1769.P2[0],null);
			}else{
				this.pwmport=pwm.getPort(new PinId[]{LPCXpresso1769.P2[0],LPCXpresso1769.P2[1]},null);
			}
		}
		public void update()
		{
			GUI.Label(new Rect(0,Screen.height-100,200,100),
				"Pwm output test."+this.c+"/"+this.tc+"\n"+
			    "Pin:  P2[0] P2[1]");
			this.pwm1=GUI.HorizontalScrollbar(new Rect (200,80,200,30), this.pwm1,0.1f, 0.0f,1.0f);
			this.pwm2=GUI.HorizontalScrollbar (new Rect (200,120,200,30), this.pwm2,0.1f, 0.0f,1.0f);
			GUI.Label(new Rect (80,80,120,40),"PWM1:"+this.pwm1);
			GUI.Label(new Rect (80,120,120,40),"PWM2:"+this.pwm2);
			if(mode==0){
				if(this.ret==null){
					c++;
					this.ret=this.pwmpin.setDutyNB(this.pwm1);
				}else{
					if(this.ret.isDone){
						this.ret=null;
					}
				}
			}else{
				if(this.ret==null){
					c++;
					this.ret=this.pwmport.setDutysNB(new float?[]{this.pwm1,this.pwm2});
				}else{
					if(this.ret.isDone){
						this.ret=null;
					}
				}
			}
			this.tc++;
		}
		private NBResult ret=null;
	}
	class NiTest
	{
		private bool ret1;
		private UInt32 ret2;
		private int tc=0;
		private Ni ni;
		public NiTest(Mcu i_mcu)
		{
			this.ni=new Ni(i_mcu);
		}
		public void update()
		{
			if(ret==null){
				ret=this.ni.callNB(0x39000000,null,new UInt32[]{39},null,new UInt32[1]);
			}else{
				if(ret.isDone){
					this.ret1=this.ret.getRet();
					this.ret2=(UInt32)this.ret.getStream()[0];
				}
			}
			GUI.Label(new Rect(0,Screen.height-100,200,100),
				"Ni test.\n"+
			    "Result[0]: "+this.ret1+"\n"+
			    "Result[1]: "+this.ret2+"\n");
			this.tc++;
		}
		private Ni.NBCallResult ret;
	}
	class MemTest
	{
		private UInt32[] ret1=null;
		private UInt32[] ret2=null;
		private UInt32[] ret3=null;
		private int tc=0;
		private Memory mem;
		public MemTest(Mcu i_mcu)
		{
			this.mem=new Memory(i_mcu,0);
		}
		private int r=0;
		public void update()
		{
			switch(r){
			case 0:
				if(this.nb[0]==null){
					this.nb[0]=this.mem.write32NB(0x20080000,839);
				}else{
					if(this.nb[0].isDone){
						this.r++;
					}
				}
				break;
			case 1:
				if(this.nb[1]==null){
					this.nb[1]=this.mem.write32NB(new UInt32[]{0x20080020,0x20080024},new UInt32[]{739,639});
				}else{
					if(this.nb[1].isDone){
						this.r++;
					}
				}
				break;
			case 2:
				if(this.nb[2]==null){
					this.nb[2]=this.mem.write32NB(0x20080008,new UInt32[]{539,439,339});
				}else{
					if(this.nb[2].isDone){
						this.r++;
					}
				}
				break;
			case 3:
				if(this.nbr[0]==null){
					this.nbr[0]=this.mem.read32NB(0x20080000);
				}else{
					if(this.nbr[0].isDone){
						this.r++;
					}
				}
				break;
			case 4:
				if(this.nbr[1]==null){
					this.nbr[1]=this.mem.read32NB(0x20080020,8);
				}else{
					if(this.nbr[1].isDone){
						this.r++;
					}
				}
				break;
			case 5:
				if(this.nbr[2]==null){
					this.nbr[2]=this.mem.read32NB(new UInt32[]{0x20080008,0x20080010,0x2008000c});
				}else{
					if(this.nbr[2].isDone){
						this.r++;
					}
				}
				break;
			case 6:
				this.ret1=this.nbr[0].getValues();
				this.ret2=this.nbr[1].getValues();
				this.ret3=this.nbr[2].getValues();
				this.r=0;
				for(int i=0;i<nb.Length;i++){
					this.nb[i]=null;
				}
				for(int i=0;i<nbr.Length;i++){
					this.nbr[i]=null;
				}
				break;
			}

			if(ret1!=null && ret2!=null && ret3!=null){
				GUI.Label(new Rect(0,Screen.height-100,200,100),
					"Memory test.\n"+
				    "Result[0]: "+this.ret1[0]+"\n"+
				    "Result[1]: "+this.ret2[0]+","+this.ret2[1]+"\n"+
				    "Result[2]: "+this.ret3[0]+","+this.ret3[1]+","+this.ret3[2]+"\n");
			}
			this.tc++;
		}
		private NBResult[] nb=new NBResult[6];
		private NBUIntArrayResult[] nbr=new NBUIntArrayResult[6];
	}
	MemTest memt;
	NiTest nit;
	GpioTest gpiot;
	AdcTest adct;
	PwmTest pwmt;
	int test_mode=0;

	void onGuiTest()
	{
		if(this._gui_proc_tc==0){
			this.memt=new MemTest(this._mcu);
			this.nit=new NiTest(this._mcu);
			this.gpiot=new GpioTest(this._mcu);
			this.adct=new AdcTest(this._mcu);
			this.pwmt=new PwmTest(this._mcu);
		}
		if(GUILayout.Button("GPIO PIN/PORT")){
			test_mode=1;
		}
		if(GUILayout.Button("AD PIN")){
			test_mode=2;
		}
		if(GUILayout.Button("PWM PIN")){
			test_mode=3;
		}
		if(GUILayout.Button("MEMORY")){
			test_mode=4;
		}
		if(GUILayout.Button("NI")){
			test_mode=5;
		}
		switch(this.test_mode){
		case 1:
			this.gpiot.update();
			break;
		case 2:
			this.adct.update();
			break;
		case 3:
			this.pwmt.update();
			break;
		case 4:
			this.memt.update();
			break;
		case 5:
			this.nit.update();
			break;
		}
		this._gui_proc_tc++;

	}
}
