local lib = starlit.mod.lib
local T,G = lib.marshal.t, lib.marshal.g

local function U(unit, prec, fixed)
	local trunc = 2
	if fixed then
		return function(amt, excludeUnit)
			local ta = lib.math.trim(amt/prec, trunc)
			if excludeUnit then return tostring(ta) end
			return string.format("%s%s", ta, unit)
		end
	else
		return function(amt, excludeUnit)
			local ta = lib.math.trim(amt/prec, trunc)
			if excludeUnit then return tostring(ta) end
			return lib.math.si(unit, amt/prec, nil, nil, trunc)
		end
	end
end

local function C(h, s, l)
	return lib.color {hue = h, sat = s or 1, lum = l or .7}
end
starlit.world.stats = {
	psi        = {min = 0, max = 500, base = 0, desc = U('ψ', 1), color = C(320), name = 'numina', srzType = T.decimal};
	-- numina is measured in ψ
	warmth     = {min = -1000, max = 1000, base = 0, desc = U('°C', 10, true), color = C(5), name = 'warmth'};
	-- warmth in measured in d°C
	fatigue    = {min = 0, max = 76 * 60, base = 0, desc = U('hr', 60, true), color = C(288,.3,.5), name = 'fatigue', harm=true, srzType = T.decimal};
	-- fatigue is measured in minutes one needs to sleep to cure it
	stamina    = {min = 0, max = 10 * 20, base = true, desc = U('m', 100), color = C(88), name = 'stamina'};
	-- stamina is measured in how many 10th-nodes (== cm) one can sprint
	nutrition  = {min = 0, max = 8000, base = 0, desc = U('kCal', 1, true), color = C(43,.5,.4), name = 'nutrition', srzType = T.decimal};
	-- hunger is measured in kcalories one must consume to cure it. at 0, you start dying
	hydration  = {min = 0, max = 4, base = 0, desc = U('L', 1), color = C(217, .25,.4), name = 'hydration', srzType = T.decimal};
	-- thirst is measured in L of H²O required to cure it
	morale     = {min = 0, max = 10 * 24 * 60, base = true, color = C(0,0,.8), name = 'morale', srzType = T.decimal;
		desc = function(amt, excU) return lib.math.timespec(amt) end};
	-- morale is measured in minutes. e.g. at base rate morale degrades by
	-- 60 points every hour. morale can last up to 10 earthdays
	irradiation = {min = 0, max = 10, base = 0, desc = U('Gy', 1), color = C(141,1,.5), name = 'irradiation', harm=true, srzType = T.decimal};
	-- irrad is measured is milligreys
	-- 1Gy counters natural healing
	-- ~3Gy counters basic nanomedicine
	-- 5Gy causes death within two weeks without nanomedicine
	-- radiation speeds up psi regen
	-- morale drain doubles with each 2Gy
	illness    = {min = 0, max = 1, base = 0, desc = U('%', .01, true), color = C(71,.4,.25), name = 'illness', harm=true, srzType = T.factor};
	-- as illness increases, maximum stamina and health gain a corresponding limit
	-- illness is increased by certain conditions, and decreases on its own as your
	-- body heals when those conditions wear off. some drugs can lower accumulated illness
	-- but illness-causing conditions require specific cures
	-- illness also causes thirst and fatigue to increase proportionately
}

starlit.world.statOrder = {
	'health', 'stamina', 'psi', 'warmth';

	'nutrition', 'hydration', 'irradiation';
	'illness', 'morale', 'fatigue';
}

local impactStruct = G.struct {
	base = G.array(8, G.struct {id = T.str, val = T.decimal});
-- 	alt = G.array(8, )
}

starlit.type.impact = lib.class {
	__name = 'starlit.type.impact';
	construct = function(p)
		-- deltaPack {
		-- 	nutrition = 5.0;
		-- 	hydration = -1;
		--		{"Taste", 'good'; {'morale', 1}};
		--		{"Narcotic", 'mixed'; {'morale', 10};
		--                          {'addiction', function(user) ... end}};
		-- 	{"Magic", 'info'; 'casts an magic spell', function(user) ... end};
		-- }
		return p
	end;
-- 	__tostring = function(me)
-- 	end;
	__index = {
		pushProps = function(me, p)
			for i,k in pairs(starlit.world.statOrder) do
				if me[k] then
					local s = starlit.world.stats[k]

					local v = me[k]
					local good = v > 0
					if s.harm then good = not good end
					local d = s.desc(v)
					if v > 0 then d = '+' .. d end

					table.insert(p, {
						title = lib.str.capitalize(s.name);
						affinity = good and 'good' or 'bad';
						desc = d;
					})
				end
			end
			for i,v in ipairs(me) do
				local label, aff = v[1], v[2]
				local effectStrs = {}
				for i = 3, #v do
					local t = v[i]
					local str
					if type(t) == 'table' then
						local id, amt = t[1], t[2]
						local s = starlit.world.stats[id]
						local desc = s.desc(amt)
						if amt > 0 then desc = '+' .. desc end
						str = string.format('%s to %s', desc, s.name)
					-- elseif type(v[i]) == 'function' then
					elseif type(t) == 'string' then
						str = t
					end
					if str then table.insert(effectStrs, str) end
				end

				table.insert(p, {
					title = lib.str.capitalize(label);
					affinity = aff;
					desc = next(effectStrs) and table.concat(effectStrs, ', ') or nil;
				})
			end
		end;
		propTable = function(me)
			local t = {}
			me:pushProps(t)
			return t
		end;
		addToItem = function(me, stack, key)
			key = key or 'starlit:impact'
			local m = stack:get_meta()
			local tbl = {base={}}
			for k,v in pairs(me) do
				if type(k) == 'string' then
					tbl.base[k] = v
				end
			end
			local armored = lib.str.meta_armor(impactStruct.enc(tbl))
			m:set_string(key, armored)
			return stack
		end;
		effective = function(me, stack, key)
			key = key or 'starlit:impact'
			local c = starlit.type.impact.clone(me)

			local m = stack:get_meta()
			if m:contains(key) then
				local tbl = impactStruct.dec(lib.str.meta_dearmor(m:get_string(key)))
				for k,v in pairs(tbl.base) do c[k] = v end
			end

			return c
		end;
		apply = function(me, user)
			for k,v in pairs(me) do
				if starlit.world.stats[k] then
					user:statDelta(k, v)
				elseif type(k) == 'number' then
					for i = 3, #v do
						local t = v[i]
						if type(t) == 'table' then
							local id, amt = t[1], t[2]
							user:statDelta(id, amt)
						elseif type(t) == 'function' then
							t(user)
						end
					end
				end
			end
		end;
	};
}