-- tolua: class class
-- Written by Waldemar Celes
-- TeCGraf/PUC-Rio
-- Jul 1998
-- $Id$

-- This code is free software; you can redistribute it and/or modify it.
-- The software provided hereunder is on an "as is" basis, and
-- the author has no obligation to provide maintenance, support, updates,
-- enhancements, or modifications.


-- Class class
-- Represents a class definition.
-- Stores the following fields:
--    name = class name
--    base = class base, if any (only single inheritance is supported)
--    {i}  = list of members
classClass = {
 classtype = 'class',
 name = '',
 base = '',
 type = '',
 btype = '',
 ctype = '',
}
classClass.__index = classClass
setmetatable(classClass,classContainer)


-- register class
function classClass:register (pre)
	if not self:check_public_access() then
		return
	end

 pre = pre or ''
 push(self)
	if _collect[self.type] then
		output(pre,'#ifdef __cplusplus\n')
  output(pre..'tolua_cclass(tolua_S,"'..self.lname..'","'..self.type..'","'..self.btype..'",'.._collect[self.type]..');')
		output(pre,'#else\n')
  output(pre..'tolua_cclass(tolua_S,"'..self.lname..'","'..self.type..'","'..self.btype..'",NULL);')
		output(pre,'#endif\n')
	else
  output(pre..'tolua_cclass(tolua_S,"'..self.lname..'","'..self.type..'","'..self.btype..'",NULL);')
	end
	if self.extra_bases then
		for k,base in ipairs(self.extra_bases) do
			-- not now
   --output(pre..' tolua_addbase(tolua_S, "'..self.type..'", "'..base..'");')
		end
	end
 output(pre..'tolua_beginmodule(tolua_S,"'..self.lname..'");')
 local i=1
 while self[i] do
  self[i]:register(pre..' ')
  i = i+1
 end
 output(pre..'tolua_endmodule(tolua_S);')
	pop()
end

-- return collection requirement
function classClass:requirecollection (t)
	if self.flags.protected_destructor then
		return false
	end
 push(self)
	local r = false
 local i=1
 while self[i] do
  r = self[i]:requirecollection(t) or r
  i = i+1
 end
	pop()
	-- only class that exports destructor can be appropriately collected
	-- classes that export constructors need to have a collector (overrided by -D flag on command line)
	if self._delete or ((not flags['D']) and self._new) then
		--t[self.type] = "tolua_collect_" .. gsub(self.type,"::","_")
		t[self.type] = "tolua_collect_" .. clean_template(self.type)
		r = true
	end
 return r
end

-- output tags
function classClass:decltype ()
 push(self)
	self.type = regtype(self.original_name or self.name)
	self.btype = typevar(self.base)
	self.ctype = 'const '..self.type
	if self.extra_bases then
		for i=1,table.getn(self.extra_bases) do
			self.extra_bases[i] = typevar(self.extra_bases[i])
		end
	end
 local i=1
 while self[i] do
  self[i]:decltype()
  i = i+1
 end
	pop()
end


-- Print method
function classClass:print (ident,close)
 print(ident.."Class{")
 print(ident.." name = '"..self.name.."',")
 print(ident.." base = '"..self.base.."';")
 print(ident.." lname = '"..self.lname.."',")
 print(ident.." type = '"..self.type.."',")
 print(ident.." btype = '"..self.btype.."',")
 print(ident.." ctype = '"..self.ctype.."',")
 local i=1
 while self[i] do
  self[i]:print(ident.." ",",")
  i = i+1
 end
 print(ident.."}"..close)
end

