/*	
 *	clipusar.c
 *
 *	UnityScript Array
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2017 Naoyuki Sawa
 *
 *	* Sat Jan 21 21:05:34 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	- UnityScriptのArrayクラスの仕様を勉強するために、C言語で仕様互換の関数を実装してみました。
 *	  内部処理は、GPtrArrayを利用しています。
 *	  勉強のためだけに作ったもので、実際のアプリケーションで当モジュールを利用する利点は有りません。
 *	  だいたいWebブラウザのJavaScriptのArrayと同じ仕様ですが、関数名の一文字目が大文字だったり、関数が少ない(?)違いが有るみたいです。
 *	  元々、UnityScriptと、WebブラウザのJavaScriptは別物なので、Arrayクラスの仕様も別物と考えて使う方が安全だと思います。
 *	  □参照資料:「Unity DOCUMENTATION - Version: 5.5 (switch to 5.6b) - Scripting API - Array」(https://docs.unity3d.com/ScriptReference/Array.html)
 */
#include "clip.h"
/****************************************************************************
 *	
 ****************************************************************************/
//Array.Array(int)
//Description
//  Creates an Array of a fixed size.
//  The Array class is only available in Javascript.
//  │var arr = new Array(5);
//  │print(arr.length);
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 本物のnew Array()では、Array.Array(int)形式の他にも、Array.Array()形式やArray.Array(value,value,...)形式が有ります。
//	  当実装では、Array.Array()形式のみを実装する事にしました。
struct UnityScriptArray* UnityScriptArray_New() {
	GPtrArray* array = g_ptr_array_new();
	return (struct UnityScriptArray*)array;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//[note]
//	- 本物のArrayではガーベージコレクションによって破棄されますが、当実装では明示的に破棄する必要が有ります。
//	  必要に応じて、当関数を呼び出す前に、配列の要素も明示的に破棄して下さい。
void UnityScriptArray_Free(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_unref(array);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//[note]
//	- 本物のArrayでの、'value=array[index]'に相当します。
void* UnityScriptArray_GetAt(struct UnityScriptArray* pArray, int iIndex) {
	GPtrArray* array = (GPtrArray*)pArray;
	return g_ptr_array_index(array, iIndex);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//[note]
//	- 本物のArrayでの、'array[index]=value'に相当します。
void UnityScriptArray_SetAt(struct UnityScriptArray* pArray, int iIndex, void* pValue) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_index(array, iIndex) = pValue;
}
/*--------------------------------------------------------------------------*/
//Array.length
//Description
//  The length property of the array that returns or sets the number of elements in array.
//  The Array.length controls how JS can access or set the size of the array.
//  The example JS script below shows that the length property of the array starts with a lower-case "l".
//  Note: This page includes a c-sharp script example.
//        The use of two array styles and their use of length are shown.
//  │function Start() {
//  │  var arr = Array("Hello", "World");
//  │  print(arr.length); //Prints "2"
//  │  arr.length = 5; //resizes the array to a size of 5
//  │}
//[note]
//	- 本物のArrayでの、'n=array.length'に相当します。
int UnityScriptArray_GetLength(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	return array->len;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//[note]
//	- 本物のArrayでの、'array.length=n'に相当します。
void UnityScriptArray_SetLength(struct UnityScriptArray* pArray, int nLength) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_set_size(array, nLength);
}
/*--------------------------------------------------------------------------*/
//Array.Add()
//Description
//  Adds value to the end of the array.
//  │var arr = new Array("Hello");
//  │arr.Add("World");
//  │print(arr); //prints "Hello", "World"
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 本物のArray.Add()は一度に複数の値を追加する事も出来ますが、当実装では一個づつ追加する事にしました。
//	  もしNULL終端の可変引数にしてしまうと、NULL値が追加出来なくなってしまうからです。
void UnityScriptArray_Add(struct UnityScriptArray* pArray, void* pValue) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_add(array, pValue);
}
/*--------------------------------------------------------------------------*/
//Array.RemoveAt()
//Description
//  Removes the element at index from the array.
//  │var arr = new Array("Hello", "and good morning", "World");
//  │arr.RemoveAt(1); //remove "and good morning"
//  │print(arr); //Prints "Hello,World"
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 引数iIndexに要素数以上の値を指定してはいけません。
//	  本物のArray.RemoveAt()でも同じ仕様です。
void UnityScriptArray_RemoveAt(struct UnityScriptArray* pArray, int iIndex) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_remove_index(array, iIndex);
}
/*--------------------------------------------------------------------------*/
//Array.Clear()
//Description
//  Empties the array.
//  The length of the array will be zero.
//  │var hello = new Array("Hello", "World");
//  │hello.Clear(); //hello now contains zero elements
//  Note: This is javascript only.
//        C# does not use this feature.
void UnityScriptArray_Clear(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_set_size(array, 0);
}
/*--------------------------------------------------------------------------*/
//Array.Push()
//Description
//  Adds value to the end of the array.
//  And returns the new length of the array.
//  │var arr = new Array("Hello");
//  │arr.Push("World");
//  │print(arr); //Prints "Hello", "World"
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- Array.Add()とArray.Push()の違いは、Array.Add()は戻り値が有りませんが、Array.Push()は追加後の要素数を返すという点のみです。
//	- 本物のArray.Push()は一度に複数の値を追加する事も出来ますが、当実装では一個づつ追加する事にしました。
//	  もしNULL終端の可変引数にしてしまうと、NULL値が追加出来なくなってしまうからです。
int UnityScriptArray_Push(struct UnityScriptArray* pArray, void* pValue) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_add(array, pValue);
	return array->len;
}
/*--------------------------------------------------------------------------*/
//Array.Pop()
//Description
//  Removes the last element of the array and returns it.
//  │var arr = new Array("Hello", "World");
//  │arr.Pop();
//  │print(arr); //Prints only "Hello"
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 空のArrayに対して当関数を呼び出してはいけません。
//	  本物のArray.Pop()でも同じ仕様です。
void* UnityScriptArray_Pop(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	return g_ptr_array_remove_index(array, array->len - 1);
}
/*--------------------------------------------------------------------------*/
//Array.Shift()
//Description
//  Removes the first element of the array and returns it.
//  │var arr = new Array("Hello", "World");
//  │arr.Shift(); //arr now only contains "World"
//  │print("World");
//  Note: This is javascript only.
//        C# does not use this feature.
void* UnityScriptArray_Shift(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	return g_ptr_array_remove_index(array, 0);
}
/*--------------------------------------------------------------------------*/
//Array.Unshift()
//Description
//  Unshift adds one or more elements to the beginning of an array and returns the new length of the array.
//  │var arr = new Array("Hello", "World");
//  │arr.Unshift("This", "is");
//  │print(arr); //Prints This, is, Hello, World
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 本物のArray.Unshift()は一度に複数の値を追加する事も出来ますが、当実装では一個づつ追加する事にしました。
//	  もしNULL終端の可変引数にしてしまうと、NULL値が追加出来なくなってしまうからです。
int UnityScriptArray_Unshift(struct UnityScriptArray* pArray, void* pValue) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_insert(array, 0, pValue);
	return array->len;
}
/*--------------------------------------------------------------------------*/
//Array.Sort()
//Description
//  │Sorts all Array elements.
//  │Var hello = new Array("c", "a", "b");
//  │hello.Sort();
//  │print(hello); //prints a, b, c.
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 当関数は、引数pArrayのオブジェクト自身を変更して、そのオブジェクト自身を戻り値とします。
//	  本物のArray.RemoveAt()でも同じ仕様です。
//	- 当実装は引数pArrayに指定された配列の要素が全て文字列であると想定して比較します。
//	  文字列でない要素を含んでいると正しく動作しません。
static int UnityScriptArray_Sort_compar(const void* a, const void* b) { return strcmp(*(char**)a, *(char**)b); }
struct UnityScriptArray* UnityScriptArray_Sort(struct UnityScriptArray* pArray) {
	GPtrArray* array = (GPtrArray*)pArray;
	g_ptr_array_sort(array, UnityScriptArray_Sort_compar);
	return pArray;
}
/*--------------------------------------------------------------------------*/
//Array.Concat()
//Description
//  Concat joins two or more arrays.
//  The method does not change the existing arrays and returns a copy of the joined array.
//  │function Start() {
//  │  var arr = new Array("Hello", "World");
//  │  var arr2 = new Array("!");
//  │  var joined = arr.Concat(arr2); //joined now contains all 3 strings
//  │  print(joined); //prints "Hello","World","!"
//  │}
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 本物のArray.Join()は配列の各要素を自動的にtoString()しますが、当実装は引数pArrayに指定された配列の要素が全て文字列であると想定します。
//	  文字列でない要素を含んでいると正しく動作しません。
//	- 戻り値の配列は、呼び出し側にてUnityScriptArray_Free()で解放して下さい。
struct UnityScriptArray* UnityScriptArray_Concat(struct UnityScriptArray* pArray/*NULL可*/, ... /*,NULL*/) {
	struct UnityScriptArray* pNewArray = UnityScriptArray_New();
	{
		va_list ap;
		va_start(ap, pArray);
		while(pArray) {
			int iIndex, nLength = UnityScriptArray_GetLength(pArray);
			for(iIndex = 0; iIndex < nLength; iIndex++) {
				void* pValue = UnityScriptArray_GetAt(pArray, iIndex);
				UnityScriptArray_Add(pNewArray, pValue);
			}
			pArray = va_arg(ap, struct UnityScriptArray*);
		}
	}
	return pNewArray;
}
/*--------------------------------------------------------------------------*/
//Array.Join()
//Description
//  Joins the contents of an array into one string.
//  The elements are seperated by the seperator string and returns a copy of the joined array.
//  │function Start() {
//  │  var arr = new Array("Hello", "World");
//  │  //Prints "Hello, World"
//  │  print(arr.join(", "));
//  │}
//  Note: This is javascript only.
//        C# does not use this feature.
//[note]
//	- 本物のArray.Join()は配列の各要素を自動的にtoString()しますが、当実装は引数pArrayに指定された配列の要素が全て文字列であると想定します。
//	  文字列でない要素を含んでいると正しく動作しません。
//	- 戻り値の文字列は、呼び出し側にてfree()で解放して下さい。
char* UnityScriptArray_Join(struct UnityScriptArray* pArray, const char* pSeperator) {
	GPtrArray* array = (GPtrArray*)pArray;
	return strjoinv(pSeperator, (char**)array->pdata);	//当ライブラリのgarray.cの実装では常に(zero_terminated=1)に相当する構造になっており、そのままでstrv形式と見なせる事を利用しています。
}
/****************************************************************************
 *	テストスイート
 ****************************************************************************/
