
---------------------
namespace settings -- functions to read and write config files (*.ini);
---------------------

export constant version = "4.12.0"

--# EXPORTS 
/*
 --functions:
     settings:Save()
     settings:Load()
     settings:Add()

 -- debug: 
    -d SETTINGS
*/

include GtkEngine.e

if not equal(version,gtk:version) then
    Error(,,"GtkSettings version mismatch","should be version " & gtk:version)
end if

constant fmt = """[].[]=[]"""

------------------------------------------------------------------------
export function Save(sequence inifile, object ctl_list, integer debug=0)
------------------------------------------------------------------------
-- writes an 'ini' type file (file name and extension are up to you)
-- with an entry for each control on ctl_list. The controls MUST have 
-- been named, otherwise there's no way to save and retrieve the 
-- value/setting for that control.
------------------------------------------------------------------------
object comments = {}
object txt,line

if file_exists(inifile) then
   
   txt = read_lines(inifile)
   for i = 1 to length(txt) do -- extract the comments;
	line = txt[i] 
	if match("--",line) = 1
	or match("!",line) = 1
	or match("+",line) = 1
	or equal("\n",line)
	or equal({},line) then
		comments = append(comments,line)
	end if
   end for
   write_lines(inifile,comments) -- removing everything but the comments;
end if -- file exists

object setting, tmp = 0, pos = {0,0}
object t1, name, prop
atom handle
integer class

atom fn = open(inifile,"a")

for x = 1 to length(ctl_list) do 

	if string(ctl_list[x]) then 
		handle = pointer(ctl_list[x]) 
	else 
		handle = ctl_list[x] 
	end if
	
	name = get(handle,"name")
	class = class_id(handle)

	if class=GtkWindow then -- special handling for multiple properties;
	        printf(fn,"%s\n",{get_setting(handle,"size")})
	        pos = get(handle,"position")
	        printf(fn,"%s.position={%d,%d}\n",{name,pos[1],pos[2]})
	        
	else
		setting = get_setting(handle) -- others have 1 obvious 'saveable' property
		if length(setting) < 1 then
			continue
		end if

		if sequence(setting) then
			printf(fn,"%s\n",{setting})
		end if
	end if
end for

flush(fn)
close(fn)

-- if ! exists for this control:property, then ignore the updated setting;
tmp = read_lines(inifile)
for i = 1 to length(tmp) do 
   if match("--!",tmp[i]) = 1 then
	t1 = split(tmp[i],"=")
	t1 = t1[1][2..$]
   end if
end for

write_lines(inifile,tmp)

return 1
end function

------------------------------------------------------------------------
export function Load(sequence inifile, integer debug=0)
------------------------------------------------------------------------
-- used to restore settings from the specified ini file.
-- this should be called after the controls are 'realized'

object txt, line, obj, prop, val1=0, val2=0, val3=0, val4=0

if not(file_exists(inifile)) then
	write_file(inifile,sprintf("--%s\n\n",{inifile}),TEXT_MODE)
	display("Creating ini file: []",{inifile})
else
	if debug then display("Reading []",{inifile}) end if
end if

txt = read_lines(inifile)

integer err=0
object a,b

for i = 1 to length(txt) do

	line = txt[i]
	
	if match("--",line) > 2 then -- strip trailing comments;
		line = line[1..match("--",line)-2]
	end if
	
	if match("--!",line) = 1 then
		line = line[4..$] -- keep it
	end if
	
	if match("+",line) = 1 or match("!",line) then
		line = line[2..$] -- dito
	end if
	
	if match("--",line) or match(";",line) 
	or match("/*",line) then 
		continue -- a comment, do nothing with it
	end if 

	if length(line) > 0  then 
		line = split(line,'=')
		a = line[1] b = line[2]
		a = split(a,'.') 
		line = a & {b}

		if debug > 1 then display(line) end if
	
		if vlookup(line[1],registry,4,1,-1) = -1 then
			err = i
			Error(,"Ini Load Error",
				sprintf("Invalid object name: %s",{line[1]}),
				sprintf("Line #%d of %s",{i,filename(inifile)}))
		else
			if equal("data",line[2]) then 
				set(line[1],"data",line[3],line[4])
			else
				set(line[1],line[2],line[3])
			end if
		end if
	end if
