----------------------------------------------------------------------------------------------------------
-- 
-- OOPやるためのUtility関連。継承、およびObject基底クラス定義
--
--	すべてのクラスは必ず Object クラスを継承すること。
--
--	インスタンスかするための newメソッドは extends関数で当該クラスに付加されます。
--	コンストラクタが必要な場合は 当該クラスにメソッド「constructor」を定義してください。
--	newメソッドに与えられた引数はそのまま constructor メソッドへ引き渡されます。
--
--	コンストラクタが定義されていない場合、メンバ変数とか定義しないただの空テーブルで初期化します。
--
--	また、metatableへの定義は extends関数でやるので個別クラス側での setmetatableは不要です。
--	なお、親クラス(テーブル)参照は変数 super で追加定義されます。
--	自クラス(テーブル)への参照は変数 thisで追加定義されます。
--
--	superクラスのメソッドを明示的に呼び出す際には以下のようにしてください。
--
--		self.super.method(self, args);
--
--		⇒ superのメソッドを「:」ではなく「.」でよんでるのがポイント。
--		   「:」は暗黙的に第一引数にselfが定義される。
--		   super:method()で呼んじゃうとインスタンステーブルではなくクラステーブルが
--		   selfに割り当てられておかしくなるため。
--	
--	使い方
--		
--		◆コンストラクタあり
--
--		UserClass = extends(Object);
--		
--		function UserClass:constructor(...)
--			self.args = ...;
--			self.name = "UserClass";
--		end
--
--		function UserClass:toString()
--			return "UserClass";
--		end
--			・
--			・
--			・
--
--		◆コンストラクタなし
--
--		UserClassNoConstructor = extends(Object);
--
--		function UserClassNoConstructor:toString()
--			return "UserClassNoConstructor";
--		end
--			・
--			・
--			・
--
--		◆オーバーライドされた親メソッドを明示的に参照
--
--		ExtendsClass = extends(UserClass);
--
--		function ExtendsClass:constructor(name)
--			self.name = name;
--		end
--
--		function ExtendsClass:toString()
--			print(self.name, self.super.name);
--			return "ExtendsClass" .. self.super:toString();
--		end
--
--		function ExtendsClass:printArguments()
--			print(self.args);
--		end
--			・
--			・
--			・
--
----------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------
--	継承操作関数 extends
--		クラスメソッド定義、new メソッドの定義追加をします。
--		コンストラクタは最上位クラスから順に呼ばれます。
--		なので、インスタンス変数は全部継承されます。
----------------------------------------------------------------------------------------------------------
function extends(baseClass)
	-- クラス定義
	local newClass = {};
	local newClassMetatable = {__index = newClass};
	setmetatable(newClass, {__index = baseClass});

	-- コンストラクタコールバック用関数
	local callBackConstructor = function(callBack, currentClass, instance, ...)	
									if (currentClass == nil)
									then
										print("class is nil")
										return;
									end
									-- 跡で末尾再起に変更
									if (getmetatable(currentClass) ~= nil)
									then
										callBack(callBack, getmetatable(currentClass).__index, instance, unpack(arg));
									end

									-- コンストラクタがあれば呼びだし
									if (currentClass["constructor"] ~= nil)
									then
										-- 「:」 ではなく 「.」 で呼んでるのがポイント
										currentClass.constructor(instance, unpack(arg));
									end
								end;

	-- newClass定義へ new メソッドを付加。
	function newClass.new(...)
		local newInstance = {}
		setmetatable(newInstance, newClassMetatable);

		-- 自クラス、スーパークラスのコンストラクタをまとめて呼び出し。
		callBackConstructor(callBackConstructor, newClass, newInstance, unpack(arg));

		-- 親クラスとかほしければこの変数使う。
		-- まあ、getmetatable(self).__index でスーパークラスは取れるんだがｗ
		newInstance.this  = newClass;
		newInstance.super = baseClass;

		return newInstance;
	end

	return newClass;
end

----------------------------------------------------------------------------------------------------------
--	すべての基底クラス Object
--		toString() のみ実装。
----------------------------------------------------------------------------------------------------------
Object=extends({});

	function Object:constructor()
	end

	function Object:toString()
		return "基底クラス Object [" .. tostring(self) .. "]";
	end

	function Object:equals(object)
		return rawequal(self, object);
	end

