--
-- xcode_common.lua
-- Functions to generate the different sections of an Xcode project.
-- Copyright (c) 2009-2011 Jason Perkins and the Premake project
--

	local xcode = premake.extensions.xcode
	local tree  = premake.tree
    local solution = premake.solution
	local project = premake.project
    local config = premake.config
	local fileconfig = premake.fileconfig


	-- Copy Info.plist and Default-568h@2x.png from skeleton to project folder
	function xcode.copymacfiles(prj)
		local kind

		if prj.name == "all" then
			return
		end

		for cfg in project.eachconfig(prj) do
			if kind and kind ~= cfg.kind then
				error("Project '" .. prj.name .. "' uses more than one target kind; not supported by Xcode", 0)
			end
			kind = cfg.kind
		end

		if kind == "ConsoleApp" or kind == "WindowedApp" then
			-- fixme: should not hard-code the list of files
			-- fixme: should not depend on MINKO_HOME
			local plist = "Info.plist"
			local plistpath = MINKO_HOME .. "/skeleton/" .. plist
			local defaultpng =  "Default-568h@2x.png"
			local defaultpngpath = MINKO_HOME .. "/skeleton/" .. defaultpng

			if not os.isfile(prj.location .. "/" .. plist) and os.isfile(plistpath) then
				os.copyfile(plistpath, prj.location)
			end
			if not os.isfile(prj.location .. "/" .. defaultpng) and os.isfile(defaultpngpath) then
				os.copyfile(defaultpngpath, prj.location)
			end
		end
	end

--
-- Return the Xcode build category for a given file, based on the file extension.
--
-- @param node
--    The node to identify.
-- @returns
--    An Xcode build category, one of "Sources", "Resources", "Frameworks", or nil.
--

	function xcode.getbuildcategory(node)
		local categories = {
			[".a"] = "Frameworks",
			[".c"] = "Sources",
			[".cc"] = "Sources",
			[".cpp"] = "Sources",
			[".cxx"] = "Sources",
			[".dylib"] = "Frameworks",
			[".framework"] = "Frameworks",
			[".m"] = "Sources",
			[".mm"] = "Sources",
			[".strings"] = "Resources",
			[".nib"] = "Resources",
			[".xib"] = "Resources",
			[".icns"] = "Resources",
			[".s"] = "Sources",
			[".S"] = "Sources",
		}
		if node.isResource then
			return "Resources"
		end
		return categories[path.getextension(node.name)]
	end

	function xcode.isItemResource(project, node)

		local res;

		if project and project.xcodebuildresources then
			if type(project.xcodebuildresources) == "table" then
				res = project.xcodebuildresources
			end
		end

		local function checkItemInList(item, list)
			if item then
				if list then
					if type(list) == "table" then
						for _,v in pairs(list) do
							if string.find(item, v) then
								return true
							end
						end
					end
				end
			end
			return false
		end

		--print (node.path, node.buildid, node.cfg, res)
		if (checkItemInList(node.path, res)) then
			return true
		end

		return false
	end
--
-- Return the displayed name for a build configuration, taking into account the
-- configuration and platform, i.e. "Debug 32-bit Universal".
--
-- @param cfg
--    The configuration being identified.
-- @returns
--    A build configuration name.
--

	function xcode.getconfigname(cfg)
		local name = cfg.buildcfg
		-- print(cfg.platform, cfg.project.name)
		if #cfg.project.solution.platforms > 1 then
			name = name .. " " .. premake.action.current().valid_platforms[cfg.platform]
		end
		return name
	end


--
-- Return the Xcode type for a given file, based on the file extension.
--
-- @param fname
--    The file name to identify.
-- @returns
--    An Xcode file type, string.
--

	function xcode.getfiletype(node)
		local types = {
			[".c"]         = "sourcecode.c.c",
			[".cc"]        = "sourcecode.cpp.cpp",
			[".cpp"]       = "sourcecode.cpp.cpp",
			[".css"]       = "text.css",
			[".cxx"]       = "sourcecode.cpp.cpp",
			[".framework"] = "wrapper.framework",
			[".gif"]       = "image.gif",
			[".h"]         = "sourcecode.c.h",
			[".html"]      = "text.html",
			[".lua"]       = "sourcecode.lua",
			[".m"]         = "sourcecode.c.objc",
			[".mm"]        = "sourcecode.cpp.objc",
			[".nib"]       = "wrapper.nib",
			[".pch"]       = "sourcecode.c.h",
			[".plist"]     = "text.plist.xml",
			[".strings"]   = "text.plist.strings",
			[".xib"]       = "file.xib",
			[".icns"]      = "image.icns",
			[".s"]         = "sourcecode.asm",
			[".bmp"]       = "image.bmp",
			[".wav"]       = "audio.wav",
			[".xcassets"]  = "folder.assetcatalog",

		}
		return types[path.getextension(node.path)] or "text"
	end

--
-- Print user configuration references contained in xcodeconfigreferences
-- @param offset
--    offset used by function _p
-- @param cfg
--    configuration
--

	function xcodePrintUserConfigReferences(offset, cfg, tr, kind)
		    local referenceName
            if kind == "project" then
		        referenceName =  cfg.xcodeconfigreferenceproject
		    else
                if kind == "target" then
		            referenceName =  cfg.xcodeconfigreferencetarget
		        end
		    end
			tree.traverse(tr, {
			    onleaf = function(node)
			        filename = node.name
			        if node.id and path.getextension(filename) == ".xcconfig" then
					    if filename == referenceName then
					        _p(offset,'baseConfigurationReference = %s /* %s */;', node.id, filename)
                            return
					    end
				    end
			    end
			}, false)
	end

--
-- Return the Xcode product type, based target kind.
--
-- @param node
--    The product node to identify.
-- @returns
--    An Xcode product type, string.
--

	function xcode.getproducttype(node)
		local types = {
			ConsoleApp  = "com.apple.product-type.tool",
			WindowedApp = "com.apple.product-type.application",
			StaticLib   = "com.apple.product-type.library.static",
			SharedLib   = "com.apple.product-type.library.dynamic",
		}
		if not types[node.cfg.kind] then
			error("Kind " .. node.cfg.kind .. " is unsupported by Xcode.")
		end
		return types[node.cfg.kind]
	end


