dump = function (o)
   if type(o) == 'table' then
      local s = '{ '
      for k,v in pairs(o) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dump(v) .. ','
      end
      return s .. '} '
   else
      return tostring(o)
   end
end

dumpf = function (o)
	outfile=io.open("dump-file.txt", "w")

	outfile:write(dump(o))
	outfile:close()
end

-- Minimally effective quoting
quote = function (astring)
    local quoted1 = string.gsub(astring,'\"', '\\"')
    local quoted2 = '"' .. string.gsub(quoted1,"%'", "\\'") .. '"'
	local linefeed = string.gsub(quoted2,"\n", "\\n")
	return linefeed
end


write_saved = function (o)
--	local escaped
   if type(o) == 'table' then
      local s = '{\n'
      for k,v in pairs(o) do
         if type(k) ~= 'number' then
		 k = '"'..k..'"'
		 end
         s = s .. '['..k..'] = ' .. write_saved(v) .. ',\n'
      end
      return s .. '}\n'
   elseif
		type(o) == 'string' then
		return quote(o)
   else
		return tostring(o)
   end
end

_size = function (t)		-- return number of elements in table
	local i = 0
	for _,_ in pairs(t) do
		i = i +1
	end
	return i
end


blank_detail = function()
			detail_name.title = ""
			detail_desc.title = ""
end

load_visibility=function()

	local infile = io.open(Settings_s, "r")

	if infile == nil then
		print (Settings_s .. "  not there, that's ok.")
		return false
	end
	io.close(infile)

	local acc, playerID, visible

	for line in io.lines(Settings_s) do

		acc,  playerID, visible =  string.match(line, "(%S+) (%S+) (%S+)")
		-- char might have been deleted or might be new setup.

		if(accounts[acc] == nil) then return false end
		if(accounts[acc].player == nil) then return false end

		if(accounts[acc].player[playerID] ~= nil) then
			if visible == "true" then
				accounts[acc].player[playerID].visible= true
			else
				accounts[acc].player[playerID].visible= false
			end
		end
	end
end


save_visibility=function()
	local outfile = io.open(Settings_s, "w")
	for acc,Account_t in pairs (accounts) do
		for playerID, player_t in pairs (Account_t.player) do

			if outfile == nil then
				print ("Couldn't open " .. Settings_s .. "  file for writing.")
				return false
			end

			outfile:write(acc .. " " .. playerID)

			if (player_t.visible) then
				outfile:write(" true\n")
			else
				outfile:write(" false\n")
			end
		end	 --player
	end --ac
	io.close(outfile)
end

generate_id=function()
--write a combined list of achievement id we look for to add to the in-game part (cut and paste) for filtering
    local unique_id= {}   -- (id,true}
	print("Generating ids.lua")
	local outfile=io.open("data/ids.lua", "w")

	if outfile == nil then
		print ("Couldn't open data/ids.lua file for writing.")
	end
	outfile:write("hist.IDVersion=" .. quote(version) .. "\n")

	outfile:write("hist.IDs = {" .. "\n")

	for _,i in ipairs(Group_Order) do
		outfile:write("--  " .. i ..  "\n")
		for _,j in ipairs (Group_Dat[i].id) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end

		end
	end

	for _,i in ipairs(Trials_Order) do
		outfile:write("-- Trial " .. i .. "\n")
		for _,j in ipairs (Trials_Dat[i].id) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end
		end
	end


	for i,j in pairs (DLC_Dat) do   -- i is DLC cname
		outfile:write("-- DLC: " ..i .. "\n")
		print(i)
		for k,l in ipairs (j.line) do
		--		print(k .. "  " ..l)
				if unique_id[l] == nil then
					unique_id[l] = true
					outfile:write("[" .. l .. "] = true,\n")
				end
		end
	end


	outfile:write("-- SQ " .. "\n")
	for j,_ in pairs (SQ_dat) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end
	end

	outfile:write("-- Pub " .. "\n")
	for _,j in ipairs (Pub_Dat_Char.id) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end
	end

	outfile:write("-- WB " .. "\n")
	for j,_ in pairs (WB_dat) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end
	end

	outfile:write("-- Specials " .. "\n")
	for j,_ in pairs (Special_dat) do
		    if unique_id[j] == nil then
				unique_id[j] = true
				outfile:write("[" .. j .. "] = true,\n")
			end
	end

	outfile:write("}" .. "\n")
	outfile:close()