----------------------------------------------------------------------------------------------------------
--	インスタンス名定義つき基底クラス NamedObject
--
--		本クラスを継承したクラスはメンバ変数に「name」が定義されます。
--		コンストラクタに渡すといろいろ微妙なので、メソッドgetName() を
--		オーバーライドして定義してね。
--		
----------------------------------------------------------------------------------------------------------
NamedObject=extends(Object);
	
	function NamedObject:constructor()
		self.name = "NamedObject";
	end

	function NamedObject:getName()
		return self.name;
	end

	function NamedObject:setName(name)
		self.name = name;
	end

----------------------------------------------------------------------------------------------------------
--	シリアライズ可能なオブジェクト
--		シリアライズ可能なオブジェクトは本クラスを継承してください。
--		別にしなくても良いけどね。シリアライズに便利なメソッドを共有化するだけの代物。
--		びみょーな感じ。
----------------------------------------------------------------------------------------------------------
Serializable=extends(Object);

	function Serializable:constructor()
	end

	function Serializable:serialize()
		error("serialize()メソッドをオーバーライドしてシリアライズ処理を記述してください。");
	end

	function Serializable:deserialize()
		error("deserialize()メソッドをオーバーライドしてデシリアライズ処理を記述してください。");
	end
	
	-- String⇒いろんな型変換
	function Serializable:toVariableTypes(value)
		local result = nil;
		
		-- String ⇒ boolean 変換
		result = self:toBoolean(value);
		if (result ~= nil)
		then
			return result;
		end
		
		-- String ⇒ Number 変換
		result = self:toNumber(value);
		if (result ~= nil)
		then
			return result;
		end
		
		-- 無変換 Stringのままで返す。
		return value;
	end
	
	-- String⇒boolean変換
	function Serializable:toBoolean(value)
		if (value == "true")
		then
			return true;
		elseif (value == "false")
		then
			return false;
		end
		return nil;
	end
	
	-- String⇒number変換
	function Serializable:toNumber(value)
		local result = tonumber(value);
		return result;
	end


function debugDump(o)
	local t = type(o)
	if t == "number" then
		return o
	elseif t == "string" then
		return string.format("%q", o)
	elseif t == "table" then
		local result = ""
		for k,v in pairs(o) do
			local tmp = debugDump(v)
			if result == "" then
				result = k.."="..tmp
			else
				result = result..","..k.."="..tmp
			end
		end
		return "{"..result.."}"
	elseif t == "nil" then
		return "nil"
	else
		error("cannot serialize a " .. type(o))
	end
end

function debugPrintProperty(instance)
	for key, value in pairs(instance)
	do
		print(key, value);
	end
end
----------------------------------------------------------------------------------------------------------
--	以下、サンプル
----------------------------------------------------------------------------------------------------------
--[[
ExtendsObject=extends(Object);

	function ExtendsObject:constructor(name)
		self.name = "Extend";
		self.selfName = name;
	end

	-- オーバーライド
	function ExtendsObject:toString()
		return self.name .. " 拡張クラス toString() オーバーライド";
	end

	function ExtendsObject:appendMethod()
		return self.name .. " 拡張クラス 追加メソ\ッド";
	end

ChildObject=extends(ExtendsObject);
	function ChildObject:constructor(name)
		self.selfName = name;
		return self;
	end
	function ChildObject:toString()
		return self.name .. " " .. self.selfName .. " " .. self.super.name;
	end

function main()

	local baseInstance = Object.new();
	local extendsInstanceA = ExtendsObject.new("hoge");
	local extendsInstanceB = ExtendsObject.new("fuga");
	local extendsInstanceC = ChildObject.new("hohoho");

	print(baseInstance:toString());
	print(extendsInstanceA:toString());
	print(extendsInstanceB:toString());
	print(extendsInstanceC:toString());
	print(extendsInstanceA:appendMethod());
	print(extendsInstanceB:appendMethod());

	while (true)
	do
		yield();
	end
end
]]--