--
-- Return the Xcode target type, based on the target file extension.
--
-- @param node
--    The product node to identify.
-- @returns
--    An Xcode target type, string.
--

	function xcode.gettargettype(node)
		local types = {
			ConsoleApp  = "\"compiled.mach-o.executable\"",
			WindowedApp = "wrapper.application",
			StaticLib   = "archive.ar",
			SharedLib   = "\"compiled.mach-o.dylib\"",
		}
		if not types[node.cfg.kind] then
			error("Kind " .. node.cfg.kind .. " is unsupported by Xcode.")
		end
		return types[node.cfg.kind]
	end


--
-- Return a unique file name for a project. Since Xcode uses .xcodeproj's to
-- represent both solutions and projects there is a likely change of a name
-- collision. Tack on a number to differentiate them.
--
-- @param prj
--    The project being queried.
-- @returns
--    A uniqued file name
--

	function xcode.getxcodeprojname(prj)
		-- if there is a solution with matching name, then use "projectname1.xcodeproj"
		-- just get something working for now
		local fname = project.getfilename(prj, ".xcodeproj")
		return fname
	end


--
-- Returns true if the file name represents a framework.
--
-- @param fname
--    The name of the file to test.
--

	function xcode.isframework(fname)
		return (path.getextension(fname) == ".framework")
	end

--
-- Check if build settings is overrided by user with xcodebuildsettings {}
-- @param buildsettingName
--    The list of values to be printed.
-- @param cfg
--    configuration
--
	function xcode.BuildSettingsExists(buildsettingName, cfg)
		if #cfg.xcodebuildsettings > 0 then
			for _, item in ipairs(cfg.xcodebuildsettings) do
				local _, k = string.find(item, "=")
				local itemName =  string.sub(item, 1, k - 1)
				itemName = string.gsub(itemName, "%s", "")
			end
		end
		return false
	end

--
-- Print user build settings contained in xcodebuildsettings
-- @param offset
--    offset used by function _p
-- @param cfg
--    configuration
--

	function xcode.PrintUserSettings(offset, cfg)
		for _, item in ipairs(cfg.xcodebuildsettings) do
			_p(offset, item .. ';')
		end
	end

--
-- Print xcode build setting if not overrided by user
-- @param offset
--    offset used by function _p
-- @param cfg
--    configuration
--

	function xcode.PrintBuildSetting(offset, buildsetting, cfg)
		-- Check that the settings was not overriden by xcode buildsettings
		if #cfg.xcodebuildsettings > 0 then

			local _, j = string.find(buildsetting, "=")
			local buildsettingName =  string.sub(buildsetting, 1, j - 1)
			buildsettingName = string.gsub(buildsettingName, "%s", "")

			if xcode.BuildSettingsExists(buildsettingName, cfg) then
				return
			end
		end

		_p(offset, buildsetting)
	end

--
-- Retrieves a unique 12 byte ID for an object. This function accepts and ignores two
-- parameters 'node' and 'usage', which are used by an alternative implementation of
-- this function for testing.
--
-- @returns
--    A 24-character string representing the 12 byte ID.
--

	function xcode.newid()
		return string.format("%04X%04X%04X%04X%04X%04X",
			math.random(0, 32767),
			math.random(0, 32767),
			math.random(0, 32767),
			math.random(0, 32767),
			math.random(0, 32767),
			math.random(0, 32767))
	end


--
-- Create a product tree node and all projects in a solution; assigning IDs
-- that are needed for inter-project dependencies.
--
-- @param sln
--    The solution to prepare.
--

	function xcode.preparesolution(sln)
		-- create and cache a list of supported platforms
		sln.xcode = { }

		for prj in premake.solution.eachproject(sln) do
			-- need a configuration to get the target information
			local cfg = project.getconfig(prj, prj.configurations[1], prj.platforms[1])

			-- build the product tree node
			local bundlepath = cfg.buildtarget.bundlename ~= "" and cfg.buildtarget.bundlename or cfg.buildtarget.name;
			if (prj.external) then
				bundlepath = cfg.project.name
			end

			local node = premake.tree.new(path.getname(bundlepath))

			node.cfg = cfg
			node.id = xcode.newid(node, "product")
			node.targetid = xcode.newid(node, "target")

			-- attach it to the project
			prj.xcode = {}
			prj.xcode.projectnode = node
		end
	end


--
-- Print out a list value in the Xcode format.
--
-- @param list
--    The list of values to be printed.
-- @param tag
--    The Xcode specific list tag.
--

	function xcode.printlist(list, tag, cfg)
		if xcode.BuildSettingsExists(tag, cfg) then
			return -- Don't write settings
		end

		if #list > 0 then
			_p(4,'%s = (', tag)
			for _, item in ipairs(list) do
				local quoted = string.sub(item, 1, 1) == '"'
				_p(5, quoted and '%s,' or '"%s",', item)
			end
			_p(4,');')
		end
	end


