-- tolua: variable 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.


-- Variable class
-- Represents a extern variable or a public member of a class.
-- Stores all fields present in a declaration.
classVariable = {
 _get = {},   -- mapped get functions
 _set = {},   -- mapped set functions
}
classVariable.__index = classVariable
setmetatable(classVariable,classDeclaration)

-- Print method
function classVariable:print (ident,close)
 print(ident.."Variable{")
 print(ident.." mod  = '"..self.mod.."',")
 print(ident.." type = '"..self.type.."',")
 print(ident.." ptr  = '"..self.ptr.."',")
 print(ident.." name = '"..self.name.."',")
 if self.dim then print(ident.." dim = '"..self.dim.."',") end
 print(ident.." def  = '"..self.def.."',")
 print(ident.." ret  = '"..self.ret.."',")
 print(ident.."}"..close)
end

-- Generates C function name
function classVariable:cfuncname (prefix)
 local parent = ""
 local unsigned = ""
 local ptr = ""

 local p = self:inmodule() or self:innamespace() or self:inclass()

 if p then
 	if self.parent.classtype == 'class' then
		parent = "_" .. self.parent.type
	else
	  parent = "_" .. p
	end
 end

 if strfind(self.mod,"(unsigned)") then
  unsigned = "_unsigned"
 end

 if self.ptr == "*" then ptr = "_ptr"
 elseif self.ptr == "&" then ptr = "_ref"
 end

 local name =  prefix .. parent .. unsigned .. "_" .. gsub(self.lname or self.name,".*::","") .. ptr

	name = clean_template(name)
 return name

end

-- check if it is a variable
function classVariable:isvariable ()
 return true
end

-- get variable value
function classVariable:getvalue (class,static, prop_get)

	local name
	if prop_get then

		name = prop_get.."()"
	else
		name = self.name
	end

	if class and static then
	 return self.parent.type..'::'..name
	elseif class then
	 return 'self->'..name
	else
	 return name
	end
end

-- get variable pointer value
function classVariable:getpointervalue (class,static)
 if class and static then
  return class..'::p'
 elseif class then
  return 'self->p'
 else
  return 'p'
 end
end

