/*
 *	jtstring.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"

/*****************************************************************************
 *	JTSTRING
 *****************************************************************************/

typedef struct _JTSTRING {
	int refcnt;
	JTCODE_INTERFACE* jtcode_interface;
	JTSTRING_INTERFACE* jtstring_interface;
	ARITHMETIC_INTERFACE* arithmetic_interface;
	COMPARE_INTERFACE* compare_interface;
	//
	char* str;	/* 文字列 */
} JTSTRING;

IMPLEMENT_JTCODE_INTERFACE(JTSTRING, jtstring)
IMPLEMENT_JTSTRING_INTERFACE(JTSTRING, jtstring)
IMPLEMENT_ARITHMETIC_INTERFACE(JTSTRING, jtstring)
IMPLEMENT_COMPARE_INTERFACE(JTSTRING, jtstring)

const INTERFACE_MAP jtstring_interface_map[] = {
	{ JTCODE_INTERFACE_ID, offsetof(JTSTRING, jtcode_interface), &jtcode_interface },
	{ JTSTRING_INTERFACE_ID, offsetof(JTSTRING, jtstring_interface), &jtstring_interface },
	{ ARITHMETIC_INTERFACE_ID, offsetof(JTSTRING, arithmetic_interface), &arithmetic_interface },
	{ COMPARE_INTERFACE_ID, offsetof(JTSTRING, compare_interface), &compare_interface },
	{ 0/*終端*/ },
};

JTCODE_INTERFACE**
jtstring_create(const char* str)
{
	JTSTRING* this = calloc(1, sizeof(JTSTRING));
	if(!this) {
		DIE(); /* メモリ不足 */
	}
	this->refcnt = 1;
	apply_interface_map(this, jtstring_interface_map);

	/* 文字列を複製して、格納します。 */
	this->str = strdup(str);
	if(!this->str) {
		DIE(); /* メモリ不足 */
	}

	return &this->jtcode_interface;
}

static void
jtstring_delete(JTSTRING* this)
{
	/* 文字列を解放します。 */
	free(this->str);

	free(this);
}

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

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

/*****************************************************************************
 *	JTSTRING_INTERFACE
 *****************************************************************************/

static const char*
jtstring_get_string(JTSTRING_INTERFACE** intf)
{
	JTSTRING* this = CONTAINING_RECORD(intf, JTSTRING, jtstring_interface);

	/* 文字列を返します。 */
	return this->str;
}

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

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

static JTCODE_INTERFACE**
arithmetic_add(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	JTSTRING* this = CONTAINING_RECORD(intf, JTSTRING, arithmetic_interface);
	//
	JTSTRING_INTERFACE** jtstring = NULL;
	STRING_BUFFER_INTERFACE** string_buffer = NULL;

	/* 文字列バッファを作成します。 */
	safe_attach(string_buffer, string_buffer_create());

	/* 追加する文字列内容を取得します。 */
	safe_assign_interface(jtstring, code, JTSTRING_INTERFACE);
	if(!jtstring) {
		DIE(); /* JTSTRING_INTERFACEが必要 */
	}

	/* 文字列バッファにて、二つの文字列内容を連結します。 */
	(*string_buffer)->append_string(string_buffer, this->str);
	(*string_buffer)->append_string(string_buffer, (*jtstring)->get_string(jtstring));

	/* 文字列バッファの内容から、新しい文字列コードを作成します。 */
	code = NULL; /* 引数codeの参照カウントを減らさないために必要! */
	safe_attach(code, jtstring_create((*string_buffer)->lock(string_buffer, 0)));

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

	return code;
}

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

static JTCODE_INTERFACE**
arithmetic_mul(ARITHMETIC_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	JTSTRING* this = CONTAINING_RECORD(intf, JTSTRING, arithmetic_interface);
	//
	JTINT_INTERFACE** jtint = NULL;
	STRING_BUFFER_INTERFACE** string_buffer = NULL;
	//
	int i;
	int cnt;
	int len;
	int total_len;
	char* buf;

	/* 文字列バッファを作成します。 */
	safe_attach(string_buffer, string_buffer_create());

	/* 繰り返し回数を取得します。 */
	safe_assign_interface(jtint, code, JTINT_INTERFACE);
	if(!jtint) {
		DIE(); /* JTINT_INTERFACEが必要 */
	}
	cnt = (*jtint)->get_value(jtint);

	/* 文字列バッファにて、自分の文字列内容を、繰り返し回数分、連結します。 */
	len = strlen(this->str);
	total_len = len * cnt;
	buf = (*string_buffer)->lock(string_buffer, total_len);
	for(i = 0; i < cnt; i++) {
		memcpy(buf, this->str, len);
		buf += len;
	}
	(*string_buffer)->unlock(string_buffer, total_len);

	/* 文字列バッファの内容から、新しい文字列コードを作成します。 */
	code = NULL; /* 引数codeの参照カウントを減らさないために必要! */
	safe_attach(code, jtstring_create((*string_buffer)->lock(string_buffer, 0)));

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

	return code;
}

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

/*****************************************************************************
 *	COMPARE_INTERFACE
 *****************************************************************************/

static int
compare_compare(COMPARE_INTERFACE** intf, JTCODE_INTERFACE** code)
{
	JTSTRING* this = CONTAINING_RECORD(intf, JTSTRING, compare_interface);
	//
	JTSTRING_INTERFACE** jtstring = NULL;
	//
	const char* str;

	/* 相手の文字列内容を取得します。 */
	safe_assign_interface(jtstring, code, JTSTRING_INTERFACE);
	if(!jtstring) {
		DIE(); /* JTSTRING_INTERFACEが必要 */
	}
	str = (*jtstring)->get_string(jtstring);

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

	/* 自分の文字列内容と、相手の文字列内容の、比較結果を返します。 */
	return strcmp(this->str, str);
}

