/**
 * @fileOverview LPCXpresso1769のMCU制御クラスを定義する。
 */
(function(){
	/** @ignore */
	var DEV=LPCXpresso1769;
	var BCF=DEV._BCF;
	var EE=DEV._EE;
	var isUndef=MiMicLib.isUndef;

	/**
	 * 配列をMiMicDBへ変換する。
	 @private
	 @throws
	 変換失敗時は例外
	 */
	function array2MimicDb(i_array)
	{
		try{
			var p="";
			for(var i=0;i<i_array.length;i++){
				p+=MiMicLib.hexout(i_array[i],8);
			}
			return p;
		}catch(e){
			throw new MiMicException(e);
		}
	}
	
	/**
	 * PINの機能名を含むPin識別子を得る。
	 @private
	 */
	function getPinByFuncName(i_name)
	{
		try{
			function getPinByFName(i_pintbl,i_name)
			{
				for(var i=0;i<i_pintbl.length;i++){
					if(i_pintbl[i]==null){
						continue;
					}
					if(DEV.hasPinFunctionName(i_pintbl[i],i_name)){
						return i_pintbl[i];
					}
				}
				return null;
			}
			var tbl=[DEV.P0,DEV.P1,DEV.P2,DEV.P3,DEV.P4];
			for(var i=0;i<tbl.length;i++){
				var pin=getPinByFName(tbl[i],i_name);
				if(pin!=null){
					return pin;
				}
			}
			throw new MiMicException("pin function '"+i_name+"' not found");
		}catch(e){
			throw new MiMicException(e);
		}
	}
	/**
	 * PIN機能名から使用するペリフェラルシンボルを推定する。
	 @private
	 */
	function pinFuncName2PhlName(i_pinfuncname)
	{
		try{
			//Pin function nameプレフィクスとペリフェラル名の対比表
			var TBL={
				"GPIO":"GPIO",
				"AD":"ADC",
				"DA":"DAC",
				"PWM":"PWM"
			};
			for(var key in TBL){
				if(i_pinfuncname.indexOf(key)==0){
					return TBL[key];
				}
			}
			//見つからぬ。
			throw new MiMicException("Peripheral for "+i_pinfuncname+" not found.");
		}catch(e){
			throw new MiMicException(e);
		}
	}
	
	/**
	 * LPCXPresso1769.MCU(MCU)クラスのコンストラクタ。
	 * MCUクラスは、物理MCUとのインタフェイスを定義する。MiMicRemoteMcuInterfaceの管理機能と、MCUの物理機能への接続手段を定義する。
	 * 提供する機能は次のリストである。
	 * <ul>
	 * <li>物理MCUとの接続管理機能。
	 * <li>MCUの管理するペリフェラル操作インスタンスの生存管理機能。
	 * <li>MCUの持つ物理デバイスの操作オブジェクト(Pin,Peripheral等)の生成機能（インテリジェント接続）。
	 * </ul>
	 * @name LPCXpresso1769.Mcu
	 * @constructor
	 * @param {string} i_mimic_addr
	 * 接続するMiMicRemoteMcuのホストアドレスを指定する。IPアドレス、ホスト名等。
	 * <pre>ex. “127.0.0.1” , “127.0.0.1:3939”</pre>
	 * @param {boolean} i_is_activate
	 * インスタンス生成と同時にMiMicRemoteMcuとの接続を確立するかのフラグ。省略時はtrueとみなす。
	 * trueの場合、関数は即座にMiMicRemoteMCUに接続する。このとき、events.onActivateChangedをハンドリングすることが出来ないので注意すること。ハンドリングが必要なら、falseを指定して、改めてactivate関数をコールする。
	 * @example
	 * //create a controlable MCU via network.
	 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
	 */
	DEV.Mcu=function Mcu(i_mimic_addr,i_is_activate)
	{
		try{
			//メンバの初期化
			this.events={onActivateChanged:null};
			this._phl_holder=[];
			//リモートインタフェイスの取得
			this._mif=new MiMicRemoteMcuInterface(i_mimic_addr);
			var ac=isUndef(i_is_activate)?true:i_is_activate;
			if(ac){
				this.activate();
			};
		}catch(e){
			throw new MiMicException(e);
		}
	};

	DEV.Mcu.prototype=
	{
		/**
		 * MiMicInterface
		 @private
		 */
		_mif:null,
		/**
		 * インスタンスがMCUと接続されているかをtrue/falseで返す。
		 * @name LPCXpresso1769.Mcu#isActive
		 * @function
		 * @return {boolean}
		 * インスタンスが接続中ならtrue
		 * @example
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”,false);
		 * mcu.events.onActivateChanged=function(f){
		 * 	if(!f){alert(f)};
		 * 	}
		 * mcu.activate(); 
		 */
		isActive:function isActive()
		{
			try{
				return this._mif.isConnected();
			}catch(e){
				throw new MiMicException(e);
			}
		},
		
		/**
		 * イベントハンドラの連想配列。メンバにイベントハンドラ関数を格納する。
		 イベントハンドラは以下の通り。
		 <ul>
		 <li>onActivateChanged as function(f:boolean) - インスタンスのアクティブ状態が変化したときに呼び出されるイベントである。fパラメータには、状態変化後のアクティブ状態値が入る。このハンドラが呼び出されるのは、ユーザが状態を切り替えたときと、システムが状態を維持できなくなったとき（例えばMCUが応答しない）である。</li>
		 </ul>
		 * @name LPCXpresso1769.Mcu#events
 		 * @field
 		 * @example
 		 * //show that MCU became active. 
 		 * var mcu=new LPCXpresso1769.Mcu("192.168.0.39",false);
 		 * mcu.events.onActivateChanged=function(f){
 		 * 	if(!f){alert(f)};
 		 * }
 		 * mcu.activate(); 		 
		 */
		events:null,
		/**
		 * ペリフェラルオブジェクトのホルダ。ペリフェラル名をキーにした、ペリフェラルオブジェクトの連想配列。
		 * MCUが、自身のペリフェラルオブジェクトを管理する為に使う。
		 * @private
		 */
		_phl_holder:null,
		/**
		 * SystemClockに関する情報を返す。
		 * @name LPCXpresso1769.Mcu#getClockInfo
		 * @function
		 * @return {cclk:MCUクロック [Hz] as int}
		 * クロック値を格納した連想配列である。
		 * @example
		 * // alert MCU clock
		 * var mcu=new LPCXpresso1769.Mcu("192.168.0.39");
		 * alert(mcu.getClockInfo().cclk);
		 */
		getClockInfo:function getClockInfo()
		{
			//CCLCK値(RemoteMCU側と合わせること。将来的にはactivateの時に決定する。)
			return {cclk:100*1000*1000};
		},
		/**
		 * インスタンスの状態をアクティブに切り替える。
		 * アクティブ化に成功すると、インスタンスは物理MCUと接続状態になる。
		 * コンストラクタで生成と同時にアクティブ化した場合は、コンストラクタ内で自動的にコールされる。
		 * 既にアクティブの場合、アクティブ化に失敗した場合に例外が発生する。
		 * @name LPCXpresso1769.Mcu#activate
		 * @function
		 * @example
		 * // MCU will be active.
		 * var mcu=new LPCXpresso1769.Mcu("192.168.0.39",false);
		 * mcu.activate();
		 */
		activate:function activate()
		{
			try{
				var _t=this;
				//既にアクティブならエラー。
				if(this.isActive()){
					throw new MiMicException("Already activated!");
				}
				//接続
				this._mif.connect(
					function(b){
						if(!b){
							//非同期切断を検出。
							if(_t.events.onActivateChanged!=null){
								_t.events.onActivateChanged(false);
							}
						}
					}
				);
				if(this.events.onActivateChanged!=null){
					this.events.onActivateChanged(true);
				}
			}catch(e){
				throw new MiMicException(e);
			}
		},
		/**
		 * インスタンスの状態を非アクティブに切り替える。
		 * 成功すると、インスタンスと物理MCUは切断状態になる。
		 * 既に非アクティブの場合、例外が発生する。
		 * @name LPCXpresso1769.Mcu#deactivate
		 * @function
		 * @example
		 * // MCU will be deactive.
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * mcu.events.onActivateChanged=function(f){
		 * 	if(!f){alert(f)};
		 * }
		 * mcu.deactivate();
		 */
		deactivate:function deactivate()
		{
			try{
				if(!this.isActive()){
					throw new MiMicException("Already activated!");
				}
	
				this._mif.disconnect();
				if(this.events.onActivateChanged!=null){
					this.events.onActivateChanged(false);
				}
			}catch(e){
				throw new MiMicException(e);
			}
		},
		/**
		 * 整形済みのMiMicBCを送信する。整形済みのMiMicBCは、MiMicTXT,MiMicDBを連結した文字列である。固定命令を送信するときに使う。
		 * @name LPCXpresso1769.Mcu#callMiMic^2
		 * @function
		 * @param {string} i_mimicbc
		 * MiMicBC。MiMicBCのフォーマットは、MiMicVM.pdf MiMicBCで定義する。
		 * @return {object as {MiMic result}}
		 * @example
		 * //send MiMic operations.
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * mcu.callMiMic(“ZAZZ.E”);//NOP .END	
		 */
		/**
		 * 整形済みのMiMicTXTと、数値配列のMiMicDBを連結して送信する。固定命令+パラメータで擬似関数を実現するときに便利である。
		 * @name LPCXpresso1769.Mcu#callMiMic^3
		 * @function
		 * @param {string} i_mimictxt
		 * MiMicTXT。MiMicTXTのフォーマットは、MiMicVM.pdf MiMicBCで定義する。
		 * @param {Array[int]} mimicdb
		 * 配列のINT値。値は関数によりMiMicDBに変換され、MiMicTXTに連結され、送信される。		 
		 * @return {object as {MiMic result}}
		 * @example
		 * //send MiMic operations.
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * mcu.callMiMic(“ZAZZ.E”,[]); //NOP .END
		 */
		/**
		 * MiMicBCをリンクしているMCUへMiMic Low level APIで同期送信し、結果を取得する。
		 * MiMic Low level APIのresult値が得られない場合、例外が発生する。この状況は、TCP/IPエラー、HTTPエラー、HTTPステータスエラー、MiMicVMのラインタイムエラー、MiMicRemoteMCUのフォールト等の原因で発生する。
		 * 関数が値を返した場合は、MiMicRemoteMCUが処理を完了し、正しい形式のステータスを受信できた場合である。例外が発生した場合は、リンクが破壊されている場合がある。リンクが破壊された場合は、1分以内にアクティブ状態が変化するので、それを検出して判断する。<br/>
		 * 引数の違いにより、数種類の呼び出し方がある。詳細は、callMiMic^nを参照。
		 * @name LPCXpresso1769.Mcu#callMiMic^1
		 * @function
		 * @param ...
		 * LPCXpresso1769.Mcu#callMiMic^nを参照
		 * @return {object as {MiMic result}}
		 * 応答値を格納した連想配列である。
		 * 詳細は 低レベルAPI。MiMicRemoteMcuInterface.execBc関数、または、MiMicVM.pdf Appendix 1. MiMicVM HTTP Interfaceを参照。 
		 */
		callMiMic:function callMiMic(/**/)
		{
			try{
				var bc;
				switch(arguments.length){
				case 1:
					//mimicBC
					bc=arguments[0];
					break;
				case 2:
					//mimicBC:string,db:array[int]
					bc=arguments[0]+array2MimicDb(arguments[1]);
					break;
				default:
					throw new MiMicException(e);
				}
				return this._mif.execBc(bc);
			}catch(e){
				throw new MiMicException(e);
			}
		},
		/**
		 * callMiMic関数のラッパーである。
		 * callMiMic関数成功後に、resultが成功(MVM_OK=0x0)であるかを確認し、それ以外であれば例外を発生させる。
		 * resultが0以外想定されないMiMicBCを実行するときに、便利である。<br/>
		 * 引数、戻り値については、callMiMic関数を参照。
		 * @name LPCXpresso1769.Mcu#callMiMicWithCheck
		 * @function
		 * @example
		 * //send 2 MiMic operations.
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * mcu.callMiMicWithCheck(“ZAZZ.E”,[]);
		 * mcu.callMiMicWithCheck(“ZAZZ.ENOOOO”);//exception!
		 */
		callMiMicWithCheck:function execBc2WithException(/**/)
		{
			try{
				var ret=this.callMiMic.apply(this,arguments);
				if(ret.result!=0x0){
					throw new MiMicException(EE.VM_RUNTIME,"Result="+ret.toString());
				}
				return ret;
			}catch(e){
				throw new MiMicException(e);
			}
		},
		/**
		 * ピン機能名から、Pinインスタンスを生成する。
		 * 生成されたピンは、ピン機能名の割り当てられている物理ピンで実現される。
		 * ピン機能名は、 LPCXpresso1769.P?[?]の[ピン機能名]で定義する文字列である。これは、UM10360　3.1.2.3  Chapter 8: LPC17xx Pin connect blockのfunction nameで定義される、PINSELレジスタに設定する文字列と一致する。
		 * @name LPCXpresso1769.Mcu#getPin^2
		 * @function
		 * @param {string} i_pin_function_name
		 * ピン機能名の文字列。 ex. “GPIO0.0” ,”AD0.0”
		 * GPIOについては、”GPIO Port 0.0”を”GPIO0.0”のように、省略すること。		 
		 * @return {object as PinObject}
		 * @example
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * //create gpio pin at PIN0[0]
		 * var gpiopin1=mcu.getPin("GPIO0.0");
		 */
		/**
		 * ピン識別子と機能名から、機能を割り当てたピンを生成する。組み合せにより、ピンを生成できない可能性もある。
		 * 組み合わせは、UM10360 3.1.2.3  Chapter 8: LPC17xx Pin connect blockを参照すること。
		 * @name LPCXpresso1769.Mcu#getPin^3
		 * @function
		 * @param {object as pin識別子} i_pin
		 * ピン識別子。指定できるのは、LPCXpresso1796.P?[?]である。
		 * @param {string as 機能名} i_function_name
		 * 機能名の文字列である。 指定できる文字列は、LPCXpresso1769.FUNC_NAMEで定義する。
		 * @return {object as PinObject}
		 * @example
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 *
		 * //create gpio pin at PIN0[1]
		 * var gpiopin2=mcu.getPin(LPCXpresso1769.P0[1],"GPIO");
		 * //or  mcu.getPin(LPCXpresso1769.P0[1],LPCXpresso1769.FUNC_SYMBOL.GPIO);
		 *
		 * //create AD at PIN0[23]
		 * var adpin1=mcu.getPin(LPCXpresso1769.P0[23],"AD"); 
		 * //or  mcu.getPin(LPCXpresso1769.P0[23],LPCXpresso1769.FUNC_SYMBOL.AD);
		 *
		 * //create AD at PIN0[3] (exception)
		 * var adpin2=mcu.getPin(LPCXpresso1769.P0[3],"AD");
		 */
		/**
		 * 物理ピンを制御するPinインスタンスを得る。
		 * 制御ペリフェラル、Pinを順に生成する方法と異なり、機能の実現に必要なインスタンスを自動的に準備できる。
		 * Pinが生成できない場合、例外が発生する。
		 * ペリフェラルが自動生成された場合、そのオプションはデフォルト値が採用される。既に生成済みのペリフェラルの場合は何もしない。PINのオプションは、デフォルト値が採用される。
		 * 引数の違いにより、数種類の呼び出し方がある。
		 * @name LPCXpresso1769.Mcu#getPin^1
		 * @function
		 * @param ...
		 * LPCXpresso1769.Mcu#getPin^nを参照
		 * @return {object as PinObject}
		 * 返却されるPinインスタンスの型は、機能により異なる。機能とPinインスタンスのクラスの対応は、以下の通りである。
		 * <ul>
		 * <li>GPIO - LPCXpresso1769.GpioPin</li>
		 * <li>AD - LPCXpresso1769.AdcPin</li>
		 * <li>PWM - LPCXpresso1769.PwmPin</li>
		 * </ul>
		 */
		getPin:function getPin(/*...*/)
		{
			switch(arguments.length){
			case 1://getPin:function getPin(i_pin_function)
				//pin_functionを持ってるPINを探す。
				var pin=getPinByFuncName(arguments[0]);
				//function名からペリフェラル名を得る。
				var phl_name=pinFuncName2PhlName(arguments[0]);
				//ピンコンストラクタとペリフェラルを使ってピンを生成。
				return this.getPeripheral(phl_name).getPin(pin);
			case 2://getPin:function getPin(i_pin,i_function_name)
				//function名からペリフェラル名を得る。(PINシンボル変換を流用)
				var phl_name=pinFuncName2PhlName(arguments[1]);
				return this.getPeripheral(phl_name).getPin(arguments[0]);
			default:
				throw new MiMicException();
			}
		},
		/**
		 * ポート（複数ピンで構成するIO）を制御するPortインスタンスを得るする。
		 * 制御ペリフェラル、Portを順に生成する方法と異なり、機能の実現に必要なインスタンスを自動的に生成する。
		 * Portが生成できない場合、例外が発生する。
		 * @name LPCXpresso1769.Mcu#getPort
		 * @function 
		 * @param {array[PIN識別子]} i_pins
		 * ポートを構成するピン識別子の配列。順序は生成するポートの説明を参照。
		 * @param {string} i_function_name
		 * 機能名の文字列である。 指定できる文字列は、LPCXpresso1769.FUNC_NAMEで定義する。
		 * @return {object as PortObject}
		 * 返却されるインスタンスのクラスは、機能により異なる。機能とインスタンスのクラスの対応は、以下の通りである。
		 * <ul>
		 * <li>GPIO - LPCXpresso1769.GpioPin</li>
		 * <li>AD - LPCXpresso1769.AdcPin</li>
		 * <li>PWM - LPCXpresso1769.PwmPin</li>
		 * </ul>		 
		 */
		getPort:function getPort(i_pins,i_function_name)
		{
			switch(arguments.length){
			case 2://getPin:function getPin(i_pins,i_function_name)
				//function名からペリフェラル名を得る。(PINシンボル変換を流用)
				var phl_name=pinFuncName2PhlName(i_function_name);
				return this.getPeripheral(phl_name).getPort(i_pins);
			default:
				throw new MiMicException();
			}
		},
		/**
		 * ペリフェラル名から、ペリフェラルオブジェクトを得る。
		 * 関数は、初めて要求されたペリフェラルについては、オブジェクトを生成し、MCUインスタンスに保存する。同じ名前のペリフェラルが再度要求されると、過去に生成したペリフェラルを返す。
		 * @name LPCXpresso1769.Mcu#getPeripheral
		 * @function
		 * @param {string as ペリフェラル名} i_phl_name
		 * ペリフェラル名は文字列である。指定できる文字列は、LPCXpresso1769.PHL_NAMEで定義する。
		 * @param {object as ペリフェラルオプション} i_opt
		 * ペリフェラルに渡すオプション値である。省略可能。ペリフェラルが新しく生成される場合（はじめてペリフェラルが設定されようとしているとき）に、コンストラクタのi_optに設定する。2回目以降の参照で設定されると、例外を発生する。値の構文は、取得しようとするペリフェラルのsetOpt関数のi_optパラメータの説明を参照すること。		 
		 * @return {object as Peripheral}
		 * 返却されるインスタンスのクラスは、ペリフェラル名により異なる。その対応は以下の通りである。
		 * <ul>
		 * 	<li>GPIO - LPCXpresso1769.Gpio</li>
		 * 	<li>ADC - LPCXpresso1769.Adc</li>
		 * 	<li>PWM - LPCXpresso1769.Pwm</li>
		 * </ul>
		 * @example
		 * //get GPIO peripheral
		 * var mcu=new LPCXpresso1769.Mcu(“192.168.0.39”);
		 * var gpio=mcu.getPeripheral(“GPIO”);		 
		 */
		getPeripheral:function getPeripheral(i_phl_symbol,i_opt)
		{
			//ペリフェラルアイテムが生成済か確認
			if(isUndef(this._phl_holder[i_phl_symbol])){
				//存在しなければPhlを生成。
				switch(i_phl_symbol){
				case "GPIO":new DEV.Gpio(this,i_opt);break;
				case "ADC":new DEV.Adc(this,i_opt);break;
				case "PWM":new DEV.Pwm(this,i_opt);break;
				default:
					throw new MiMicException("Unknown peripheral symbol "+i_phl_symbol);
				}
			}else{
				//2回目以降でi_optが設定されている？それはだめだよ。
				if(!isUndef(i_opt)){
					throw new MiMicException();
				}
			}
			//phlからregisterのコールバックがかかるはず。
			return this._phl_holder[i_phl_symbol];
		},
		/**
		 * [forSystem]システム関数。MCU依存のペリフェラルオブジェクトを登録する。
		 @private
		 */
		registerPhl:function registerPhl(i_phl,i_symbol)
		{
			try{
				//登録済みのシンボルは登録できない。
				if(!isUndef(this._phl_holder[i_symbol])){
					throw new MiMicException("Peripheral symbol "+i_symbol+" is already exist on mcu.");
				}
				this._phl_holder[i_symbol]=i_phl;
			}catch(e){
				throw new MiMicException(e);
			}
		}
	}
}());