-- vim: ft=terra
local util = dofile 'common.lua'
local struct srv {
	sources: lib.mem.ptr(lib.store.source)
	webmgr: lib.net.mg_mgr
	webcon: &lib.net.mg_connection
}

local handle = {
	http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
		switch event do
			case lib.net.MG_EV_HTTP_MSG then
				lib.dbg('routing HTTP request')
				var msg = [&lib.net.mg_http_message](p)

			end
		end
	end;
}
local char = macro(function(ch) return `[string.byte(ch:asvalue())] end)
local terra cfg(s: &srv, befile: rawstring)
	lib.report('configuring backends from ', befile)

	var fr = lib.file.open(befile, [lib.file.mode.read])
	if fr.ok == false then
		lib.bail('could not open configuration file ', befile)
	end

	var f = fr.val
	var c: lib.mem.vec(lib.store.source) c:init(8)
	var text: lib.string.acc text:init(64)
	do var buf: int8[64]
		while true do
			var ct = f:read(buf, [buf.type.N])
			if ct == 0 then break end
			text:push(buf, ct)
		end
	end
	f:close()

	var cur = text.buf
	var segs: tuple(&int8, &int8)[3] = array(
		{[&int8](0),[&int8](0)},
		{[&int8](0),[&int8](0)},
		{[&int8](0),[&int8](0)}
	)
	var segdup = [terra(s: {rawstring, rawstring})
		var sz = s._1 - s._0
		var str = s._0
		return [lib.mem.ptr(int8)] {
			ptr = lib.str.ndup(str, sz);
			ct = sz;
		}
	end]
	var fld = 0
	while (cur - text.buf) < text.sz do
		if segs[fld]._0 == nil then
			if not (@cur == char(' ') or @cur == char('\t') or @cur == char('\n')) then
				segs[fld] = {cur, nil}
			end
		else
			if fld < 2 and @cur == char(' ') or @cur == char('\t') then
				segs[fld]._1 = cur
				fld = fld + 1
				segs[fld] = {nil, nil}
			elseif @cur == char('\n') or cur == text.buf + (text.sz-1) then
				if fld < 2 then lib.bail('incomplete backend line in ', befile) else
					segs[fld]._1 = cur
					var src = c:new()
					src.id = segdup(segs[0])
					src.string = segdup(segs[2])
					src.backend = nil
					for i = 0,[lib.store.backends.type.N] do
						if lib.str.ncmp(segs[1]._0, lib.store.backends[i].id, segs[1]._1 - segs[1]._0) == 0 then
							src.backend = &lib.store.backends[i]
							break
						end
					end
					if src.backend == nil then
						lib.bail('unknown backend in ', befile)
					end
					src.handle = nil
					fld = 0
					segs[0] = {nil, nil}
				end
			end
		end
		cur = cur + 1
	end
	text:free()

	s.sources = c:crush()
end

--srv.methods.conf_set = terra(self: &srv, key: rawstring, val:rawstring)
--	self.sources.ptr[0]:conf_set(key, val)
--end

srv.metamethods.__methodmissing = macro(function(meth, self, ...)
	local primary, ptr, stat, simple = 0,1,2,3
	local tk, rt = primary
	local expr = {...}
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
		if fn == meth then
			rt = ft.type.returntype
			if rt == bool then tk = simple
			elseif rt.stat_basetype then tk = stat
			elseif rt.ptr_basetype then tk = ptr end
			break
		end
	end
	
	if tk == primary then
		return `self.sources.ptr[0]:[meth]([expr])
	else local ok, empty
		local r = symbol(rt)
		if tk == ptr then
			ok = `r.ptr ~= nil
			empty = `[rt]{ptr=nil,ct=0}
		elseif tk == stat then
			ok = `r.ok ~= false
			empty = `[rt]{ok=false,error=1}
		elseif tk == simple then
			ok = `r == true
			empty = `false
		end
		return quote
			var [r] = empty
			for i=0,self.sources.ct do var src = self.sources.ptr + i
				if src.handle ~= nil then
					r = src:[meth]([expr])
					if [ok] then break
						else r = empty end
				end
			end
		in r end
	end
end)

srv.methods.start = terra(self: &srv, befile: rawstring)
	cfg(self, befile)
	var success = false
	for i=0,self.sources.ct do var src = self.sources.ptr + i
		lib.report('opening data source ', src.id.ptr, '(', src.backend.id, ')')
		src.handle = src.backend.open(src)
		if src.handle ~= nil then success = true end
	end
	if not success then
		lib.bail('could not connect to any data sources!')
	end

	var dbbind = self:conf_get('bind')
	var envbind = lib.proc.getenv('parsav_bind')
	var bind: rawstring
	if envbind ~= nil then
		bind = envbind
	elseif dbbind.ptr ~= nil then
		bind = dbbind.ptr
	else bind = '[::]:10917' end

	lib.report('binding to ', bind)
	lib.net.mg_mgr_init(&self.webmgr)
	self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, nil)
	dbbind:free()

end

srv.methods.poll = terra(self: &srv)
	lib.net.mg_mgr_poll(&self.webmgr,1000)
end

srv.methods.shutdown = terra(self: &srv)
	lib.net.mg_mgr_free(&self.webmgr)
	for i=0,self.sources.ct do var src = self.sources.ptr + i
		lib.report('closing data source ', src.id.ptr, '(', src.backend.id, ')')
		src:close()
	end
	self.sources:free()
end

return srv
