using System;
using System.Collections.Generic;
using MiMic.CsApi;
using UnityEngine;
namespace MiMic.CsApi.LPC1769
{
	public class Ni:MiMicClass
	{
		internal Mcu _mcu;
		public Ni(Mcu i_mcu)
		{
			this._mcu=i_mcu;
		}
		public class WM
		{
			public UInt32?[] reg;
			public WM()
			{
				reg=new UInt32?[8];
			}
			public WM(UInt32? v0,UInt32? v1,UInt32? v2,UInt32? v3,UInt32? v4,UInt32? v5,UInt32? v6,UInt32? v7)
			{
				reg=new UInt32?[8];
				reg[0]=v0;
				reg[1]=v1;
				reg[2]=v2;
				reg[3]=v3;
				reg[4]=v4;
				reg[5]=v5;
				reg[6]=v6;
				reg[7]=v7;
			}
			public const UInt32 PH=0;//Place holder
		}
		/**
		 * call
		 */
		
		/// <summary>
		///The call function result when Non blocking mode. 
		///ノンブロッキングモード時の戻り値型
		/// </summary>
		public class NBCallResult:NBResult
		{
			private Ni _parent;
			private Ni.WM _ref_wm;
			private UInt32[] _ref_stream;
			private bool _ret;
			private bool _parsed=false;
			public NBCallResult(Ni i_parent,MiMicRemoteMcuInterface.NBMiMicResult i_result,Ni.WM i_out_wm,UInt32[] i_out_stream):base(i_result)
			{
				this._parent=i_parent;
				this._ref_wm=i_out_wm;
				this._ref_stream=i_out_stream;
			}
			private void _parseResult()
			{
				if(!this.isDone){
					throw new MiMicException();
				}
				if(this._parsed){
					return;
				}
				this._ret=this._parent.PARSE_call(this.result,this._ref_wm,this._ref_stream);
				this._parsed=true;
			}
			public bool getRet()
			{
				//try parseing
				this._parseResult();
				return this._ret;
			}
			public Ni.WM getWm()
			{
				//try parseing
				this._parseResult();
				return this._ref_wm;
			}
			public UInt32[] getStream()
			{
				//try parseing
				this._parseResult();
				return this._ref_stream;
			}
		}
		
		private string BCF_call(UInt32 i_id,Ni.WM i_in_wm,UInt32[] i_in_stream,Ni.WM o_out_wm,List<UInt32> i_db)
		{
			string bc="";
			//WM初期化命令
			if(i_in_wm!=null)
			{
				//wmがあれば、初期化命令を追加。
				//LD命令を直書き
				for(int i=0;i<8;i++){
					if(i_in_wm.reg[i]!=null){
						bc+="FB"+MiMicLib.hexout((UInt32)i,2)+MiMicLib.hexout((UInt32)i_in_wm.reg[i],8);
					}
				}
			}
			if(i_in_stream!=null)
			{
				//streamをセット
				for(int i=0;i<i_in_stream.Length;i++){
					i_db.Add(i_in_stream[i]);
				}
			}
			//CALL命令を直書き
			bc+="ZF"+MiMicLib.hexout(i_id,8);
			//
			if(o_out_wm!=null)
			{
				//o_out.wmxがあれば、回収用命令を追記
				//SGET命令を直書き
				for(int i=0;i<8;i++){
					if(o_out_wm.reg[i]!=null){
						bc+="EE"+MiMicLib.hexout((UInt32)i,2);
					}
				}
			}
			return bc;			
		}
		private bool PARSE_call(MiMicRemoteMcuInterface.MiMicResult i_result,Ni.WM o_out_wm,UInt32[] o_out_stream)
		{
			if(i_result.result!=0x00){
				//失敗.理由はresultの内容。
				return false;
			}
			int num_of_wm=0;
			if(o_out_wm!=null)
			{
				for(int i=0;i<8;i++){
					if(o_out_wm.reg[i]!=null){
						num_of_wm++;
					}
				}
			}
			//streamから値を回収
			//streamは、関数の返したストリーム+WMの返却値
			if(o_out_wm!=null){
				//WM回収のためにSGET命令を直書き
				int offset=i_result.stream.Count-num_of_wm;
				for(int i=0;i<8;i++){
					if(o_out_wm.reg[i]!=null){
						o_out_wm.reg[i]=i_result.stream[offset];
						offset++;
					}
				}
			}
			//o_out.streamがあれば、値を回収。
			if(o_out_stream!=null){
				//数が一致しなければエラー
				if(o_out_stream.Length!=i_result.stream.Count-num_of_wm){
					return false;
				}
				for(int i=0;i<i_result.stream.Count-num_of_wm;i++){
					o_out_stream[i]=i_result.stream[i];
				}
			}
			return true;
		}
		
		public bool call(UInt32 i_id,Ni.WM i_in_wm,UInt32[] i_in_stream,Ni.WM o_out_wm,UInt32[] o_out_stream)
		{
			List<UInt32> db=new List<UInt32>();
			string bc=this.BCF_call(i_id,i_in_wm,i_in_stream,o_out_wm,db);
			//実行
			return this.PARSE_call(
				this._mcu.callMiMic(bc+LPCXpresso1769._BCF.END,db),
			    o_out_wm,o_out_stream);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="i_id">
		/// A <see cref="UInt32"/>
		/// </param>
		/// <param name="i_in_wm">
		/// A <see cref="Ni.WM"/>
		/// </param>
		/// <param name="i_in_stream">
		/// A <see cref="UInt32[]"/>
		/// </param>
		/// <param name="o_out_wm">
		/// レジスターを受け取る変数。受け取りたいレジスタに、0又はPHを指定することで、その要素にレジスタ値を得ることが出来る。
		/// この変数の参照は、戻り値のオブジェクトに引き継がれる。
		/// /// A <see cref="Ni.WM"/>
		/// </param>
		/// <param name="o_out_stream">
		/// 出力ストリームを受け取る変数。要素数は、RPCコールの返すストリーム要素数と一致している必要がある。
		/// この変数の参照は、戻り値のオブジェクトに引き継がれる。
		/// A <see cref="UInt32[]"/>
		/// </param>
		/// <returns>
		/// A <see cref="NBCallResult"/>
		/// </returns>
		public NBCallResult callNB(UInt32 i_id,Ni.WM i_in_wm,UInt32[] i_in_stream,Ni.WM o_out_wm,UInt32[] o_out_stream)
		{
			List<UInt32> db=new List<UInt32>();
			string bc=this.BCF_call(i_id,i_in_wm,i_in_stream,o_out_wm,db);
			return new NBCallResult(this,this._mcu.callMiMicNB(bc+LPCXpresso1769._BCF.END,db),o_out_wm,o_out_stream);
		}
	}
}