/*
 *	jtobject.c
 *
 *	JoyToyインタプリタ
 *
 *	* Sun Jan 01 00:00:00 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Jan 31 00:00:00 JST 2006 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Jun 17 05:26:02 JST 2006 Naoyuki Sawa
 *	- FIELD_OFFSET()→offsetof()に変更。
 */
#include "app.h"

/*****************************************************************************
 *	JTOBJECT
 *****************************************************************************/

typedef struct _JTOBJECT {
	int refcnt;
	JTCODE_INTERFACE* jtcode_interface;
	JTOBJECT_INTERFACE* jtobject_interface;
	ARITHMETIC_INTERFACE* arithmetic_interface;
	//
	MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * slots_table;	/* スロットテーブル */
} JTOBJECT;

IMPLEMENT_JTCODE_INTERFACE(JTOBJECT, jtobject)
IMPLEMENT_JTOBJECT_INTERFACE(JTOBJECT, jtobject)
IMPLEMENT_ARITHMETIC_INTERFACE(JTOBJECT, jtobject)

const INTERFACE_MAP jtobject_interface_map[] = {
	{ JTCODE_INTERFACE_ID, offsetof(JTOBJECT, jtcode_interface), &jtcode_interface },
	{ JTOBJECT_INTERFACE_ID, offsetof(JTOBJECT, jtobject_interface), &jtobject_interface },
	{ ARITHMETIC_INTERFACE_ID, offsetof(JTOBJECT, arithmetic_interface), &arithmetic_interface },
	{ 0/*終端*/ },
};

JTCODE_INTERFACE**
jtobject_create(MAP/*<JTSYMBOL_INTERFACE**,JTCODE_INTERFACE**>*/ * slots_table)
{
	JTOBJECT* this = calloc(1, sizeof(JTOBJECT));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtobject_interface_map);

	/* スロットテーブルを複製して、格納します。 */
	this->slots_table = map_duplicate(slots_table);

	return &this->jtcode_interface;
}

JTCODE_INTERFACE**
jtobject_clone(JTOBJECT_INTERFACE** object)
{
	JTOBJECT* a = CONTAINING_RECORD(object, JTOBJECT, jtobject_interface);

	/* 複製元のオブジェクトと同じスロットテーブルの複製を持つ、新しいオブジェクトを作成します。 */
	return jtobject_create(a->slots_table);
}

JTCODE_INTERFACE**
jtobject_merge(JTOBJECT_INTERFACE** object1, JTOBJECT_INTERFACE** object2)
{
	JTOBJECT* a = CONTAINING_RECORD(object1, JTOBJECT, jtobject_interface);
	JTOBJECT* b = CONTAINING_RECORD(object2, JTOBJECT, jtobject_interface);
	//
	JTCODE_INTERFACE** value = NULL;
	JTSYMBOL_INTERFACE** symbol = NULL;
	//
	int i;
	int count;
	LIST/*<JTSYMBOL*>*/ * symbol_list;

	/* 空のスロットテーブルを持つ、新しいオブジェクトを作成します。 */
	JTOBJECT* this = calloc(1, sizeof(JTOBJECT));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtobject_interface_map);
	//
	this->slots_table = map_create(&jtsymbol_to_jtcode_map_info);

	/* 一つめのオブジェクトの全スロットを、新しいオブジェクトに追加します。 */
	symbol_list = map_keys(a->slots_table);
	count = list_size(symbol_list);
	for(i = 0; i < count; i++) {
		safe_attach(symbol, list_get(symbol_list, i));
		safe_attach(value, map_get(a->slots_table, symbol));
		map_put(this->slots_table, symbol, value);
	}
	list_delete(symbol_list);

	/* 二つめのオブジェクトの全スロットを、新しいオブジェクトに追加します。(重複時は上書き) */
	symbol_list = map_keys(b->slots_table);
	count = list_size(symbol_list);
	for(i = 0; i < count; i++) {
		safe_attach(symbol, list_get(symbol_list, i));
		safe_attach(value, map_get(b->slots_table, symbol));
		map_put(this->slots_table, symbol, value);
	}
	list_delete(symbol_list);

	/* 不要になったインターフェイスを解放します。 */
	safe_release(value);
	safe_release(symbol);

	return &this->jtcode_interface;
}

static void
jtobject_delete(JTOBJECT* this)
{
	/* スロットテーブルを削除します。 */
	map_delete(this->slots_table);

	free(this);
}

/*****************************************************************************
 *	JTCODE_INTERFACE
 *****************************************************************************/

