/**
 * @fileOverview LPC1769の、GPIOペリフェラル、ピンの制御クラスを定義する。
 */

(function(){
var DEV=LPC1769;
var BCF=DEV._BCF;
var EE=DEV._EE;
var isUndef=MiMicLib.isUndef;
var cloneAssoc=MiMicLib.cloneAssoc;
var isArray=MiMicLib.isArray;
/**
 * LPCXPresso1769.Gpio (Gpio)クラスのコンストラクタ。
 * MCUに関連付けしたGpioペリフェラルを生成する。
 * GPIOペリフェラルは、物理的には存在しない仮想ペリフェラルである。GPIOを集中管理するために定義している。
 * @name LPC1769.Gpio
 * @constructor
 * @param {object as LPC1769.Mcu} i_mcu
 * インスタンスを結びつけるMcuオブジェクト。
 * @param {associative array} i_opt
 * 常に無視する。省略すること。
 * @example
 * //create GPIO (logical)pheripheral
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var gpio=new LPC1769.Gpio(mcu);
 */
DEV.Gpio=function Gpio(i_mcu,i_opt)
{
	try{
		this._mcu=i_mcu;
		i_mcu.registerPhl(this,"GPIO");
	}catch(e){
		throw new MiMicException(e);
	}
}
DEV.Gpio.prototype=
{
	_FIO_DIR :[0x2009C000,0x2009C020,0x2009C040,0x2009C060,0x2009C080],
	_FIO_PIN :[0x2009C014,0x2009C034,0x2009C054,0x2009C074,0x2009C094],
	_FIO_SET :[0x2009C018,0x2009C038,0x2009C058,0x2009C078,0x2009C098],
	_FIO_CLR :[0x2009C01C,0x2009C03C,0x2009C05C,0x2009C07C,0x2009C09C],
	_FIO_MASK:[0x2009C010,0x2009C030,0x2009C050,0x2009C070,0x2009C090],
	_mcu:null,
	BCF_getValues:function BCF_getValues(i_ch,i_mask,i_db)
	{
		/*GPIOレジスタからとってくるBC
		SGET #0;//GPIOの値レジスタアドレス
		SGET #1;//GPIOのMASKレジスタアドレス
		SGET #2;//MASKレジスタの値
		MSET #2,#1;//MASK設定
		MGET #3,#0;//値取得
		SPUT #3;//SPUT
		 */
		try{
			i_db.push(this._FIO_PIN[i_ch],this._FIO_MASK[i_ch],~i_mask);
			return "EA00EA01EA02DF0201DB0300EE03";
		}catch(e){
			throw new MiMicException(e);
		}		
	},
	/**
	 FIOPINとFIOMASKに値をセットする。
	 SGET #0;//GPIOの値レジスタアドレス
	 SGET #1;//GPIOのMASKレジスタアドレス
	 SGET #2;//MASKレジスタの値
	 SGET #3;//GPIO値
	 MPUT #2,#1;//MASK設定
	 MPUT #3,#0;//値設定
	* @private
	*/
	BCF_setValues:function BCF_setValues(i_ch,i_mask,i_value,i_db)
	{
		try{
			i_db.push(this._FIO_PIN[i_ch],this._FIO_MASK[i_ch],~i_mask,i_value);
			return "EA00EA01EA02EA03DF0201DF0300";
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * 
	 * @param {int} i_dir
	 * i_maskに合致したビットイメージ
	 * @private
	 */
	BCF_setDirs:function BCF_setDirs(i_ch,i_mask,i_dir,i_db)
	{
		try{
			return BCF.setBit(this._FIO_DIR[i_ch],i_mask,i_dir,0,i_db);
		}catch(e){
			throw new MiMicException(e);
		}
	},	
	/**
	 * GPIO機能を持つピンを取得する。
	 * ピン識別子で指定されるピンをGPIOペリフェラルと結合して、GpioPinを生成する。
	 * 関数は、LPC1769.GpioPinクラスのコンストラクタをコールして、GpioPinを生成する。失敗すると、例外をスローする。
	 * 生成ルールについての詳細は、LPC1769.GpioPinを参照。
	 * @name LPC1769.Gpio#getPin
	 * @function
	 * @param {object as ピン識別子} i_pin
	 * GPIO機能を割り当てるPINの識別子である。
	 * @param {associative array} i_opt
	 * GpioPinのコンストラクタに渡すオプション値を指定する。省略時はundefinedである。詳細はLPC1769.GpioPinを参照。
	 * @return {object as GpioPin}
	 * GpioPinクラスのオブジェクトである。
	 * @example
	 * //create GpioPin direction=out
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpio=new LPC1769.Gpio(mcu);
	 * var pin=gpio.getPin(LPC1769.P0[0],{dir:1});
	 */
	getPin:function getPin(i_pin,i_opt)
	{
		try{
			return new DEV.GpioPin(this,i_pin,i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * Gpio機能を持つポート(Pin集合)を取得する。
	 * ピン識別子で指定されるピンのセットをGpioペリフェラルと結合して、GpioPortを生成する。
	 * 関数は、GpioPortのコンストラクタをコールする。
	 * 生成ルールについては、GpioPort関数を参照すること。
	 * @name LPC1769.Gpio#getPort
	 * @function
	 * @param {array[ピン識別子]} i_pin
	 * Gpioポートを構成するPINの識別子の配列である。値は、LPC1769.Pn[m]のメンバ変数である。
	 * @param {associative array} i_opt
	 * GpioPortのコンストラクタに渡すオプション値。省略時はundefinedである。詳細はLPC1769.GpioPortを参照。
	 * @return {object as LPC1769.GpioPort}
	 * LPC1769.GpioPortクラスのオブジェクトである。
	 * @example
	 * //create 2AdcPort that has 2 pins.
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpio=new LPC1769.Gpio(mcu);
	 * var port=gpio.getPort([LPC1769.P0[0],LPC1769.P0[1]]);
	 */	
	getPort:function getPort(i_pins,i_opt)
	{
		try{
			return new DEV.GpioPort(this,i_pins,i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	}
}

/**
 * GPIO pinからGPIOInfoを取得
 * @private
 */
function makeGpioPinInfoArray(i_pins)
{
	try{
		var ret=new Array();
		for(var i=0;i<i_pins.length;i++){
			//pinの完全な機能名を得る。(得られれば機能がある。)
			var fn=DEV.completePinFunctionName(i_pins[i],"GPIO");
			//pin名からポートとビットを得る。
			var a=fn.substring(4).split(".");
			if(isNaN(a[0]) || isNaN(a[1])){
				throw new MiMicException(EE.INVALID_CFG);
			}
			//pin情報を構成。
			ret.push({port:parseInt(a[0]),bit:parseInt(a[1]),pin_sel:DEV.getPinSelByFunctionName(i_pins[i],fn)});
		}
		return ret;
	}catch(e){
		throw new MiMicException(e);
	}
}

/**
 * LPC1769.GpioPort (GpioPort)クラスのコンストラクタ。複数のGPIOピンを一括して操作する、パラレルポート等に使用する。
 * Gpioペリフェラルオブジェクトに、ピン識別子の配列で指定されたピン集合を関連付けて、GPIO機能を持つポートを生成する。
 * 関数は、ピン識別子を元に、そのピンがGPIO機能に接続できるかを調べる。全てのピンにGPIO機能を割り当てられない場合、例外が発生する。どのピンにGPIO機能が割り当てられるかは、MCUのスペックシートを参照すること。
 * @constructor
 * @name LPC1769.GpioPort
 * @param {object as LPC1769.Gpio} i_gpio
 * インスタンスを結びつけるGpioオブジェクト。
 * @param {array[pin識別子]} i_pins
 * ピン識別子の配列。指定できるのは、LPCXpresso1796.P?[?]である。順番は、このインスタンスの返す値の順序に影響する。
 * 0番目のピンはビット0に対応する。同様に、1番目のピンはビット1に対応する。
 * 組み合わせるピンは、同じ物理ポートに所属している必要がある。例えば、GPIO0.0とGPIO0.1は組み合わせられるが、GPIO0.0とGPIO1.1は組み合わせることが出来ない。
 * @param {associative array} i_opt
 * setOpt関数のi_optに渡すパラメタである。省略可能。省略時は{pin:{sel:auto}}を設定する。
 * autoは、関数が自動的に決定するPINSELの値である。詳細はsetOpt関数を参照。 
 * @example
 * //create [LPC1769.P0[0],LPC1769.P0[1]]
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var gpio=new LPC1769.Gpio(mcu);
 * var port=new LPC1769.GpioPort(adc,[LPC1769.P0[0],LPC1769.P0[1]]); 
 */
DEV.GpioPort=function GpioPort(i_gpio,i_pins,i_opt)
{
	try{
		this._gpio=i_gpio;
		//ピンセットを取得
		var pininfo=makeGpioPinInfoArray(i_pins);
		//pinが全て同じポートに所属しているか確認
		var p=pininfo[0].port;
		for(var i=1;i<pininfo.length;i++){
			if(p!=pininfo[i].port){
				throw new MiMicException("Invalid pin combination.");
			}
		}
		//ポートの生成
		this._port=new DEV.Port(i_gpio._mcu,i_pins);
		this._port_no=p;
		//GPIO用のマスクを生成。
		this._mask=0;
		for(var i=0;i<pininfo.length;i++){
			this._mask|=(0x1<<pininfo[i].bit);
		}

		//ピンオプションの生成
		var opt={};
		if(isUndef(i_opt)){
			opt.pin=this._port.clonePinOptAssoc({});
		}else{
			opt.pin=this._port.clonePinOptAssoc(isUndef(i_opt.pin)?{}:i_opt.pin);
		};
		//pinselの自動設定
		for(var i=0;i<pininfo.length;i++){
			if(isUndef(opt.pin[i].sel)){
				opt.pin[i].sel=pininfo[i].pin_sel;
			}
		}
		this._pininfo=pininfo;
		//ピンオプションの設定
		this.setOpt(opt);
	}catch(e){
		throw new MiMicException(e);
	}		
}
DEV.GpioPort.prototype=
{
	_pininfo:null,
	_gpio:null,
	_port_no:0,
	_port:null,
	_mask:0,
	/**
	 * Gpioポートにオプション値を設定する。
	 *　関数は、ポートを構成する全てのピンに、同一なオプション値を設定する。
	 * 設定可能な値は、LPC1769.GpioPin#setOptと同じである。	 
	 * @name LPC1769.GpioPort#setOpt
	 * @function
	 * @param {associative array | array[associative array]} i_opt
	 * GPIOピンのコンフィグレーションパラメタである。必要な値を格納した連想配列、またはその配列を指定する。
	 * 全てのピンに同じパラメータを指定する場合は、連装配列で指定する。個別に設定する場合は、連装配列を配列にして指定する。
	 * <pre>{i_opt:associative array}</pre>
	 * <ul>
	 * <li>pin(option) - array[{PIN_OPTION}] | {PIN_OPTION}</li>
	 * <li>dir(option) - array[{1bit int}] | {1bit int}</li>
	 * </ul>
	 * パラメータの詳細は、LPC1769.GpioPin#setOptを参照。
	 * @example
	 * //ポートを構成する全てのピンに同じパラメータを設定する場合
	 * setOpt({pin:{v1},dir:1});
	 * //ポートを構成するピンに個々にパラメータを設定する場合
	 * setOpt({pin:[{v1},{v2}],dir:[1,1]});
	 */	
	setOpt:function setOpt(i_opt)
	{
		try{
			var pininfo=this._pininfo;
			var db=new Array();
			//BCの生成
			var bc="";
			//pinの設定
			if(!isUndef(i_opt.pin)){
				bc+=this._port.BCF_setOpts(i_opt.pin,db);
			}
			//dir設定
			if(!isUndef(i_opt.dir)){
				var dir_bits=0x0;
				var dir_mask=0x0;
				if(isArray(i_opt.dir)){
					if(this._port.pins.length!=i_opt.dir.length){
						throw new MiMicException(DEV._EE.INVALID_ARG);
					}					
					for(var i=0;i<pininfo.length;i++){
						if(isUndef(i_opt.dir[i])){
							continue;
						}
						dir_mask|=(0x1<<pininfo[i].bit);
						dir_bits|=(i_opt.dir[i]<<pininfo[i].bit);
					}
				}else{
					dir_bits=this._mask*i_opt.dir;
					dir_mask=this._mask;
				}
				bc+=this._gpio.BCF_setDirs(this._port_no,dir_mask,dir_bits,db);
			}
			this._gpio._mcu.callMiMicWithCheck(bc+BCF.END,db);
			return;
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ポートの出力状態を設定する。
	 * 値は、nビットの整数値で指定する。nは、ポートを構成するピン数である。
	 * この関数は、directionを1(output)に設定したポートで使用できる。inputに設定したピンには使用できない。
	 * @name LPC1769.GpioPort#setValue
	 * @function
	 * @param {int} i_val
	 * nビットの出力値。ビット数は、このポートを構成するピンの数に一致する。2ビットのポートを構成したときに0x03を指定すると、2本のピンがON状態になる。
	 * @example
	 * //set P0[0] pin to “on”. If LED was connected pin it will turn on.
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * port=mcu.getPort([LPC1769.P0[0],LPC1769.P0[1]],"GPIO");
	 * port.setOpt({dir:1,pin:{mode:1,od:0}});
	 * port.setValue(0x3);	 
	 */	
	setValue:function setValue(i_val)
	{
		try{
			var v=0;
			for(var i=0;i<this._pininfo.length;i++){
				//セットするbit値を得る。
				var sv=((i_val>>i)&0x01);
				//値のセット
				v|=(sv<<this._pininfo[i].bit);
			}
			//値をセット
			var db=new Array();
			var bc=this._gpio.BCF_setValues(this._port_no,this._mask,v,db);
			this._gpio._mcu.callMiMicWithCheck(bc+BCF.END,db);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ポートの入力状態を返す。
	 * 値は、nビットの整数値である。nは、ポートを構成するピン数である。
	 * この関数は、directionを0(input)に設定したポートで使用できる。outputに設定したピンには使用できない。
	 * @function
	 * @return {int}
	 * nビットの整数値。ビット数は、このポートを構成するピンの数に一致する。2ビットのポートを構成したときに1,1の入力があると、0x03が返る。
	 * @name LPC1769.GpioPort#getValue
	 * @example
	 * //show P0[0] value
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var port=mcu.getPort([LPC1769.P0[0],LPC1769.P0[1]],"GPIO");
	 * port.setOpt({dir:0,pin:{mode:0,od:0}});
	 * alert(port.getValue());	 
	 */	
	getValue:function getValue()
	{
		try{
			var db=new Array();
			var bc=this._gpio.BCF_getValues(this._port_no,this._mask,db);
			var retval=this._gpio._mcu.callMiMicWithCheck(bc+BCF.END,db).stream[0];
			//値の再構成
			var v=0;
			for(var i=this._pininfo.length-1;i>=0;i--){
				//セットするbit値を得る。
				var sv=((retval>>this._pininfo[i].bit)&0x1);
				//値のセット(pinArrayの並びとビット並びが同じになるようにする)
				v=(v<<1)|sv;
			}
			return v;
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * 直列化された値パターンを出力する。
	 * ポートに連続した値パターンを出力するときに使用する。
	 * 出力速度はMCU依存であり、コントロールできない。
	 * この関数は、directionを1(output)に設定したポートで使用できる。inputに設定したピンには使用できない。
	 * @name LPC1769.GpioPort#outPatt
	 * @function
	 * @param {array[int]} i_val_array
	 * ビットパターンの配列。nビットの値(0 or 1)の配列を指定する。最大数は20である。ビット数は、このポートを構成するピンの数に一致する。
	 * [3,0,3,0]の場合、0,3,0,3の順に、パターンを出力する。
	 * @example
	 * //output  0x01,0x02 to P0[0]+P0[1]
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var port=mcu.getPin([LPC1769.P0[0],LPC1769.P0[1]],"GPIO");
	 * port.setOpt({dir:1,pin:{mode:1,od:0}});
	 * port.outPatt([0x01,0x02]);
	 */	
	outPatt:function outPatt(i_val_array)
	{
		try{
			var db=new Array();
			var bc="";
			for(var i2=0;i2<i_val_array.length;i2++){
				var v=0;
				for(var i=0;i<this._pininfo.length;i++){
					//セットするbit値を得る。
					var sv=((i_val_array[i2]>>i)&0x01);
					//値のセット(pinArrayの並びと最下位ビットの位置が同じになるように反転)
					v|=(sv<<this._pininfo[i].bit);
				}
				//値をセット
				bc+=this._gpio.BCF_setValues(this._port_no,this._mask,v,db);
			}
			this._gpio._mcu.callMiMicWithCheck(bc+BCF.END,db);
		}catch(e){
			throw new MiMicException(e);
		}
	}
}


/**
 * LPC1769.GpioPin (GpioPin)クラスのコンストラクタ。
 * Gpioペリフェラルオブジェクトにピン識別子で指定されたピンを関連付けて、GPIO機能を持つピンを生成する。
 * 関数は、ピン識別子を元に、そのピンがGPIO機能に接続できるかを調べる。ピンにGPIO機能を割り当てられない場合、例外が発生する。どのピンにGPIO機能が割り当てられるかは、MCUのスペックシートを参照すること。
 * ピンがGPIO機能を持たない場合、例外が発生する。
 * @name LPC1769.GpioPin
 * @constructor
 * @param i_gpio
 * インスタンスを結びつけるGpioオブジェクト。
 * @param {object as pin識別子} i_pin
 * ピン識別子。指定できるのは、LPCXpresso1796.P?[?]である。
 * @param i_opt
 * setOpt関数のi_optに渡すパラメタ。省略可能。
 * 省略時は、{pin:{sel:auto}}を使用する。autoは関数が自動的に決定PINSEL値である。
 * 詳細はsetOpt関数を参照。
 * @example
 * //create GPIO Port 0.0 dir=out
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var gpio=new LPC1769.Gpio(mcu);
 * var pin=new  LPC1769.GpioPin(gpio,P0[0],{dir:1}); 
 */
DEV.GpioPin=function GpioPin(i_gpio,i_pin,i_opt)
{
	try{
		//1ピンのポート
		this._gport=new DEV.GpioPort(i_gpio,[i_pin],i_opt);
	}catch(e){
		throw new MiMicException(e);
	}
}
DEV.GpioPin.prototype=
{
	_gport:null,
	/**
	 * GPIOピンにオプション値を設定する。
	 * @name LPC1769.GpioPin#setOpt
	 * @function
	 * @param {object as associative array} i_opt
	 * GPIOピンのコンフィグレーションパラメタである。必要な値を格納した連想配列で指定する。
	 * 全ての値を省略することは出来ない。連想配列のメンバは以下の通り。
	 * <pre>{dir:int,pin:object as associative array}</pre>
	 * <ul>
	 * <li>dir - ピンのIN/OUTを指定する1bitの値。1=out,0=in</li>
	 * <li>pin - LPC1769.Pin#setOpt関数のi_optに渡すパラメタである。</li>
	 * </ul>
	 * @example
	 * //set P0[0] to output  GPIO and mode=repeter and open drain=1
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpiopin=mcu.getPin(LPC1769.P0[0],"GPIO");
	 * gpiopin.setOpt({dir:1,pin:{mode:1,od:0}});
	 * gpiopin.setValue(1);	 
	 */	
	setOpt:function setOpt(i_opt)
	{
		try{
			this._gport.setOpt(i_opt);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * ピンの出力状態を設定する。
	 * この関数は、directionを1(output)に設定したピンで使用できる。inputに設定したピンには使用できない。
	 * @name LPC1769.GpioPin#setValue
	 * @function
	 * @param {int} i_val
	 * 1ビットの出力値。
	 * @example
	 * //set P0[0] pin to “on”. If LED was connected pin it will turn on.
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpiopin=mcu.getPin(LPC1769.P0[0],"GPIO");
	 * gpiopin.setOpt({dir:1,pin:{mode:1,od:0}});
	 * gpiopin.setValue(1);	 
	 */
	setValue:function setValue(i_val)
	{
		try{
			this._gport.setValue(i_val);
		}catch(e){
			throw new MiMicException(e);
		}
	},	
	/**
	 * ピンの入力状態を1/0で返す。
	 * この関数は、directionを0(input)に設定したピンで使用できる。outputに設定したピンには使用できない。
	 * @function
	 * @return {int}
	 * 1,又は0
	 * @name LPC1769.GpioPin#getValue
	 * @example
	 * //show P0[0] value
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpiopin=mcu.getPin(LPC1769.P0[0],"GPIO");
	 * gpiopin.setOpt({dir:0,pin:{mode:0,od:0}});
	 * alert(gpiopin.getValue());	 
	 */
	getValue:function getValue()
	{
		try{
			return this._gport.getValue();
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * 直列化されたビットパターンを出力する。
	 *　単純なビットパターンをピンに出力するときに使用する。
	 * 出力速度はMCU依存であり、コントロールできない。
	 * この関数は、directionを1(output)に設定したピンで使用できる。inputに設定したピンには使用できない。
	 * @name LPC1769.GpioPin#outPatt
	 * @function
	 * @param {array[int]} i_val_array
	 * ビットパターンの配列。1ビットの値(0 or 1)の配列を指定する。最大数は20である。
	 * [0,1,0,1]の場合、0,1,0,1の順に、パターンを出力します。
	 * @example
	 * //output  0101010100001010 to P0[0]
	 * var mcu=new LPC1769.Mcu("192.168.0.39");
	 * var gpiopin=mcu.getPin(LPC1769.P0[0],"GPIO");
	 * gpiopin.setOpt({dir:1,pin:{mode:1,od:0}});
	 * gpiopin.outPatt([0,1,0,1,0,1,0,1,0,0,0,0,1,0,1,0]);
	 */
	outPatt:function outPatt(i_val_array)
	{
		try{
			this._gport.outPatt(i_val_array);
		}catch(e){
			throw new MiMicException(e);
		}
	},
	
}

}());