---------------------------------------------------------------------------
-- Section generator functions, in the same order in which they appear
-- in the .pbxproj file
---------------------------------------------------------------------------

	function xcode.Header()
		_p('// !$*UTF8*$!')
		_p('{')
		_p(1,'archiveVersion = 1;')
		_p(1,'classes = {')
		_p(1,'};')
		_p(1,'objectVersion = 46;')
		_p(1,'objects = {')
		_p('')
	end


	function xcode.PBXBuildFile(tr)
		_p('/* Begin PBXBuildFile section */')
		tree.traverse(tr, {
			onnode = function(node)
				if node.buildid then
					_p(2,'%s /* %s in %s */ = {isa = PBXBuildFile; fileRef = %s /* %s */; };',
						node.buildid, node.name, xcode.getbuildcategory(node), node.id, node.name)
				end
			end
		})
		_p('/* End PBXBuildFile section */')
		_p('')
	end


	function xcode.PBXContainerItemProxy(tr)
		if #tr.projects.children > 0 then
			_p('/* Begin PBXContainerItemProxy section */')
			for _, node in ipairs(tr.projects.children) do
				_p(2,'%s /* PBXContainerItemProxy */ = {', node.productproxyid)
				_p(3,'isa = PBXContainerItemProxy;')
				_p(3,'containerPortal = %s /* %s */;', node.id, path.getname(node.path))
				_p(3,'proxyType = 2;')
				_p(3,'remoteGlobalIDString = %s;', node.project.xcode.projectnode.id)
				_p(3,'remoteInfo = "%s";', node.project.xcode.projectnode.name)
				_p(2,'};')
				_p(2,'%s /* PBXContainerItemProxy */ = {', node.targetproxyid)
				_p(3,'isa = PBXContainerItemProxy;')
				_p(3,'containerPortal = %s /* %s */;', node.id, path.getname(node.path))
				_p(3,'proxyType = 1;')
				_p(3,'remoteGlobalIDString = %s;', node.project.xcode.projectnode.targetid)
				_p(3,'remoteInfo = "%s";', node.project.xcode.projectnode.name)
				_p(2,'};')
			end
			_p('/* End PBXContainerItemProxy section */')
			_p('')
		end
	end


	function xcode.PBXFileReference(tr)
		_p('/* Begin PBXFileReference section */')

		local cfg = project.getfirstconfig(tr.project)

		tree.traverse(tr, {
			onleaf = function(node)
				-- I'm only listing files here, so ignore anything without a path
				if not node.path then
					return
				end

				-- is this the product node, describing the output target?
				if node.kind == "product" then
					_p(2,'%s /* %s */ = {isa = PBXFileReference; explicitFileType = %s; includeInIndex = 0; name = "%s"; path = "%s"; sourceTree = BUILT_PRODUCTS_DIR; };',
						node.id, node.name, xcode.gettargettype(node), node.name, path.getname(node.cfg.buildtarget.bundlename ~= "" and node.cfg.buildtarget.bundlename or node.cfg.buildtarget.relpath))

				-- is this a project dependency?
				elseif node.parent.parent == tr.projects then
					-- ms Is there something wrong with path is relative ?
					-- if we have a and b without slashes get relative should assume the same parent folder and return ../
					-- this works if we put it like below
					local relpath = path.getrelative(path.getabsolute(tr.project.location), path.getabsolute(node.parent.project.location))
					_p(2,'%s /* %s */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "%s"; path = "%s"; sourceTree = SOURCE_ROOT; };',
						node.parent.id, node.parent.name, node.parent.name, path.join(relpath, node.parent.name))

				-- something else
				else
					local pth, src
					if xcode.isframework(node.path) then
						--respect user supplied paths
						-- look for special variable-starting paths for different sources
						local nodePath = node.path
						local _, matchEnd, variable = string.find(nodePath, "^%$%((.+)%)/")
						if variable then
							-- by skipping the last '/' we support the same absolute/relative
							-- paths as before
							nodePath = string.sub(nodePath, matchEnd + 1)
						end
						if string.find(nodePath,'/') then
							if string.find(nodePath,'^%.') then
								--error('relative paths are not currently supported for frameworks')
								pth = path.getrelative(tr.project.location, node.path)
								--print(tr.project.location, node.path , pth)
								src = "SOURCE_ROOT"
							else
								pth = nodePath
								src = "<absolute>"
							end
						end
						-- if it starts with a variable, use that as the src instead
						if variable then
							src = variable
							-- if we are using a different source tree, it has to be relative
							-- to that source tree, so get rid of any leading '/'
							if string.find(pth, '^/') then
								pth = string.sub(pth, 2)
							end
						else
							for i = 1, #cfg.libdirs do
								local l = cfg.libdirs[i] .. '/' .. node.path
								if os.isdir(l) then
									pth = l
									break
								end
							end
							if pth then
								src = "<group>"
							else
								src = "SDKROOT"
								pth = "System/Library/Frameworks/" .. node.path
							end
						end
					else
						-- something else; probably a source code file
						src = "<group>"

						-- if the parent node is virtual, it won't have a local path
						-- of its own; need to use full relative path from project
						--if node.parent.kind == "vgroup" then
						--	pth = node.parent.name
						--else
						local filecfg = fileconfig.getconfig(node, cfg)
						--pth = tree.getlocalpath(node)
						--if node.parent.path then
						--	pth = node.name
						--else

						if filecfg then
							pth = path.getrelative(tr.project.location, filecfg.abspath)
						else
							pth = node.path
						end
						--end
					end

					_p(2,'%s /* %s */ = {isa = PBXFileReference; lastKnownFileType = %s; name = "%s"; path = "%s"; sourceTree = "%s"; };',
						node.id, node.name, xcode.getfiletype(node), node.name, pth, src)
				end
			end
		})

		_p('/* End PBXFileReference section */')
		_p('')
	end


	function xcode.PBXFrameworksBuildPhase(tr)
		_p('/* Begin PBXFrameworksBuildPhase section */')
		_p(2,'%s /* Frameworks */ = {', tr.products.children[1].fxstageid)
		_p(3,'isa = PBXFrameworksBuildPhase;')
		_p(3,'buildActionMask = 2147483647;')
		_p(3,'files = (')

		-- write out library dependencies
		tree.traverse(tr.frameworks, {
			onleaf = function(node)
				if node.buildid then
					_p(4,'%s /* %s in Frameworks */,', node.buildid, node.name)
				end
			end
		})

		-- write out project dependencies
		tree.traverse(tr.projects, {
			onleaf = function(node)
				if node.buildid then
					_p(4,'%s /* %s in Frameworks */,', node.buildid, node.name)
				end
			end
		})

		_p(3,');')
		_p(3,'runOnlyForDeploymentPostprocessing = 0;')
		_p(2,'};')
		_p('/* End PBXFrameworksBuildPhase section */')
		_p('')
	end


	function xcode.PBXGroup(tr)
		_p('/* Begin PBXGroup section */')

		tree.traverse(tr, {
			onnode = function(node)
				-- Skip over anything that isn't a proper group
				if (node.path and #node.children == 0) or node.kind == "vgroup" then
					return
				end

				-- project references get special treatment
				if node.parent == tr.projects then
					_p(2,'%s /* Products */ = {', node.productgroupid)
				else
					_p(2,'%s /* %s */ = {', node.id, node.name)
				end

				_p(3,'isa = PBXGroup;')
				_p(3,'children = (')
				for _, childnode in ipairs(node.children) do
					_p(4,'%s /* %s */,', childnode.id, childnode.name)
				end
				_p(3,');')

				if node.parent == tr.projects then
					_p(3,'name = Products;')
				else
					_p(3,'name = "%s";', node.name)

					local vpath = project.getvpath(tr.project, node.name)

					if node.path and node.name ~= vpath then
						local p = node.path
						if node.parent.path then
							p = path.getrelative(node.parent.path, node.path)
						end
						_p(3,'path = "%s";', p)
					end
				end

				_p(3,'sourceTree = "<group>";')
				_p(2,'};')
			end
		}, true)

		_p('/* End PBXGroup section */')
		_p('')
	end


	function xcode.PBXNativeTarget(tr)
		_p('/* Begin PBXNativeTarget section */')
		for _, node in ipairs(tr.products.children) do
			local name = tr.project.name

			-- This function checks whether there are build commands of a specific
			-- type to be executed; they will be generated correctly, but the project
			-- commands will not contain any per-configuration commands, so the logic
			-- has to be extended a bit to account for that.
			local function hasBuildCommands(which)
				-- standard check...this is what existed before
				if #tr.project[which] > 0 then
					return true
				end
				-- what if there are no project-level commands? check configs...
				for _, cfg in ipairs(tr.configs) do
					if #cfg[which] > 0 then
						return true
					end
				end
			end

			_p(2,'%s /* %s */ = {', node.targetid, name)
			_p(3,'isa = PBXNativeTarget;')
			_p(3,'buildConfigurationList = %s /* Build configuration list for PBXNativeTarget "%s" */;', node.cfgsection, name)
			_p(3,'buildPhases = (')
			if hasBuildCommands('prebuildcommands') then
				_p(4,'9607AE1010C857E500CD1376 /* Prebuild */,')
			end
			_p(4,'%s /* Resources */,', node.resstageid)
			_p(4,'%s /* Sources */,', node.sourcesid)
			if hasBuildCommands('prelinkcommands') then
				_p(4,'9607AE3510C85E7E00CD1376 /* Prelink */,')
			end
			_p(4,'%s /* Frameworks */,', node.fxstageid)
			if hasBuildCommands('postbuildcommands') then
				_p(4,'9607AE3710C85E8F00CD1376 /* Postbuild */,')
			end
			_p(3,');')
			_p(3,'buildRules = (')
			_p(3,');')

			_p(3,'dependencies = (')
			for _, node in ipairs(tr.projects.children) do
				_p(4,'%s /* PBXTargetDependency */,', node.targetdependid)
			end
			_p(3,');')

			_p(3,'name = "%s";', name)

			local p
			if node.cfg.kind == "ConsoleApp" then
				p = "$(HOME)/bin"
			elseif node.cfg.kind == "WindowedApp" then
				p = "$(HOME)/Applications"
			end
			if p then
				_p(3,'productInstallPath = "%s";', p)
			end

			_p(3,'productName = "%s";', name)
			_p(3,'productReference = %s /* %s */;', node.id, node.name)
			_p(3,'productType = "%s";', xcode.getproducttype(node))
			_p(2,'};')
		end
		_p('/* End PBXNativeTarget section */')
		_p('')
	end


	function xcode.PBXProject(tr)
		_p('/* Begin PBXProject section */')
		_p(2,'08FB7793FE84155DC02AAC07 /* Project object */ = {')
		_p(3,'isa = PBXProject;')
		_p(3,'buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "%s" */;', tr.name)
		_p(3,'compatibilityVersion = "Xcode 3.2";')
		_p(3,'hasScannedForEncodings = 1;')
		_p(3,'mainGroup = %s /* %s */;', tr.id, tr.name)
		_p(3,'projectDirPath = "";')

		if #tr.projects.children > 0 then
			_p(3,'projectReferences = (')
			for _, node in ipairs(tr.projects.children) do
				_p(4,'{')
				_p(5,'ProductGroup = %s /* Products */;', node.productgroupid)
				_p(5,'ProjectRef = %s /* %s */;', node.id, path.getname(node.path))
				_p(4,'},')
			end
			_p(3,');')
		end

		_p(3,'projectRoot = "";')
		_p(3,'targets = (')
		for _, node in ipairs(tr.products.children) do
			_p(4,'%s /* %s */,', node.targetid, node.name)
		end
		_p(3,');')
		_p(2,'};')
		_p('/* End PBXProject section */')
		_p('')
	end


	function xcode.PBXReferenceProxy(tr)
		if #tr.projects.children > 0 then
			_p('/* Begin PBXReferenceProxy section */')
			tree.traverse(tr.projects, {
				onleaf = function(node)
					_p(2,'%s /* %s */ = {', node.id, node.name)
					_p(3,'isa = PBXReferenceProxy;')
					_p(3,'fileType = %s;', xcode.gettargettype(node))
					_p(3,'path = "%s";', node.name)
					_p(3,'remoteRef = %s /* PBXContainerItemProxy */;', node.parent.productproxyid)
					_p(3,'sourceTree = BUILT_PRODUCTS_DIR;')
					_p(2,'};')
				end
			})
			_p('/* End PBXReferenceProxy section */')
			_p('')
		end
	end


	function xcode.PBXResourcesBuildPhase(tr)
		_p('/* Begin PBXResourcesBuildPhase section */')
		for _, target in ipairs(tr.products.children) do
			_p(2,'%s /* Resources */ = {', target.resstageid)
			_p(3,'isa = PBXResourcesBuildPhase;')
			_p(3,'buildActionMask = 2147483647;')
			_p(3,'files = (')
			tree.traverse(tr, {
				onnode = function(node)
					if xcode.getbuildcategory(node) == "Resources" then
						_p(4,'%s /* %s in Resources */,', node.buildid, node.name)
					end
				end
			})
			_p(3,');')
			_p(3,'runOnlyForDeploymentPostprocessing = 0;')
			_p(2,'};')
		end
		_p('/* End PBXResourcesBuildPhase section */')
		_p('')
	end

	function xcode.PBXShellScriptBuildPhase(tr)
		local wrapperWritten = false

		local function doblock(id, name, which)
			-- start with the project-level commands (most common)
			local prjcmds = tr.project[which]
			local commands = table.join(prjcmds, {})

			-- see if there are any config-specific commands to add
			for key, cfg in ipairs(tr.configs) do
				local cfgcmds = cfg[which]
				if #cfgcmds > #prjcmds then
					table.insert(commands, 'if [ "${CONFIGURATION}" = "' .. xcode.getconfigname(cfg) .. '" ]; then')
					for _, cfgvalue in ipairs(cfgcmds) do
						local exist = false;
						for _, prjvalue in ipairs(prjcmds) do
							if prjvalue == cfgvalue then
								exist = true
								break
							end
						end

						if exist == false then
							table.insert(commands, cfgvalue)
						end
					end
					table.insert(commands, 'fi')
				end
			end

			if #commands > 0 then
				if not wrapperWritten then
					_p('/* Begin PBXShellScriptBuildPhase section */')
					wrapperWritten = true
				end
				_p(2,'%s /* %s */ = {', id, name)
				_p(3,'isa = PBXShellScriptBuildPhase;')
				_p(3,'buildActionMask = 2147483647;')
				_p(3,'files = (')
				_p(3,');')
				_p(3,'inputPaths = (');
				_p(3,');');
				_p(3,'name = %s;', name);
				_p(3,'outputPaths = (');
				_p(3,');');
				_p(3,'runOnlyForDeploymentPostprocessing = 0;');
				_p(3,'shellPath = /bin/sh;');
				_p(3,'shellScript = "%s";', table.concat(commands, "\\n"):gsub('"', '\\"'))
				_p(2,'};')
			end
		end

		doblock("9607AE1010C857E500CD1376", "Prebuild", "prebuildcommands")
		doblock("9607AE3510C85E7E00CD1376", "Prelink", "prelinkcommands")
		doblock("9607AE3710C85E8F00CD1376", "Postbuild", "postbuildcommands")

		if wrapperWritten then
			_p('/* End PBXShellScriptBuildPhase section */')
		end
	end


	function xcode.PBXSourcesBuildPhase(tr)
		_p('/* Begin PBXSourcesBuildPhase section */')
		for _, target in ipairs(tr.products.children) do
			_p(2,'%s /* Sources */ = {', target.sourcesid)
			_p(3,'isa = PBXSourcesBuildPhase;')
			_p(3,'buildActionMask = 2147483647;')
			_p(3,'files = (')
			tree.traverse(tr, {
				onleaf = function(node)
					if xcode.getbuildcategory(node) == "Sources" then
						_p(4,'%s /* %s in Sources */,', node.buildid, node.name)
					end
				end
			})
			_p(3,');')
			_p(3,'runOnlyForDeploymentPostprocessing = 0;')
			_p(2,'};')
		end
		_p('/* End PBXSourcesBuildPhase section */')
		_p('')
	end


	function xcode.PBXVariantGroup(tr)
		_p('/* Begin PBXVariantGroup section */')
		tree.traverse(tr, {
			onbranch = function(node)
				if node.kind == "vgroup" then
					_p(2,'%s /* %s */ = {', node.id, node.name)
					_p(3,'isa = PBXVariantGroup;')
					_p(3,'children = (')
					for _, lang in ipairs(node.children) do
						_p(4,'%s /* %s */,', lang.id, lang.name)
					end
					_p(3,');')
					_p(3,'name = %s;', node.name)
					_p(3,'sourceTree = "<group>";')
					_p(2,'};')
				end
			end
		})
		_p('/* End PBXVariantGroup section */')
		_p('')
	end


	function xcode.PBXTargetDependency(tr)
		if #tr.projects.children > 0 then
			_p('/* Begin PBXTargetDependency section */')
			tree.traverse(tr.projects, {
				onleaf = function(node)
					_p(2,'%s /* PBXTargetDependency */ = {', node.parent.targetdependid)
					_p(3,'isa = PBXTargetDependency;')
					_p(3,'name = "%s";', node.name)
					_p(3,'targetProxy = %s /* PBXContainerItemProxy */;', node.parent.targetproxyid)
					_p(2,'};')
				end
			})
			_p('/* End PBXTargetDependency section */')
			_p('')
		end
	end

	-- function printSetting(obs, level, name, value, quoted)
	-- 	-- if we have this name in the xcodebuildsettings the override the value
	-- 	local overridenValue = obs[name] and obs[name][1] or value
	-- 	local format = quoted and '%s = "%s";' or '%s = %s;'
	-- 	_p(level, format, name, overridenValue)
	-- end

	function xcode.XCBuildConfiguration_Target(tr, target, cfg)
		local cfgname = xcode.getconfigname(cfg)

		local bs = {}
		if cfg.xcodebuildsettings then
			if type(cfg.xcodebuildsettings) == "table" then
				bs = cfg.xcodebuildsettings
			end
		end

		_p(2,'%s /* %s */ = {', cfg.xcode.targetid, cfgname)
		_p(3,'isa = XCBuildConfiguration;')
		_p(3,'buildSettings = {')

		-- printSetting(bs, 4, "ALWAYS_SEARCH_USER_PATHS", "NO")

		xcode.PrintUserSettings(4, cfg)
		xcode.PrintBuildSetting(4, 'ALWAYS_SEARCH_USER_PATHS = NO;', cfg)

		xcode.PrintBuildSetting(4, 'ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;', cfg)
		xcode.PrintBuildSetting(4, 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;', cfg)

		if premake.config.isDebugBuild(cfg) then
			-- printSetting(bs, 4,"DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym", true)
			xcode.PrintBuildSetting(4, 'DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";', cfg)
		else
			xcode.PrintBuildSetting(4, 'DEBUG_INFORMATION_FORMAT = "dwarf";', cfg)
		end

		xcode.printlist(cfg.libdirs, 'FRAMEWORK_SEARCH_PATHS', cfg)

		if cfg.kind ~= "StaticLib" and cfg.buildtarget.prefix ~= "" then
			-- printSetting(bs, 4,"EXECUTABLE_PREFIX", cfg.buildtarget.prefix)
			xcode.PrintBuildSetting(4, 'EXECUTABLE_PREFIX = '.. cfg.buildtarget.prefix .. ';', cfg)
		end

		--if cfg.targetextension then
			--local ext = cfg.targetextension
			--ext = iif(ext:startswith("."), ext:sub(2), ext)
			--_p(4,'EXECUTABLE_EXTENSION = %s;', ext)
		--end

		local outdir = path.getrelative(tr.project.location, path.getdirectory(cfg.buildtarget.relpath))
		if outdir ~= "." then
			--printSetting(bs, 4,"CONFIGURATION_BUILD_DIR", outdir)
			xcode.PrintBuildSetting(4, 'CONFIGURATION_BUILD_DIR = "' .. outdir .. '";', cfg)
		end

		-- printSetting(bs, 4,"GCC_DYNAMIC_NO_PIC", "NO")
		-- printSetting(bs, 4,"GCC_MODEL_TUNING", "G5")
		xcode.PrintBuildSetting(4, 'GCC_DYNAMIC_NO_PIC = NO;', cfg)
		-- xcode.PrintBuildSetting(4, 'GCC_MODEL_TUNING = G5;', cfg)

		if tr.infoplist then
			local fcfg = config.findfile(cfg, path.getextension(tr.infoplist.name))
			-- printSetting(bs, 4,"INFOPLIST_FILE", fcfg, true)
			if fcfg then
				xcode.PrintBuildSetting( 4,'INFOPLIST_FILE = "' .. fcfg ..  '";', cfg)
			end
		end

		installpaths = {
			ConsoleApp = '/usr/local/bin',
			WindowedApp = '"$(HOME)/Applications"',
			SharedLib = '/usr/local/lib',
			StaticLib = '/usr/local/lib',
		}

		local builtInSettings = {
			"ASSETCATALOG_COMPILER_APPICON_NAME",
			"ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME",
			"ALWAYS_SEARCH_USER_PATHS",
			"DEBUG_INFORMATION_FORMAT",
			"EXECUTABLE_PREFIX",
			"CONFIGURATION_BUILD_DIR",
			"GCC_DYNAMIC_NO_PIC",
			"GCC_MODEL_TUNING",
			"INFOPLIST_FILE",
			"INSTALL_PATH",
			"PRODUCT_NAME",
			"TARGETED_DEVICE_FAMILY",
			"CODE_SIGN_IDENTITY"
		}

		local additionalSettings = {
			"GCC_VERSION",
			"GCC_SYMBOLS_PRIVATE_EXTERN",
			"CODE_SIGN_IDENTITY",
			"IPHONEOS_DEPLOYMENT_TARGET"
		}

		local function inTable(tbl, item)
			for key, value in pairs(tbl) do
				if value == item then return key end
			end
			return false
		end
		-- go trough all the overridden settings and write those that havent been already processed
		for key, value in pairs(bs) do
			if not inTable(builtInSettings, key) and inTable(additionalSettings, key) then
				_p(4,'%s = "%s";', key, value[1])
			end
		end

		-- printSetting(bs, 4,"INSTALL_PATH", installpaths[cfg.kind])
		xcode.PrintBuildSetting(4, 'INSTALL_PATH = ' .. installpaths[cfg.kind] .. ';', cfg)
		-- printSetting(bs, 4,"PRODUCT_NAME", cfg.buildtarget.basename, true)
		xcode.PrintBuildSetting(4, 'PRODUCT_NAME = "' .. cfg.buildtarget.basename .. '";', cfg)

		if cfg.platform == "ios" then
			xcode.PrintBuildSetting(4, 'TARGETED_DEVICE_FAMILY = "1,2";', cfg)
			xcode.PrintBuildSetting(4, 'CODE_SIGN_IDENTITY = "iPhone Developer";', cfg)
		end

		_p(3,'};')
		_p(3,'name = "%s";', cfgname)
		_p(2,'};')
	end


	function xcode.XCBuildConfiguration_Project(tr, cfg)
		local cfgname = xcode.getconfigname(cfg)

		if not cfg.platform then
			return
		end

		-- local bs = {}
		-- if cfg.xcodebuildsettings then
		-- 	if type(cfg.xcodebuildsettings) == "table" then
		-- 		bs = cfg.xcodebuildsettings
		-- 	end
		-- end

		_p(2,'%s /* %s */ = {', cfg.xcode.projectid, cfgname)
		_p(3,'isa = XCBuildConfiguration;')
		_p(3,'buildSettings = {')

		local builtInSettings =
		{
			"ARCHS",
			"CONFIGURATION_BUILD_DIR",
			"CONFIGURATION_TEMP_DIR",
			"COPY_PHASE_STRIP",
			"GCC_C_LANGUAGE_STANDARD",
			"GCC_ENABLE_CPP_EXCEPTIONS",
			"GCC_ENABLE_CPP_RTTI",
			"GCC_ENABLE_FIX_AND_CONTINUE",

			"GCC_ENABLE_OBJC_EXCEPTIONS",
			"GCC_OPTIMIZATION_LEVEL",
			"GCC_PRECOMPILE_PREFIX_HEADER",

			"GCC_SYMBOLS_PRIVATE_EXTERN",
			"GCC_TREAT_WARNINGS_AS_ERRORS",
			"GCC_WARN_ABOUT_RETURN_TYPE",
			"GCC_WARN_UNUSED_VARIABLE",
			"WARNING_CFLAGS"
		}

		local additionalSettings =
		{
			"SDKROOT",
			"VALID_ARCHS",
			"CLANG_CXX_LANGUAGE_STANDARD",
			"CLANG_CXX_LIBRARY"
		}

		xcode.PrintUserSettings(4, cfg)

		if cfg.platform == "ios" then
			xcode.PrintBuildSetting(4, 'SDKROOT = iphoneos;', cfg)
			xcode.PrintBuildSetting(4, 'ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";', cfg)
			xcode.PrintBuildSetting(4, 'SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";', cfg)
		elseif cfg.platform == "osx64" then
			xcode.PrintBuildSetting(4, 'SDKROOT = macosx;', cfg)
			xcode.PrintBuildSetting(4, 'ARCHS = "$(NATIVE_ARCH_ACTUAL)";', cfg)
			xcode.PrintBuildSetting(4, 'SUPPORTED_PLATFORMS = "macosx";', cfg)
		else
			error("Platform '" .. cfg.platform .. "' is not supported with Xcode solutions")
		end

		local targetdir = path.getdirectory(cfg.buildtarget.relpath)

		if targetdir ~= "." then
			-- printSetting(bs, 4, "CONFIGURATION_BUILD_DIR" , "$(SYMROOT)", true);
			xcode.PrintBuildSetting(4, 'CONFIGURATION_BUILD_DIR = "$(SYMROOT)";', cfg);
		end

		-- printSetting(bs, 4,"CONFIGURATION_TEMP_DIR" , "$(OBJROOT)", true)
		xcode.PrintBuildSetting(4, 'CONFIGURATION_TEMP_DIR = "$(OBJROOT)";', cfg)

		if cfg.flags.Symbols then
			-- printSetting(bs, 4,"COPY_PHASE_STRIP" , "NO")
			xcode.PrintBuildSetting(4,'COPY_PHASE_STRIP = NO;', cfg)
		end

		-- printSetting(bs, 4, "GCC_C_LANGUAGE_STANDARD" , "gnu99")
		-- xcode.PrintBuildSetting(4, 'GCC_C_LANGUAGE_STANDARD = "gnu99";', cfg)

		xcode.PrintBuildSetting(4, 'CLANG_CXX_LANGUAGE_STANDARD = "c++0x";', cfg)
		xcode.PrintBuildSetting(4, 'CLANG_CXX_LIBRARY = "libc++";', cfg)

		if cfg.flags.NoExceptions then
			-- printSetting(bs, 4, "GCC_ENABLE_CPP_EXCEPTIONS", "NO")
			xcode.PrintBuildSetting(4, 'GCC_ENABLE_CPP_EXCEPTIONS = NO;', cfg)
		end

		if cfg.flags.NoRTTI then
			-- printSetting(bs, 4,"GCC_ENABLE_CPP_RTTI", "NO")
			xcode.PrintBuildSetting(4, 'GCC_ENABLE_CPP_RTTI = NO;', cfg)
		end

		if cfg.flags.Symbols and not cfg.flags.NoEditAndContinue then
			-- printSetting(bs, 4, "GCC_ENABLE_FIX_AND_CONTINUE" , "YES")
			xcode.PrintBuildSetting(4, 'GCC_ENABLE_FIX_AND_CONTINUE = YES;', cfg)
		end

		if cfg.flags.NoExceptions then
			-- printSetting(bs, 4, "GCC_ENABLE_OBJC_EXCEPTIONS" , "NO")
			xcode.PrintBuildSetting(4, 'GCC_ENABLE_OBJC_EXCEPTIONS = NO;', cfg)
		end

		local map = { On = "3", Size = "s", Speed = "3" }

		-- printSetting(bs, 4, "GCC_OPTIMIZATION_LEVEL" , map[cfg.optimize] or "0")
		xcode.PrintBuildSetting(4, 'GCC_OPTIMIZATION_LEVEL = ' .. (map[cfg.optimize] or "0") .. ';', cfg)

		if cfg.pchheader and not cfg.flags.NoPCH then
			-- printSetting(bs, 4, "GCC_PRECOMPILE_PREFIX_HEADER" , "YES")
			-- printSetting(bs, 4, "GCC_PREFIX_HEADER", cfg.pchheader, true)
			xcode.PrintBuildSetting(4, 'GCC_PRECOMPILE_PREFIX_HEADER = YES;', cfg)
			xcode.PrintBuildSetting(4, 'GCC_PREFIX_HEADER = "' ..  cfg.pchheader .. '";', cfg)
		end

		xcode.printlist(cfg.defines, 'GCC_PREPROCESSOR_DEFINITIONS', cfg)

		-- printSetting(bs, 4,"GCC_SYMBOLS_PRIVATE_EXTERN" ,"NO")
		xcode.PrintBuildSetting(4, 'GCC_SYMBOLS_PRIVATE_EXTERN = NO;', cfg)

		if cfg.flags.FatalWarnings then
			-- printSetting(bs, 4, "GCC_TREAT_WARNINGS_AS_ERRORS" , "YES")
			xcode.PrintBuildSetting(4, 'GCC_TREAT_WARNINGS_AS_ERRORS = YES;', cfg)
		end

		-- printSetting(bs, 4,"GCC_WARN_ABOUT_RETURN_TYPE", "YES")
		-- printSetting(bs, 4,"GCC_WARN_UNUSED_VARIABLE", "YES")
		xcode.PrintBuildSetting(4, 'GCC_WARN_ABOUT_RETURN_TYPE = YES;', cfg)
		xcode.PrintBuildSetting(4, 'GCC_WARN_UNUSED_VARIABLE = YES;', cfg)

		for i,v in ipairs(cfg.includedirs) do
			cfg.includedirs[i] = premake.project.getrelative(cfg.project, cfg.includedirs[i])
		end

		for i,v in ipairs(cfg.libdirs) do
			-- print(cfg.libdirs[i])
			cfg.libdirs[i] = premake.project.getrelative(cfg.project, cfg.libdirs[i])
		end

		-- xcode.printlist(cfg.includedirs, 'HEADER_SEARCH_PATHS')
		-- xcode.printlist(cfg.libdirs, 'LIBRARY_SEARCH_PATHS')
		xcode.printlist(cfg.includedirs, 'HEADER_SEARCH_PATHS', cfg)
		xcode.printlist(cfg.libdirs, 'LIBRARY_SEARCH_PATHS', cfg)

		local objDir = path.getrelative(tr.project.location, cfg.objdir)
		-- printSetting(bs, 4, "OBJROOT", objDir, true)
		-- printSetting(bs, 4,"ONLY_ACTIVE_ARCH" ,iif(premake.config.isDebugBuild(cfg),'YES','NO'))
		xcode.PrintBuildSetting(4, 'OBJROOT = "' .. cfg.objdir .. '";', cfg)
		xcode.PrintBuildSetting(4, 'ONLY_ACTIVE_ARCH = ' .. iif(premake.config.isDebugBuild(cfg), 'YES', 'NO') .. ';', cfg)
		xcode.PrintBuildSetting(4, 'ENABLE_BITCODE = NO;', cfg)

		-- build list of "other" C/C++ flags
		local checks = {
			["-ffast-math"]          = cfg.flags.FloatFast,
			["-ffloat-store"]        = cfg.flags.FloatStrict,
			["-fomit-frame-pointer"] = cfg.flags.NoFramePointer,
		}

		local flags = { }
		for flag, check in pairs(checks) do
			if check then
				table.insert(flags, flag)
			end
		end
		xcode.printlist(table.join(flags, cfg.buildoptions), 'OTHER_CFLAGS', cfg)

		-- build list of "other" linked flags. All libraries that aren't frameworks
		-- are listed here, so I don't have to try and figure out if they are ".a"
		-- or ".dylib", which Xcode requires to list in the Frameworks section
		flags = { }
		for _, lib in ipairs(config.getlinks(cfg, "system")) do
			if not xcode.isframework(lib) then
				table.insert(flags, "-l" .. lib)
			end
		end

		--ms this step is for reference projects only
		for _, lib in ipairs(config.getlinks(cfg, "dependencies", "object")) do
			if (lib.external) then
				if not xcode.isframework(lib.linktarget.basename) then
					table.insert(flags, "-l" .. lib.linktarget.basename)
				end
			end
		end

		flags = table.join(flags, cfg.linkoptions)
		xcode.printlist(flags, 'OTHER_LDFLAGS', cfg)

		if cfg.flags.StaticRuntime then
			-- printSetting(bs, 4,"STANDARD_C_PLUS_PLUS_LIBRARY_TYPE", "static")
			xcode.PrintBuildSetting(4, 'STANDARD_C_PLUS_PLUS_LIBRARY_TYPE = static;', cfg)
		end

		if targetdir ~= "." then
			-- printSetting(bs, 4,"SYMROOT", path.getrelative(tr.project.location, targetdir), true)
			xcode.PrintBuildSetting(4, 'SYMROOT = "' .. targetdir .. '";', cfg)
		end

		if cfg.warnings == "Extra" then
			-- printSetting(bs, 4,"WARNING_CFLAGS" , "-Wall", true)
			xcode.PrintBuildSetting(4,'WARNING_CFLAGS = "-Wall";', cfg)
		end

		local function inTable(tbl, item)
			for key, value in pairs(tbl) do
				if value == item then return key end
			end
			return false
		end

		-- go trough all the overridden settings and write those that havent been already processed
		-- for key, value in pairs(bs) do
		-- 	if not inTable(builtInSettings, key) and inTable(additionalSettings, key) then
		-- 		_p(4,'%s = "%s";', key, value[1])
		-- 	end
		-- end

		_p(3,'};')
		_p(3,'name = "%s";', cfgname)
		_p(2,'};')
	end


	function xcode.XCBuildConfiguration(tr)
		_p('/* Begin XCBuildConfiguration section */')
		for _, target in ipairs(tr.products.children) do
			for _, cfg in ipairs(tr.configs) do
				xcode.XCBuildConfiguration_Target(tr, target, cfg)
			end
		end
		for _, cfg in ipairs(tr.configs) do
			xcode.XCBuildConfiguration_Project(tr, cfg)
		end
		_p('/* End XCBuildConfiguration section */')
		_p('')
	end


	function xcode.XCBuildConfigurationList(tr)
		local sln = tr.project.solution

		_p('/* Begin XCConfigurationList section */')
		for _, target in ipairs(tr.products.children) do
			_p(2,'%s /* Build configuration list for PBXNativeTarget "%s" */ = {', target.cfgsection, target.name)
			_p(3,'isa = XCConfigurationList;')
			_p(3,'buildConfigurations = (')
			for _, cfg in ipairs(tr.configs) do
				_p(4,'%s /* %s */,', cfg.xcode.targetid, xcode.getconfigname(cfg))
			end
			_p(3,');')
			_p(3,'defaultConfigurationIsVisible = 0;')
			_p(3,'defaultConfigurationName = "%s";', xcode.getconfigname(tr.configs[1]))
			_p(2,'};')
		end
		_p(2,'1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "%s" */ = {', tr.name)
		_p(3,'isa = XCConfigurationList;')
		_p(3,'buildConfigurations = (')
		for _, cfg in ipairs(tr.configs) do
			_p(4,'%s /* %s */,', cfg.xcode.projectid, xcode.getconfigname(cfg))
		end
		_p(3,');')
		_p(3,'defaultConfigurationIsVisible = 0;')
		_p(3,'defaultConfigurationName = "%s";', xcode.getconfigname(tr.configs[1]))
		_p(2,'};')
		_p('/* End XCConfigurationList section */')
		_p('')
	end


	function xcode.Footer()
		_p(1,'};')
		_p('\trootObject = 08FB7793FE84155DC02AAC07 /* Project object */;')
		_p('}')
	end