end


--Run through Location data to get the info needed to dimension the WB and SQ boxes
--Columns are AreaName, Name, Description
-- parameter "name is Either WB or SQ
Location_Box = function (name)

	log("Location Box ")
	local cols = {}
	local lines = {}
	local return_t = iup.matrix{}

	if name == "WB" then
		for Area,Data in pairs(Area_names) do
			if Data.WB ~= nil then
				for _,Ach in ipairs (Data.WB) do
					table.insert(lines,{Area=Area, Ach=Ach})
				end
			end
		end
		return_t.numcol=3
    end
	if name == "SQ" then   -- Extra Column
		for Area,Data in pairs(Area_names) do
			if Data.SQ ~= nil then
				for _,Ach in ipairs (Data.SQ) do

					table.insert(lines,{Area=Area, Ach=Ach, Link='"' .. SQ_dat[Ach].link1 .. '"'})
				end
			end
		end
		return_t.numcol=4
    end


	return_t.numlin=#lines


	iup.SetAttribute(return_t, "READONLY", "YES")
	iup.SetAttribute(return_t, "ALIGNMENT0", "ACENTER")
	iup.SetAttribute(return_t, "WIDTH1", 80)	-- Location
	iup.SetAttribute(return_t, "WIDTH2", 110)	-- Name
	iup.SetAttribute(return_t, "WIDTH3", 350)   -- Description
	iup.SetAttribute(return_t, "WIDTH4", 250)   -- Link	SQ Only
	iup.SetAttribute(return_t, "ALIGNMENT3", "ALEFT")
	iup.SetAttribute(return_t, "ALIGNMENT4", "ALEFT")
	-- Set Headings
	return_t:setcell(0,0, L.Ach_ID)
	return_t:setcell(0,1, L.Location)
	return_t:setcell(0,2, L.Achievement)

	--Set Lines

	for line,Data in ipairs(lines) do
		return_t:setcell(line,0, tostring(Data.Ach))
		return_t:setcell(line,1, tostring(Area_names[Data.Area].name))
		return_t:setcell(line,2, tostring(Ach_Detail[Data.Ach].name))
		return_t:setcell(line,3, tostring(Ach_Detail[Data.Ach].description))
		if Data.Link ~=nil  then
			return_t:setcell(line,4, tostring(Data.Link))
		end

		if thischar.ach[Data.Ach] ~= nil then
				iup.SetAttribute(return_t,  "BGCOLOR" .. tostring(line) .. ":*", BG_Colour_Complete)
		else
				iup.SetAttribute(return_t,  "BGCOLOR" .. tostring(line) .. ":*", BG_Colour_Not_Complete)
		end

	end


	function return_t:click_cb (Line,C)
		if C == 4 then
			iup.Help(self:getcell(Line,C))		-- Launch Browser
		else
			return IUP_IGNORE
		end
	end
	return return_t
end



append=function (destination_t,source_t)
--takes 2 arrays. source_t is appended to destination_t
	if type(destination_t) ~= "table" then
		print("error (append), destination_t is not a table")
		return
	end

	if type(source_t) ~= "table" then
		print("error (append), source_t is not a table")
		return
	end

	for line, Ach in ipairs (source_t) do
		table.insert(destination_t,Ach)
	end
end


--for filtering
select_box=function (account_t)

-- account_t   is accounts[acc]

local marks_t = {}
local names_t = {}
for _,playerID in  ipairs(account_t.AllplayerIDs) do

	if account_t.player[playerID].visible then
		table.insert(marks_t,1)
	else
		table.insert(marks_t,0)
	end
	table.insert(names_t,account_t.player[playerID].name)

end

local size = #names_t

local error = iup.ListDialog(2,"Show Characters",size, names_t,0,1,size, marks_t)


if error == -1 then
	return false
end


for i,playerID in  ipairs(account_t.AllplayerIDs) do

	if (marks_t[i] == 0) then
		account_t.player[playerID].visible = false
	else
		account_t.player[playerID].visible = true
	end

end




for _, ADung in pairs (account_t.alldungeons) do	-- for each dungeon

	for col,playerID in  ipairs(account_t.AllplayerIDs) do

		if account_t.player[playerID].visible then
			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"100")
		else
			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"0")
		end
	end
end

save_visibility()

return true
end