--- 
-- LuaDoc Patch
-- outputs an empty(without documentation) LuaDoc interface 
-- by klapeto (http://cegui.org.uk/forum/viewtopic.php?f=7&t=6784)
function classClass:output_luadoc()

	if not self:check_public_access() then
		return
	end
	output('---------------------------------------------------------------------------------\n')
	output('-- '..self.lname..'\n')
	output('-- @type '..cleanseType(self.type)..'\n')
	if (self.btype ~='') then
		output('-- @extends #'..cleanseType(self.btype)..'\n')
	end
	output('\n')

	if (self.parent~=nil) then
		output('---\n')
		output('-- @field [parent=#'..cleanseType(self.parent.name)..'] #'..cleanseType(self.type)..' '..self.lname..'\n')
	else
		output('---\n')
		output('-- @field [parent=#global] #'..cleanseType(self.type)..' '..self.lname..'\n')
	end
	output('\n')
	local i=1
	while self[i] do
		self[i]:output_luadoc()
		i = i+1
	end
	output('\n')
end

function classClass:set_protected_destructor(p)
	self.flags.protected_destructor = self.flags.protected_destructor or p
end

-- Internal constructor
function _Class (t)
 setmetatable(t,classClass)
 t:buildnames()
 append(t)
 return t
end

-- Constructor
-- Expects the name, the base (array) and the body of the class.
function Class (n,p,b)

	if table.getn(p) > 1 then
		b = string.sub(b, 1, -2)
		for i=2,table.getn(p),1 do
			b = b.."\n tolua_inherits "..p[i].." __"..p[i].."__;\n"
		end
		b = b.."\n}"
	end

	-- check for template
	b = string.gsub(b, "^{%s*TEMPLATE_BIND", "{\nTOLUA_TEMPLATE_BIND")
	local t,_,T,I = string.find(b, "^{%s*TOLUA_TEMPLATE_BIND%s*%(+%s*\"?([^\",]*)\"?%s*,%s*([^%)]*)%s*%)+")
	if t then

		-- remove quotes
		I = string.gsub(I, "\"", "")
		T = string.gsub(T, "\"", "")
		-- get type list
		local types = split_c_tokens(I, ",")
		-- remove TEMPLATE_BIND line
		local bs = string.gsub(b, "^{%s*TOLUA_TEMPLATE_BIND[^\n]*\n", "{\n")

		-- replace
		for i =1 , types.n do

			local Tl = split(T, " ")
			local Il = split_c_tokens(types[i], " ")
			local bI = bs
			local pI = {}
			for j = 1,Tl.n do
				Tl[j] = findtype(Tl[j]) or Tl[j]
				bI = string.gsub(bI, "([^_%w])"..Tl[j].."([^_%w])", "%1"..Il[j].."%2")
				if p then
					for i=1,table.getn(p) do
						pI[i] = string.gsub(p[i], "([^_%w]?)"..Tl[j].."([^_%w]?)", "%1"..Il[j].."%2")
					end
				end
			end
			--local append = "<"..string.gsub(types[i], "%s+", ",")..">"
			local append = "<"..concat(Il, 1, table.getn(Il), ",")..">"
			append = string.gsub(append, "%s*,%s*", ",")
			append = string.gsub(append, ">>", "> >")
			for i=1,table.getn(pI) do
				--pI[i] = string.gsub(pI[i], ">>", "> >")
				pI[i] = resolve_template_types(pI[i])
			end
			bI = string.gsub(bI, ">>", "> >")
			Class(n..append, pI, bI)
		end
		return
	end

	local mbase

	if p then
		mbase = table.remove(p, 1)
		if not p[1] then p = nil end
	end

	mbase = mbase and resolve_template_types(mbase)

	local c
	local oname = string.gsub(n, "@.*$", "")
	oname = getnamespace(classContainer.curr)..oname

	if _global_classes[oname] then
		c = _global_classes[oname]
		if mbase and ((not c.base) or c.base == "") then
			c.base = mbase
		end
	else
		c = _Class(_Container{name=n, base=mbase, extra_bases=p})

		local ft = getnamespace(c.parent)..c.original_name
		append_global_type(ft, c)
	end

	push(c)
	c:parse(strsub(b,2,strlen(b)-1)) -- eliminate braces
	pop()
end

