/**
 * @fileOverview LPC1769のポート(ピンセット)を制御するクラスを定義する。
 */

(function(){
var DEV=LPC1769;
var BCF=DEV._BCF;
var EE=DEV._EE;
var isUndef=MiMicLib.isUndef;
var cloneAssoc=MiMicLib.cloneAssoc;
var isArray=MiMicLib.isArray;
/**
 * @private
 * レジスタの内容
 */
var REG={
	_PINSEL:[0x4002C000,0x4002C004,0x4002C008,0x4002C00C,0x4002C010,null,null,0x4002C0C0,null,0x4002C024,0x4002C028],
	_PINMODE:[0x4002C040,0x4002C044,0x4002C048,0x4002C04C,0x4002C050,0x4002C054,0x4002C058,0x4002C05C,null,0x4002C064],
	_PINMODE_OD:[0x4002C068,0x4002C06C,0x4002C070,0x4002C074,0x4002C078],
};
/**
 * LPCXPresso1769.Port (Port)クラスのコンストラクタ。
 * ピン識別子を元に、MCUに関連付けたPortインスタンスを生成する。
 * Portクラスは、複数の物理ピンをまとめて、操作インタフェイスを定義する。
 * PINSEL,PINMODE,PINMODE_ODレジスタを管理する。
 * @name LPC1769.Port
 * @constructor
 * @param {object as LPC1769.Mcu} i_mcu
 * インスタンスを結びつけるMcuオブジェクト。
 * @param {array[object as ピン識別子]} i_pinset
 * ポートを構成するピンの配列。
 * @param {array[associative array]} i_opt
 * インスタンス生成と同時にsetOpt関数で設定する値。省略時は無視する。
 * 詳細はsetOpt関数を参照。
 * このクラスは、上位クラスへピンの基本的な操作機能を提供する為のものであり、ユーザが直接使用することは（あまり）ない。
 * この関数は、MiMicの管理しているピン（ENET_?）も操作することが出来るが、操作してしまうとMiMicRemoteMcuとのコネクションが破壊されるので、注意すること。
 * @example
 * //create pin instance at P0[0]
 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
 * var port=new LPC1769.Port(mcu,[LPCXpresso.P0[0]]);
 */
DEV.Port=function Port(i_mcu,i_pinset,i_opts)
{
	try{
		this._mcu=i_mcu;
		//ピン情報の生成
		this.pins=new Array();
		for(var i=0;i<i_pinset.length;i++){
			this.pins.push({
				pin:i_pinset[i],
				reginfo:DEV.getPinRegInfo(i_pinset[i])
			});
		}
		//optionの設定
		if(!isUndef(i_opts)){
			this.setOpts(i_opts);
		}
	}catch(e){
		throw new MiMicException(e);
	}		
}

DEV.Port.prototype=
{
	_mcu:null,
	/**
	 * {array[object as ピン識別子]} Portを構成するピンの配列。Portコンストラクタのi_pinsetのコピー。
	 * @name LPC1769.Port#pins
	 */
	pins:null,
	/**
	 * 複数のピン設定をレジスタごとにまとめたBCを生成する。
	 * @private
	 * @param {array[associative array]| associative array} i_opts
	 * ピン設定情報の連装配列か、その配列。配列の時はPortを構成するPinと同じ数である必要がある。
	 * 単一のassociative arrayを指定した場合は、全てのピンに同じパラメータを設定する。
	 * <pre>associative array</>
	 * ピン設定連装配列 {sel:int,mode:int,od:int}
	 */
	BCF_setOpts:function BCF_setOpts(i_opts,i_db)
	{
		try{
			//10/5レジスタ分のワーク領域を作る。
			var d={
				selr:[0,0,0,0,0,0,0,0,0,0],
				selr_mask:[0,0,0,0,0,0,0,0,0,0],
				modr:[0,0,0,0,0,0,0,0,0,0],
				modr_mask:[0,0,0,0,0,0,0,0,0,0],
				odr:[0,0,0,0,0],
				odr_mask:[0,0,0,0,0]};
			var opts=isArray(i_opts)?i_opts:this.clonePinOptAssoc(i_opts);
			
			//レジスタ値、マスクの生成
			for(var i=0;i<this.pins.length;i++)
			{
				var pi=this.pins[i].reginfo;
				var opt=opts[i];
				if(!isUndef(opt.sel))
				{
					d.selr[pi.s]|=(opt.sel<<pi.smb);
					d.selr_mask[pi.s]|=(0x3<<pi.smb);
				}
				if(!isUndef(opt.mode))
				{
					d.modr[pi.m]|=(opt.mode<<pi.smb);
					d.modr_mask[pi.m]|=(0x3<<pi.smb);
				}
				if(!isUndef(opt.od))
				{
					d.odr[pi.o]|=(opt.od<<pi.ob);
					d.odr_mask[pi.o]|=(0x1<<pi.ob);
				}
			}
			//BCFを生成
			var bc="";
			for(var i=0;i<10;i++){
				if(d.selr_mask[i]!=0x0){
					bc+=BCF.setBit(REG._PINSEL[i],d.selr_mask[i],d.selr[i],0,i_db);
				}
				if(d.modr_mask[i]!=0x0){
					bc+=BCF.setBit(REG._PINMODE[i],d.modr_mask[i],d.modr[i],0,i_db);
				}
			}
			for(var i=0;i<5;i++){
				if(d.odr_mask[i]!=0x0){
					bc+=BCF.setBit(REG._PINMODE_OD[i],d.odr_mask[i],d.odr[i],0,i_db);
				}
			}
			return bc;
		}catch(e){
			throw new MiMicException(e);
		}
	},
	/**
	 * Portを構成するPinにオプション値を設定する。
	 * @name LPC1769.Port#setOpts
	 * @function
	 * @param {array[associative array] | associative array} i_opts
	 * ピンコンフィグレーションの連装配列、または配列である。
	 * ピンオプションの連装配列は、Pin#setOptを参照。
	 * @example
	 * //set GPIO,mode=1,open drain=0
	 * var mcu=new LPC1769.Mcu(“192.168.0.39”);
	 * var port=new LPC1769.Port(mcu,[LPCXpresso.P2[3],LPCXpresso.P2[4]]);
	 * //set option to each pin.
	 * port.setOpts([{sel:0,mode:1,od:0},{sel:0,mode:1,od:0}]);
	 * //set same option to all pin.
	 * port.setOpts({sel:0,mode:1,od:0});
	 */
	setOpts:function setOpts(i_opts)
	{
		try{
			var db=new Array();
			var bc=this.BCF_setOpts(i_opts,db);
			if(bc.length>0){
				//MiMicBCを生成して実行
				this._mcu.callMiMicWithCheck(bc+BCF.END,db);
			}
		}catch(e){
			throw new MiMicException(e);
		}
		return;
	},
	/**
	 * pinoptAssocを複製する。必要に応じて配列に展開する。
	 * @private
	 * @param {associative array array[associative array]| } i_pin_opt_assoc
	 */
	clonePinOptAssoc:function clonePinOptAssoc(i_pin_opt_assoc)
	{
		var ret=new Array();
		if(isArray(i_pin_opt_assoc)){
			//Arrayなら複製
			if(i_pin_opt_assoc.length!=this.pins.length){
				throw new MiMicException();
			}
			for(var i=0;i<this.pins.length;i++)
			{
				ret.push(cloneAssoc(i_pin_opt_assoc[i]));
			}
		}else{
			//非Arrayの時は同じ値をN個複製。
			for(var i=0;i<this.pins.length;i++)
			{
				ret.push(cloneAssoc(i_pin_opt_assoc));
			}
		}
		return ret;
	}	
}

}());