static JTCODE_INTERFACE**
jtcode_run(JTCODE_INTERFACE** intf)
{
	JTOBJECT* this = CONTAINING_RECORD(intf, JTOBJECT, jtcode_interface);
	//
	JTCODE_INTERFACE** code = NULL;
	JTSYMBOL_INTERFACE** name = NULL;
	JTVAR_INTERFACE** var = NULL;
	//
	int i;
	int count;
	LIST/*<JTSYMBOL_INTERFACE**>*/ * name_list;

	/* オブジェクトの初期化に関する注意:
	 *
	 * * スロットの初期化処理は、オブジェクトが定義された位置の関数フレームで実行されます。
	 *   初期化するオブジェクトを実行対象とする、新しい関数フレームは、作成しません。
	 * - 従って、フィールド変数の初期化式として、オブジェクト自身のフィールド変数や、メソッドを参照することはできません。
	 *   <例>
	 *	var o1 = object {
	 *		var v1 = 1;
	 *		fun f1(x) { x * 2; }
	 *		var v2 = v1;     // エラー!! v1は参照できません。
	 *		var v3 = f1(10); // エラー!! f1は参照できません。
	 *	};
	 * - その代わりに、オブジェクトが定義された位置の関数フレームの、ローカル変数や、関数を参照することができます。
	 *   <例>
	 *	var v1 = 1;
	 *	fun f1(x) { x * 2; }
	 *	var o1 = object {
	 *		var v2 = v1;     // OK!! v2 = 1
	 *		var v3 = f1(10); // OK!! v3 = 20
	 *	};
	 */

	/* すべてのスロットを走査して... */
	name_list = map_keys(this->slots_table);
	count = list_size(name_list);
	for(i = 0; i < count; i++) {
		safe_attach(name, list_get(name_list, i)); /* フィールド変数、または、メソッドの名前を取得 */
		safe_attach(code, map_get(this->slots_table, name)); /* フィールド変数、または、メソッドを取得 */

		/* フィールド変数ならば、初期値を評価して、評価後の値を、あらためて登録します。
		 * run()実行後のスロットテーブルには、(JTVAR_INTERFACE**)は残っていません。
		 */
		safe_assign_interface(var, code, JTVAR_INTERFACE);
		if(var) { /* フィールド変数? */
			safe_attach(code, (*var)->get_code(var));
			safe_attach(code, (*code)->run(code));
			map_put(this->slots_table, name, code);
		}
	}
	list_delete(name_list); /* 忘れないで!! */

	/* 不要になったインターフェイスを解放します。 */
	safe_release(code);
	safe_release(name);
	safe_release(var);

	return safe_addref(intf); /* 呼び出し元のために+1 */
}

/*****************************************************************************
 *	JTOBJECT_INTERFACE
 *****************************************************************************/

static JTCODE_INTERFACE**
jtobject_get_field_value(JTOBJECT_INTERFACE** intf, JTSYMBOL_INTERFACE** symbol)
{
	JTOBJECT* this = CONTAINING_RECORD(intf, JTOBJECT, jtobject_interface);
	//
	JTCODE_INTERFACE** value = NULL;

	/* フィールド変数が定義されていたら... */
	if(map_contains(this->slots_table, symbol)) {

		/* フィールド変数の値を取得します。 */
		safe_attach(value, map_get(this->slots_table, symbol));
	}

	return value;
}

static void
jtobject_set_field_value(JTOBJECT_INTERFACE** intf, JTSYMBOL_INTERFACE** symbol, JTCODE_INTERFACE** value)
{
	JTOBJECT* this = CONTAINING_RECORD(intf, JTOBJECT, jtobject_interface);

	/* フィールド変数の値を設定します。 */
	if(!map_contains(this->slots_table, symbol)) {
		DIE(); /* フィールド変数が定義されていない */
	}
	map_put(this->slots_table, symbol, value);
}

/*****************************************************************************
 *	ARITHMETIC_INTERFACE
 *****************************************************************************/

static JTCODE_INTERFACE**
arithmetic_neg(ARITHMETIC_INTERFACE** intf)
{
	DIE(); /* 適用不可 */
}

static JTCODE_INTERFACE**
arithmetic_add(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	JTCODE_INTERFACE** result = NULL;
	JTOBJECT_INTERFACE** object1 = NULL;
	JTOBJECT_INTERFACE** object2 = NULL;

	/* 自分自身の、JTOBJECT_INTERFACEインターフェイスを取得します。 */
	safe_assign_interface(object1, intf, JTOBJECT_INTERFACE);
	if(!object1) {
		DIE(); /* 有り得ない */
	}

	/* 合成するオブジェクトの、JTOBJECT_INTERFACEインターフェイスを取得します。 */
	safe_assign_interface(object2, code, JTOBJECT_INTERFACE);
	if(!object2) {
		DIE(); /* オブジェクト以外を合成しようとした (読み込んだプログラムのエラー) */
	}

	/* 二つのオブジェクトを合成します。 */
	safe_attach(result, jtobject_merge(object1, object2));

	/* 不要になったインターフェイスを解放します。 */
	safe_release(object1);
	safe_release(object2);

	return result;
}

static JTCODE_INTERFACE**
arithmetic_sub(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	DIE(); /* 適用不可 */
}

static JTCODE_INTERFACE**
arithmetic_mul(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	DIE(); /* 適用不可 */
}

static JTCODE_INTERFACE**
arithmetic_div(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	DIE(); /* 適用不可 */
}

