using UnityEngine;
using System;
using System.Collections.Generic;
using System.Collections;
using MiMic.CsApi;
namespace MiMic.CsApi.LPC1769
{
	public class Mcu:MiMicClass
	{
		
		public delegate void TonActivateChanged(bool i_is_connect);
		public struct TClockInfo
		{
			public UInt32 cclk;
			public TClockInfo(UInt32 i_cclk)
			{
				this.cclk=i_cclk;
			}
		}
		internal interface IBasicPeripheral
		{
			object getPinObject(PinId i_id,object i_opt);
			object getPortObject(PinId[] i_id,object i_opt);
		}
		
		private static string array2MimicDb(List<UInt32> i_array)
		{
			string p="";
			for(int i=0;i<i_array.Count;i++){
				p+=MiMicLib.hexout(i_array[i],8);
			}
			return p;
		}
		private static PinId getPinByFName(PinId[] i_pintbl,string i_name)
		{
			for(var i=0;i<i_pintbl.Length;i++){
				if(i_pintbl[i]==null){
					continue;
				}
				if(LPCXpresso1769.hasPinFunctionName(i_pintbl[i],i_name)){
					return i_pintbl[i];
				}
			}
			return null;
		}
		private static PinId getPinByFuncName(string i_name)
		{
			PinId[][] tbl=new PinId[][]{LPCXpresso1769.P0,LPCXpresso1769.P1,LPCXpresso1769.P2,LPCXpresso1769.P3,LPCXpresso1769.P4};
			for(int i=0;i<tbl.Length;i++){
				PinId pin=getPinByFName(tbl[i],i_name);
				if(pin!=null){
					return pin;
				}
			}
			throw new MiMicException("pin function '"+i_name+"' not found");
		}
		/**
		 * PIN機能名から使用するペリフェラルシンボルを推定する。
		 @private
		 */
		private static string pinFuncName2PhlName(string i_pinfuncname)
		{
			//Pin function nameプレフィクスとペリフェラル名の対比表
			Hashtable TBL=new Hashtable(){
				{"GPIO","GPIO"},
				{"AD","ADC"},
				{"DA","DAC"},
				{"PWM","PWM"},
			};
		    foreach (DictionaryEntry de in TBL) {
				if(i_pinfuncname.CompareTo(de.Key)==0){
					return (string)de.Value;
				}
		    }
			//見つからぬ。
			throw new MiMicException("Peripheral for "+i_pinfuncname+" not found.");
		}		
		
		
		
		
		
		
		private MiMicRemoteMcuInterface _mif;
		public Mcu(MonoBehaviour i_bh,string i_cbname,string i_mimic_addr,bool i_is_activate)
		{
			this._Mcu_1(i_bh,i_cbname,i_mimic_addr,i_is_activate);
		}
		private void _Mcu_1(MonoBehaviour i_bh,string i_cbname,string i_mimic_addr,bool i_is_activate)
		{
			//メンバの初期化.
			//リモートインタフェイスの取得.
//			MiMicLib.log("construct");
			this._mif=new MiMicRemoteMcuInterface(i_bh,i_cbname,i_mimic_addr);			
			if(i_is_activate){
				this.activate();
			}
		}
		
		/**
		 * [for system]
		 * The function is called from Behaviour in periodic.
		 */ 
		public void periodicProc()
		{
			bool f=this._mif.periodic();
//			MiMicLib.log("periodic");
			if(this.onActivateChanged!=null){
				this.onActivateChanged(f);
			}
		}
		public TonActivateChanged onActivateChanged=null;
		private void callOnActivateChanged(bool i_param)
		{
			if(this.onActivateChanged!=null){
				this.onActivateChanged(i_param);
			}
		}

		
		public bool isActive()
		{
			return this._mif.isConnected();
		}
		public TClockInfo getClockInfo()
		{
			//CCLCK値(RemoteMCU側と合わせること。将来的にはactivateの時に決定する。).
			TClockInfo ret=new TClockInfo(100*1000*1000);
			return ret;
		}
		public void activate()
		{
			//既にアクティブならエラー。
			if(this.isActive()){
				throw new MiMicException("Already activated!");
			}
			//接続.
			this._mif.connect(this.callOnActivateChanged);
			//call the event.
			this.callOnActivateChanged(true);
		}
		public void deactivate()
		{
			if(!this.isActive()){
				throw new MiMicException("Already activated!");
			}
	
			this._mif.disconnect();
			this.callOnActivateChanged(false);
		}
		public MiMicRemoteMcuInterface.MiMicResult callMiMic(string i_bc)
		{
			return this._mif.execBc(i_bc);
		}
		public MiMicRemoteMcuInterface.MiMicResult callMiMic(string i_bc,List<UInt32> i_db)
		{
			return this._mif.execBc(i_bc+array2MimicDb(i_db));
		}
		public MiMicRemoteMcuInterface.NBMiMicResult callMiMicNB(string i_bc,List<UInt32> i_db)
		{
			return this._mif.NBexecBc(i_bc+array2MimicDb(i_db));
		}

