-- This is an example illustrating how to use cnrun package in Lua.
--
-- 1. After loading the cnrun module with 'require', the first step is
--    to get or create an interpreter context.  It is an opaque light
--    user data object, which you will pass as the first argument to
--    all subsequent calls to cnrun functions.
--
-- 2. You can create and keep multiple models in a context, modify and
--    advance them independently. Models are identified by a label (a
--    string).
--
-- 3. On error, all cnrun methods return two values: first a nil,
--    second a string describing what went wrong.  On success, the
--    first value is 1 (an integer), and the rest are method-specific.
--
-- 4. Don't lose the context object.  It will not be gabage-collected
--    for you (it is a C++ thing).

-- To execute this script with lua-5.1, do s/table.unpack/unpack/g.

local M = require("cnrun")

local res, ult, result
local C, model

M.dump_available_units ()

res, ult = M.get_context ()
if res == nil then
   print (ult)
   return
end
C = ult

local mname = "FAFA"
res, ult = M.new_model (C, mname)
if res == nil then
   print (ult)
   return
end
model = ult
print ("Created model")

print ("Setting verbosely to 4")
M.set_model_parameter (C, mname, "verbosely", 4)

result = {M.list_models (C)}
res, ult = result[1], {table.unpack(result, 2)}
if res == nil then
   print (ult)
   return
end
print ()

print ("Model(s):")
local model_list = ult
print (table.concat(model_list))
print ()


res, ult = M.import_nml (C, mname, "m.nml")
if res == nil then
   print (ult)
   -- return
end
print ()


print ("Host parmeters:")
local parameters = {
   "verbosely", "integration_dt_min",
   "integration_dt_max", "integration_dt_cap",
   "listen_dt", "listen_mode",
   "sxf_start_delay", "sxf_period", "sdf_sigma"
}
local fmt = " %22s: %-q"
for i,p in ipairs(parameters) do
   res, ult = M.get_model_parameter (C, mname, p)
   print (string.format (fmt, p, ult))
end
print ()

res, ult = M.delete_model (C, "fafa moo")
if res == nil then
   print (ult .. " (ignored)")
   -- return
end


result = {M.get_units_matching(C, mname, "L.*")}
res, ult = result[1], {table.unpack(result, 2)}
if res == nil then
   print (ult)
   return
end
print ()
print ("There are " .. #ult .. " unit(s) matching L.*:")
local unit_list = ult
local fmt = " %-10s %-16s %-16s %-12s %-16s %-6s"
print (string.format(
          fmt,
          "label", "class", "family", "species", "has_sources", "is_altered"))
print (string.rep('-', 87))
for _, u in ipairs(unit_list) do
   result = {M.get_unit_properties (C, mname, u)}
   res, ult = result[1], {table.unpack(result, 2)}
   local b = function (x) if x then return "yes" else return "no" end end
   print (string.format(
             fmt,
             ult[1], ult[2], ult[3], ult[4], b(ult[5]), b(ult[6])))
end
print()


print ("Advancing 10 sec:")
res, ult = M.advance (C, mname, 10000)


print ("Modify parameter:")
local u, p, v0, v9, vr = "LNz.0", "gNa"
_, ult = M.get_unit_parameter (C, mname, u, p)
v0 = ult
_, ult = M.set_unit_parameter (C, mname, u, p, v0 * 2)
_, ult = M.get_unit_parameter (C, mname, u, p)
v9 = ult
-- with a revert
res, ult = M.revert_matching_unit_parameters (C, mname, u)
if res == nil then
   print (ult)
   return
end
local count_reset = ult
_, ult = M.get_unit_parameter (C, mname, u, p)
vr = ult
print (string.format(
          ".. changed %s of %s from %g to %g, then reset (%d affected) to %g\n",
          p, u, v0, v9, count_reset, vr))


print ("Modify parameter in bulk:")
local us, ut, gsyn = "LNz.0", "LN1.0"
_, ult = M.set_matching_synapse_parameter (C, mname, us, ut, "gsyn", 4.2)
if res == nil then
   print (ult)
   return
end
print (string.format(
          ".. changed gsyn of synapse connecting %s to %s, to %g\n",
          us, ut, 4.2))

res, ult = M.describe_model (C, mname)


print ("State variables:")
for i = 1, 6, 1 do
   M.advance (C, mname, 1000)
   result = {M.get_unit_vars (C, mname, "LNz.0")}
   res, ult = result[1], {table.unpack(result, 2)}
   print (table.concat(ult, '; '))
end
print()


local affected, remaining
print ("Putout:")
-- there is unit_list already:
math.randomseed(os.time())
local deleting = unit_list[math.random(1, #unit_list)]
-- deleting, _ = string.gsub(deleting, ".", "\\.")
res, ult = M.putout (C, mname, deleting)
if res == nil then
   print (ult)
   return
end
print (string.format(".. deleted unit %s", deleting))
print()

print ("Decimate:")
res, ult = M.decimate (C, mname, "L.*", 0.3)
if res == nil then
   print (nil)
   return
end
affected, remaining = ult
remaining = #{M.get_units_matching (C, mname, ".*")} - 1
print (string.format(
          ".. %d units gone, %d remaining",
          affected, remaining))
print()


res, ult = M.delete_model (C, mname)
if res == nil then
   print ("Error: Failed to delete model: ", ult)
   return
end
print ("Model ".. ult .. " deleted")

res, ult = M.drop_context (C)
if res == nil then
   print ("Error: Failed to drop context: ", ult)
   return
end
print ("Context dropped: " .. ult)