-- Write binding functions
function classVariable:supcode ()

 local class = self:inclass()

	local prop_get,prop_set
	if string.find(self.mod, 'tolua_property') then

		_,_,type = string.find(self.mod, "tolua_property__([^%s]*)")
		type = type or "default"
		prop_get,prop_set = get_property_methods(type, self.name)
		self.mod = string.gsub(self.mod, "tolua_property[^%s]*", "")
	end

 -- get function ------------------------------------------------
 if class then
  output("/* get function:",self.name," of class ",class," */")
 else
  output("/* get function:",self.name," */")
 end
 self.cgetname = self:cfuncname("tolua_get")
 output("#ifndef TOLUA_DISABLE_"..self.cgetname)
 output("\nstatic int",self.cgetname,"(lua_State* tolua_S)")
 output("{")

 -- declare self, if the case
 local _,_,static = strfind(self.mod,'^%s*(static)')
 if class and static==nil then
  output(' ',self.parent.type,'*','self = ')
  output('(',self.parent.type,'*) ')
  output('tolua_tousertype(tolua_S,1,0);')
 elseif static then
  _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
 end


 -- check self value
 if class and static==nil then
	 output('#ifndef TOLUA_RELEASE\n')
  output('  if (!self) tolua_error(tolua_S,"invalid \'self\' in accessing variable \''..self.name..'\'",NULL);');
		output('#endif\n')
 end

 -- return value
 if string.find(self.mod, 'tolua_inherits') then
 	output('#ifdef __cplusplus\n')
	output('  tolua_pushusertype(tolua_S,(void*)static_cast<'..self.type..'*>(self), "',self.type,'");')
	output('#else\n')
	output('  tolua_pushusertype(tolua_S,(void*)(('..self.type..'*)self), "',self.type,'");')
	output('#endif\n')
 else
	local t,ct = isbasic(self.type)
	if t then
		output('  tolua_push'..t..'(tolua_S,(',ct,')'..self:getvalue(class,static,prop_get)..');')
	else
		t = self.type
		if self.ptr == '&' or self.ptr == '' then
			output('  tolua_pushusertype(tolua_S,(void*)&'..self:getvalue(class,static,prop_get)..',"',t,'");')
		else
			output('  tolua_pushusertype(tolua_S,(void*)'..self:getvalue(class,static,prop_get)..',"',t,'");')
		end
	end
 end
 output(' return 1;')
 output('}')
 output('#endif //#ifndef TOLUA_DISABLE\n')
 output('\n')

 -- set function ------------------------------------------------
 if not (strfind(self.type,'const%s+') or string.find(self.mod, 'tolua_readonly') or string.find(self.mod, 'tolua_inherits'))  then
  if class then
   output("/* set function:",self.name," of class ",class," */")
  else
   output("/* set function:",self.name," */")
  end
  self.csetname = self:cfuncname("tolua_set")
  output("#ifndef TOLUA_DISABLE_"..self.csetname)
  output("\nstatic int",self.csetname,"(lua_State* tolua_S)")
  output("{")

  -- declare self, if the case
  if class and static==nil then
   output(' ',self.parent.type,'*','self = ')
   output('(',self.parent.type,'*) ')
   output('tolua_tousertype(tolua_S,1,0);')
   -- check self value
		end
  -- check types
		output('#ifndef TOLUA_RELEASE\n')
		output('  tolua_Error tolua_err;')
  if class and static==nil then
   output('  if (!self) tolua_error(tolua_S,"invalid \'self\' in accessing variable \''..self.name..'\'",NULL);');
  elseif static then
   _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
  end

  -- check variable type
  output('  if (!'..self:outchecktype(2)..')')
  output('   tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);')
		output('#endif\n')

  -- assign value
		local def = 0
		if self.def ~= '' then def = self.def end
		if self.type == 'char*' and self.dim ~= '' then -- is string
		 output(' strncpy(')
			if class and static then
				output(self.parent.type..'::'..self.name)
			elseif class then
				output('self->'..self.name)
			else
				output(self.name)
			end
			output(',tolua_tostring(tolua_S,2,',def,'),',self.dim,'-1);')
		else
			local ptr = ''
			if self.ptr~='' then ptr = '*' end
			output(' ')
			local name = prop_set or self.name
			if class and static then
				output(self.parent.type..'::'..name)
			elseif class then
				output('self->'..name)
			else
				output(name)
			end
			local t = isbasic(self.type)
			if prop_set then
				output('(')
			else
				output(' = ')
			end
			if not t and ptr=='' then output('*') end
			output('((',self.mod,self.type)
			if not t then
				output('*')
			end
			output(') ')
			if t then
				if isenum(self.type) then
					output('(int) ')
				end
				output('tolua_to'..t,'(tolua_S,2,',def,'))')
			else
				output('tolua_tousertype(tolua_S,2,',def,'))')
			end
			if prop_set then
				output(")")
			end
			output(";")
		end
  output(' return 0;')
  output('}')
  output('#endif //#ifndef TOLUA_DISABLE\n')
  output('\n')
 end

end

function classVariable:register (pre)

	if not self:check_public_access() then
		return
	end
 pre = pre or ''
 local parent = self:inmodule() or self:innamespace() or self:inclass()
 if not parent then
  if classVariable._warning==nil then
   warning("Mapping variable to global may degrade performance")
   classVariable._warning = 1
  end
 end
 if self.csetname then
  output(pre..'tolua_variable(tolua_S,"'..self.lname..'",'..self.cgetname..','..self.csetname..');')
 else
  output(pre..'tolua_variable(tolua_S,"'..self.lname..'",'..self.cgetname..',NULL);')
 end
end

--- 
-- LuaDoc Patch
-- outputs an empty(without documentation) LuaDoc interface 
-- by klapeto (http://cegui.org.uk/forum/viewtopic.php?f=7&t=6784)
function classVariable:output_luadoc()
	if not self:check_public_access() then
		return
	end
	local parent = self:inmodule() or self:innamespace() or self:inclass()
	if not parent then
		if classVariable._warning==nil then
			warning("Mapping variable to global may degrade performance")
			classVariable._warning = 1
		end
	end
	local pType = cleanseType(self.type)
	if (self.parent~=nil) then
		output('---\n')
		output('-- @field [parent=#'..cleanseType(self.parent.name)..'] #'..pType..' '..self.lname..'\n')
	else
		output('---\n')
		output('-- @field [parent=#global] #'..pType..' '..self.lname..'\n')
	end
	output('\n')
end


-- Internal constructor
function _Variable (t)
 setmetatable(t,classVariable)
 append(t)
 return t
end

-- Constructor
-- Expects a string representing the variable declaration.
function Variable (s)
 return _Variable (Declaration(s,'var'))
end