end for

if debug = 1 then 
 for i = 1 to length(txt) do
	if i = err then
		display("ERROR:[:3]:[]",{i,txt[i]})
	else
		display("[:3]:[]",{i,txt[i]})
	end if
 end for
end if

write_lines(inifile,txt)

return 1
end function

-------------------------------------------------------------------------------------------
export function Add(object ini,object ctl, sequence prop, object v1=0, object v2=0)
-------------------------------------------------------------------------------------------
if atom(ctl) then ctl = get(ctl,"name") end if

Delete(ini,ctl,prop)

if equal("position",prop) or equal("size",prop) then
if string(v1) then v2 = v1[2] v1 = v1[1] end if
end if

if atom(v2) and v2=0 then
	writefln("+[].[]=[]",{ctl,prop,v1},{ini,"a"})
else
	writefln("+[].[]={[],[]}",{ctl,prop,v1,v2},{ini,"a"})
end if

return 1
end function	

--------------------------------------------------------------------
export function Delete(object ini,object ctl, sequence prop)
--------------------------------------------------------------------
if atom(ctl) then ctl = get(ctl,"name") end if
object tmp = read_lines(ini) 
object out = {}
object s1 = text:format("[].[]",{ctl,prop}) 
for i = 1 to length(tmp) do
	if match(s1,tmp[i]) then -- skip
	else out &= {tmp[i]}
	end if
end for
write_lines(ini,out)
return 1
end function

---------------------------------------------------------------------------------
function get_setting(object self, object property = 0) 
-- returns the 'important' value from a control
---------------------------------------------------------------------------------
-- returns settings string
-- object must be a named object, e.g. MyButton

-- MyButton.active=1
-- My Calendar.date={YYYY,MM,DD}

object name = 0
object dt

if string(self) then 
	name = self 
	self = vlookup(self,registry,4,1) 
end if

object val = 0
object txt = {}

integer x = find(self,vslice(registry,1))

	ifdef SETTINGS then
		display("SELF [] []",{x,self})
		display(registry[x])
	end ifdef
	
	switch class_id(self) do 
	-- this decides which value is the important one to save;
	-- and stores the control's current value into the registry;	
	
		case GtkEntry,GtkEntryBuffer then property = "text"

		case GtkCheckButton,GtkRadioButton, GtkToggleButton,
			GtkSwitch,GtkComboBox,GtkComboBoxText,
			GtkCheckMenuItem,GtkToggleToolButton, GtkSearchEntry
			then property = "active"

		case GtkPopover then property = "position"

		case GtkFontButton then property = "font name"

		case GtkAdjustment,GtkSpinButton,GtkScaleButton,GtkVolumeButton,
			GtkModelButton,GtkScale 
			then property = "value"

		case GtkEntryCompletion then property = "model"

		case GtkLinkButton then property = "uri"

		case GtkMenuItem then property = "sensitive"
			
		case GtkPaned then property = "position"
		        
		case GtkFileChooser, GtkFileChooserButton, GtkFileChooserWidget, GtkFileChooserDialog
			then property = "filename"
			
		case GtkFontChooser, GtkFontChooserWidget, GtkFontChooserDialog then
			property = "font"

		case GtkCalendar then 
			dt = get(self,"datetime",0)
			registry[x][5] = {"date",dt[1..3]}
			txt &= text:format(fmt,{registry[x][4],"date",dt[1..3]})
			return txt

		case GtkColorButton,GtkColorChooser,GtkColorChooserWidget,
			GtkColorChooserDialog then
			registry[x][5] = {"rgba",get(self,"rgba",1)}
			txt &= text:format(fmt,{registry[x][4],"rgba",get(self,"rgba",1)})
			return txt
		
	end switch
	
	if atom(property) then
	        return txt
	end if
	
	val = get(self,property)
	registry[x][5] = {property,val}
	txt &= text:format(fmt,{registry[x][4],property,val})
	
return txt
end function
