using System;
using System.Collections.Generic;
using System.Collections;
using MiMic.CsApi;

namespace MiMic.CsApi.LPC1769
{
	public class Adc :MiMicClass,Mcu.IBasicPeripheral
	{
		public class Option
		{
			public Peripheral.Option phl;
			public Option(Option s)
			{
				if(s!=null){
					this.phl=new Peripheral.Option(s.phl);
				}
			}
			public Option(Peripheral.Option i_phl)
			{
				this.phl=i_phl;
			}
			
		};
		private const UInt32 _AD0CR=0x40034000;
		private static UInt32[] _AD0DR=new UInt32[]{0x40034010,0x40034014,0x40034018,0x4003401C,0x40034020,0x40034024,0x40034028,0x4003402C};
		private Peripheral _phl=null;
		internal Mcu _mcu=null;
		internal string BCF_setOpt(Option i_opt,List<UInt32> i_db)
		{
			string bc="";
			if(i_opt.phl!=null){
				bc+=this._phl.BCF_setOpt(i_opt.phl,i_db);
			}
			return bc;
		}
		internal string BCF_setSels(UInt32 i_mask,UInt32 i_val,List<UInt32> i_db)
		{
			return LPCXpresso1769._BCF.setBit(_AD0CR,i_mask,i_val*i_mask,0,i_db);
		}
		internal string BCF_getAD0DR(int i_ch,List<UInt32> i_db)
		{
			return LPCXpresso1769._BCF.getMem(_AD0DR[i_ch],i_db);
		}		
		public Adc(Mcu i_mcu,Option i_opt)
		{
			this._mcu=i_mcu;
			//PHL生成。
			this._phl=new Peripheral(i_mcu,LPCXpresso1769.PHL.ADC,null);
			//設定値のロード
			Option opt=new Option(i_opt);
			if(opt.phl==null){
				opt.phl=new Peripheral.Option(null);
			}
			//デフォルト値設定
			if(opt.phl.power==null){opt.phl.power=1;};
	
			//初期化。
			string bc="";
			List<UInt32> db=new List<UInt32>();
			bc+=this.BCF_setOpt(opt,db);
			//0x00210000(PDN,BURSTを1)
			bc+=LPCXpresso1769._BCF.setMem(_AD0CR,0x00200400,db);
			bc+=LPCXpresso1769._BCF.setMem(_AD0CR,0x00210400,db);
			//
			this._mcu.callMiMicWithCheck(bc+LPCXpresso1769._BCF.END,db);
			//ペリフェラルをMCUに登録
			this._mcu.registerPhl(this,"ADC");
		}

		public void setOpt(Option i_opt)
		{
			List<UInt32> db=new List<UInt32>();
			string bc=this.BCF_setOpt(i_opt,db);
			this._mcu.callMiMicWithCheck(bc+LPCXpresso1769._BCF.END,db);
		}
		public AdcPin getPin(PinId i_pin,AdcPin.Option i_opt)
		{
			return new AdcPin(this,i_pin,i_opt);
		}
		public AdcPort getPort(PinId[] i_pins,AdcPin.Option i_opt)
		{
			return new AdcPort(this,i_pins,i_opt);
		}
		//for intelligent connection
		public object getPinObject(PinId i_id,object i_opt)
		{
			return getPin(i_id,(AdcPin.Option)i_opt);
		}
		public object getPortObject(PinId[] i_id,object i_opt)
		{
			return getPort(i_id,(AdcPin.Option)i_opt);
		}
	}
	
	
	public class AdcPort:MiMicClass
	{
		private class AdcPinInfo
		{
			public UInt32 port;
			public int ch;
			public UInt32 pin_sel;
			public AdcPinInfo(UInt32 i_port,int i_ch,UInt32 i_pin_sel)
			{
				this.port=i_port;
				this.ch=i_ch;
				this.pin_sel=i_pin_sel;
			}
		}
		private static AdcPinInfo pin2AdcPinInfo(PinId i_pin)
		{
			//pinの完全な機能名を得る。(得られれば機能がある。)
			string func_name=LPCXpresso1769.completePinFunctionName(i_pin,"AD");
			//portとbitを得る(AD0だけしか管理しないよ)
			var a=func_name.Substring(2).Split('.');
			return new AdcPinInfo(0,int.Parse(a[1]),LPCXpresso1769.getPinSelByFunctionName(i_pin,func_name));
		}
		private const UInt32 _PINSEL_AUTO_DETECT=0x0fffffff;
		private AdcPinInfo[] _pins;
		private Adc _adc;
		private Port _port;
		private UInt32 _port_no;
		private UInt32 _adcr_mask;
		public AdcPort(Adc i_adc,PinId[] i_pins,AdcPin.Option i_opt)
		{
			this._adc=i_adc;
			//ピンセットを取得
			this._pins=new AdcPinInfo[i_pins.Length];
			//pinに変換する。
			for(int i=0;i<i_pins.Length;i++){
				this._pins[i]=pin2AdcPinInfo(i_pins[i]);
			}
			//pinが全て同じポートに所属しているか確認
			UInt32 p=this._pins[0].port;
			for(int i=1;i<this._pins.Length;i++){
				if(p!=this._pins[i].port){
					throw new MiMicException("Invalid pin combination.");
				}
			}
			//ポートの生成
			this._port=new Port(i_adc._mcu,i_pins,null);
			this._port_no=p;
			//AD0CR用のマスクを生成。
			this._adcr_mask=0;
			for(int i=0;i<this._pins.Length;i++){
				this._adcr_mask|=((UInt32)0x1<<this._pins[i].ch);
			}
			//ピンオプションの生成
			AdcPin.Option opt=new AdcPin.Option(i_opt);
			if(opt.pin==null){
				opt.pin=new Pin.Option(null);
			}
			opt.sel=1;//ADxCRの値
			//設定が無ければ、ピンセレクタを自動に設定
			if(opt.pin.sel==null){opt.pin.sel=_PINSEL_AUTO_DETECT;}
			//ピンオプションの設定
			this.setOpt(opt);
		}
		public void setOpt(AdcPin.Option i_opt)
		{
			List<UInt32> db=new List<UInt32>();
			//BCの生成
			string bc="";
			//i_optの展開
			if(i_opt.pin!=null){
				Pin.Option[] optset=new Pin.Option[this._pins.Length];
				for(int i=0;i<this._pins.Length;i++){
					//pinselが_PINSEL_AUTO_DETECTならばauto。そうでなければundefinedも含めて設定
					UInt32? s=(i_opt.pin.sel==_PINSEL_AUTO_DETECT)?this._pins[i].pin_sel:i_opt.pin.sel;
					optset[i]=new Pin.Option(s,i_opt.pin.mode,i_opt.pin.od);
				}
				//portの設定
				bc+=this._port.BCF_setOpts(optset,db);
			}
			//隠し。ADxCR
			if(i_opt.sel!=null){
				bc+=this._adc.BCF_setSels(this._adcr_mask,(UInt32)i_opt.sel,db);
			}
			this._adc._mcu.callMiMicWithCheck(bc+LPCXpresso1769._BCF.END,db);
			return;
		}
		//
		//GetValues
		//
		private class NBgetValueseResult:NBUIntArrayResult
		{
			private AdcPort _parent;
			public NBgetValueseResult(AdcPort i_parent,MiMicRemoteMcuInterface.NBMiMicResult i_result):base(i_result)
			{
				this._parent=i_parent;
				return;
			}
			public override UInt32[] getValues()
			{
				if(!this.isDone){
					throw new MiMicException();
				}
				return this._parent.PARSE_getValues(this.result);
			}
		}		
		