#if 0
int app_main(int argc, char* argv[]) {
	struct UnityScriptArray *a1,*a2;
	char* s;
	a1 = UnityScriptArray_New();
	UnityScriptArray_Add(a1,"Alpha");				//		["Echo"]
	UnityScriptArray_Clear(a1);					//		[]
	UnityScriptArray_Add(a1,"Alpha");				//		["Alpha"]
	printf("%d\n",UnityScriptArray_Push(a1,"Delta"));		//2		["Alpha","Delta"]
	UnityScriptArray_SetAt(a1,1,"Bravo");				//		["Alpha","Bravo"]
	printf("%d\n",UnityScriptArray_Unshift(a1,"Charlie"));		//3		["Charlie","Alpha","Bravo"]
	if(UnityScriptArray_Sort(a1) != a1) { DIE(); }			//		["Alpha","Bravo","Charlie"]
	s = UnityScriptArray_Join(a1,","); printf("%s\n",s); free(s);	//"Alpha,Bravo,Charlie"
	a2 = UnityScriptArray_Concat(a1,a1,a1,NULL);			//		["Alpha","Bravo","Charlie","Alpha","Bravo","Charlie","Alpha","Bravo","Charlie"]
	UnityScriptArray_SetLength(a2,5);				//		["Alpha","Bravo","Charlie","Alpha","Bravo"]
	printf("%d\n",UnityScriptArray_GetLength(a2));			//5
	printf("%s\n",UnityScriptArray_GetAt(a2,1));			//"Bravo"
	printf("%s\n",UnityScriptArray_GetAt(a2,3));			//"Alpha"
	printf("%s\n",UnityScriptArray_Pop(a2));			//"Bravo"	["Alpha","Bravo","Charlie","Alpha"]
	printf("%s\n",UnityScriptArray_Shift(a2));			//"Alpha"	["Bravo","Charlie","Alpha"]
	UnityScriptArray_RemoveAt(a2,1);				//		["Bravo","Alpha"]
	UnityScriptArray_Free(a1);
	UnityScriptArray_Free(a2);
	return 0;
}
#endif
