//
//	clipmisc_net.cs
//
//	汎用のユーティリティ関数、その他いろいろです。
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Thu Mar 16 22:31:07 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//
using System;
using System.Reflection;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	
		//*****************************************************************************
		//クラス配列の要素を自動的に作成する、ユーティリティ関数です。
		//[in]
		//	o		対称とするオブジェクト
		//[note]
		//	- 当関数は利便性のために作成したものですが、あまり当関数に依存し過ぎると判り辛くなる問題があると思います。
		//	  従って、当関数はあくまでもアプリケーション側だけで使用するようにし、ライブラリ側では使用しないで下さい。
		//	  ライブラリ側ではこれまで通り、明示的な処理でクラス配列の要素を作成するようにして下さい。
		//	  ライブラリ側のコードはさほど頻繁に書き換えないと思うので、多少手間が掛かっても問題無いと思います。
		//	- <例>
		//	  │using System;
		//	  │using static org.piece_me.libclip;
		//	  │class Inner { public int dummy; }
		//	  │class Outer {
		//	  │	public  Inner[]  a1 = new Inner[3];
		//	  │	private Inner[,] a2 = new Inner[2,2]; //NonPublicフィールドも対象となります。
		//	  │	public  Inner[]  a3;
		//	  │	static void Main() {
		//	  │		Outer o = new Outer();
		//	  │		InitInstance(o); //a1,a2の要素が作成されます。a3は配列を作成していないので要素も作成されません。
		//	  │		Console.WriteLine((o.a1[0] == null) ? "a1[0] == null" : "a1[0] != null");
		//	  │		Console.WriteLine((o.a1[1] == null) ? "a1[1] == null" : "a1[1] != null");
		//	  │		Console.WriteLine((o.a1[2] == null) ? "a1[2] == null" : "a1[2] != null");
		//	  │		Console.WriteLine((o.a2[0,0] == null) ? "a2[0,0] == null" : "a2[0,0] != null");
		//	  │		Console.WriteLine((o.a2[0,1] == null) ? "a2[0,1] == null" : "a2[0,1] != null");
		//	  │		Console.WriteLine((o.a2[1,0] == null) ? "a2[1,0] == null" : "a2[1,0] != null");
		//	  │		Console.WriteLine((o.a2[1,1] == null) ? "a2[1,1] == null" : "a2[1,1] != null");
		//	  │		Console.WriteLine((o.a3 == null) ? "a3 == null" : "a3 != null");
		//	  │		while(!Console.KeyAvailable) { }
		//	  │	}
		//	  │}
		//	- 2017/03/16現在の所は上記だけの機能ですが、今後もしかしたら、フィールドに属性を設定したりして、その他の初期化処理も行うようにするかも知れません。
		//	  clipprp.csモジュールのプロパティーテーブルと似た機能を、C#のオブジェクトに対して直接実行するようなイメージです。
		//	  まだ具体的な予定はありませんが、もしおこなう事になったら、当関数に機能を追加して行く予定です。(※TODO:)
		public static void InitInstance(object o) {
			//指定されたオブジェクトの、各フィールドについて…
			foreach(FieldInfo fi in o.GetType().GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance)) {
				//このフィールドの型を取得する。
				Type ft = fi.FieldType;
				//このフィールドが配列ならば…
				if(ft.IsArray) {
					//配列を取得する。
					Array a = (Array)fi.GetValue(o);
					//配列が作成されていたら…
					if(a != null) {
						//この配列の要素の型を取得する。
						Type et = ft.GetElementType();
						//インデクスリストを作成する。
						int[] indices = new int[a.Rank];
						//全てのインデクスについて…
						for(;;) {
							//この配列の要素がクラスならば…
							if(et.IsClass) {
								//要素を取得する。
								object e = a.GetValue(indices);
								//要素が作成されていなければ…
								if(e == null) {
									//要素を作成する。
									e = et.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
									//要素を格納する。
									a.SetValue(e, indices);
								}
								//この要素に対して再帰する。
								InitInstance(e);
							}
							//インデクスリストを次へ進める。
							int iRank;
							for(iRank = a.Rank - 1; iRank >= 0; iRank--) {
								if(++indices[iRank] < a.GetLength(iRank)) { break; }
								indices[iRank] = 0;
							}
							if(iRank < 0) { break; }	//全てのインデクスを走査したらループを抜ける。
						}
					}
				}
			}
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//型を指定してオブジェクトを作成し、InitInstance()を呼び出します。
		//[note]
		//	- <例>
		//	  │Outer o = (Outer)CreateInstance(typeof(Outer));
		public static object CreateInstance(Type t) {
			object o = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
			InitInstance(o);
			return o;
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		//上記の関数のテンプレート版です。
		//[note]
		//	- <例>
		//	  │Outer o = CreateInstance<Outer>();
		public static T CreateInstance<T>() where T : new() {
			T o = new T();
			InitInstance(o);
			return o;
		}
	}
}