		public MiMicRemoteMcuInterface.MiMicResult callMiMicWithCheck(string i_bc)
		{
			MiMicRemoteMcuInterface.MiMicResult h=this.callMiMic(i_bc);
			if(h.result!=0x0){
				throw new MiMicException("Result="+h.ToString());
			}
			return h;
		}
		public MiMicRemoteMcuInterface.MiMicResult callMiMicWithCheck(string i_bc,List<UInt32> i_db)
		{
			MiMicRemoteMcuInterface.MiMicResult h=this.callMiMic(i_bc,i_db);
			if(h.result!=0x0){
				throw new MiMicException("Result="+h.ToString());
			}
			return h;
		}

		private Dictionary<string,IBasicPeripheral> _phl_holder=new Dictionary<string, IBasicPeripheral>();
		public object getPin(string i_pin_function)
		{
			//pin_functionを持ってるPINを探す。
			PinId pin=Mcu.getPinByFuncName(i_pin_function);
			//function名からペリフェラル名を得る。
			string phl_name=pinFuncName2PhlName(i_pin_function);
				//ピンコンストラクタとペリフェラルを使ってピンを生成。
			return this.getBasicPeripheral(phl_name,null).getPinObject(pin,null);
		}
		public object getPin(PinId i_pin_id,string i_pin_function)
		{
			//function名からペリフェラル名を得る。(PINシンボル変換を流用)
			string phl_name=pinFuncName2PhlName(i_pin_function);
			return this.getBasicPeripheral(phl_name,null).getPinObject(i_pin_id,null);
		}
		public object getPort(PinId[] i_pins,string i_function_name)
		{
			//function名からペリフェラル名を得る。(PINシンボル変換を流用)
			string phl_name=pinFuncName2PhlName(i_function_name);
			return this.getBasicPeripheral(phl_name,null).getPortObject(i_pins,null);
		}
		public object getPeripheral(string i_phl_symbol,object i_opt)
		{
			return this.getBasicPeripheral(i_phl_symbol,null);
		}
		
		/**
		 * for interigent connection.
		 */
		internal IBasicPeripheral getBasicPeripheral(string i_phl_symbol,object i_opt)
		{
			//ペリフェラルアイテムが生成済か確認
			if(!this._phl_holder.ContainsKey(i_phl_symbol)){
				//存在しなければPhlを生成。
				switch(i_phl_symbol){
				case "GPIO":new Gpio(this,(Gpio.Option)i_opt);break;
				case "ADC":new Adc(this,(Adc.Option)i_opt);break;
				case "PWM":new Pwm(this,(Pwm.Option)i_opt);break;
				default:
					throw new MiMicException("Unknown peripheral symbol "+i_phl_symbol);
				}
			}else{
				//2回目以降でi_optが設定されている？それはだめだよ。
				if(i_opt!=null){
					throw new MiMicException();
				}
			}
			//phlからregisterのコールバックがかかるはず。
			return this._phl_holder[i_phl_symbol];
		}
		
		/**
		 * [forSystem]システム関数。MCU依存のペリフェラルオブジェクトを登録する。
		 @private
		 */
		internal void registerPhl(IBasicPeripheral i_phl,string i_symbol)
		{
			//登録済みのシンボルは登録できない。
			if(this._phl_holder.ContainsKey(i_symbol)){
				throw new MiMicException("Peripheral symbol "+i_symbol+" is already exist on mcu.");
			}
			this._phl_holder[i_symbol]=i_phl;
		}		
	}
}