		private string BCF_getValues(List<UInt32> i_db)
		{
			//メモリから値取得
			string bc="";
			for(int i=0;i<this._pins.Length;i++){
				bc+=this._adc.BCF_getAD0DR(this._pins[i].ch,i_db);
			}
			return bc;
		}
		private UInt32[] PARSE_getValues(MiMicRemoteMcuInterface.MiMicResult i_result)
		{
			//値の整形
			UInt32[] r=new UInt32[this._pins.Length];
			for(int i=0;i<this._pins.Length;i++){
				r[i]=((i_result.stream[i]>>4)&0x00000fff);
			}
			return r;
		}
		
		public UInt32[] getValues()
		{
			//メモリから値取得
			List<UInt32> db=new List<UInt32>();
			string bc=this.BCF_getValues(db);
			MiMicRemoteMcuInterface.MiMicResult ret=this._adc._mcu.callMiMicWithCheck(bc+LPCXpresso1769._BCF.END,db);
			return PARSE_getValues(ret);
		}
		public NBUIntArrayResult getValuesNB()
		{
			List<UInt32> db=new List<UInt32>();
			string bc=this.BCF_getValues(db);
			return new NBgetValueseResult(this,this._adc._mcu.callMiMicNB(bc+LPCXpresso1769._BCF.END,db));
		}
	}
	public class AdcPin:MiMicClass
	{
		public class Option
		{
			public UInt32? sel;
			public Pin.Option pin;
			public Option(UInt32? i_sel,Pin.Option i_pin)
			{
				this.sel=i_sel;
				this.pin=i_pin;
			}
			public Option(Option s)
			{
				if(s!=null){
					this.sel=s.sel;
					this.pin=s.pin;
				}
			}
		}
		protected AdcPort _aport;
		public AdcPin(Adc i_adc,PinId i_pin,AdcPin.Option i_opt)
		{
			this._aport=new AdcPort(i_adc,new PinId[]{i_pin},i_opt);
		}
		//
		//getValue
		//
		private class NBgetValueResult:NBUIntResult
		{
			private NBUIntArrayResult _array_result;
			public NBgetValueResult(NBUIntArrayResult i_result):base(i_result)
			{
				this._array_result=i_result;
			}
			public override UInt32 getValue()
			{
				if(!this.isDone){
					throw new MiMicException();
				}
				return this._array_result.getValues()[0];
			}
		}		
		
		
		public UInt32 getValue()
		{
			return this._aport.getValues()[0];
		}
		
		public NBUIntResult getValueNB()
		{
			return new NBgetValueResult(this._aport.getValuesNB());
		}
		public void setOpt(AdcPin.Option i_opt)
		{
			this._aport.setOpt(i_opt);
		}		
	}

}