1--[[
2	Licensed according to the included 'LICENSE' document
3	Author: Thomas Harning Jr <harningt@gmail.com>
4]]
5local type = type
6local print = print
7local tostring = tostring
8local pairs = pairs
9local getmetatable, setmetatable = getmetatable, setmetatable
10local select = select
11
12local _ENV = nil
13
14local function foreach(tab, func)
15	for k, v in pairs(tab) do
16		func(k,v)
17	end
18end
19local function printValue(tab, name)
20        local parsed = {}
21        local function doPrint(key, value, space)
22                space = space or ''
23                if type(value) == 'table' then
24                        if parsed[value] then
25                                print(space .. key .. '= <' .. parsed[value] .. '>')
26                        else
27                                parsed[value] = key
28                                print(space .. key .. '= {')
29                                space = space .. ' '
30                                foreach(value, function(key, value) doPrint(key, value, space) end)
31                        end
32                else
33					if type(value) == 'string' then
34						value = '[[' .. tostring(value) .. ']]'
35					end
36					print(space .. key .. '=' .. tostring(value))
37                end
38        end
39        doPrint(name, tab)
40end
41
42local function clone(t)
43	local ret = {}
44	for k,v in pairs(t) do
45		ret[k] = v
46	end
47	return ret
48end
49
50local function inner_merge(t, remaining, from, ...)
51	if remaining == 0 then
52		return t
53	end
54	if from then
55		for k,v in pairs(from) do
56			t[k] = v
57		end
58	end
59	return inner_merge(t, remaining - 1, ...)
60end
61
62--[[*
63    Shallow-merges tables in order onto the first table.
64
65    @param t table to merge entries onto
66    @param ... sequence of 0 or more tables to merge onto 't'
67
68    @returns table 't' from input
69]]
70local function merge(t, ...)
71	return inner_merge(t, select('#', ...), ...)
72end
73
74-- Function to insert nulls into the JSON stream
75local function null()
76	return null
77end
78
79-- Marker for 'undefined' values
80local function undefined()
81	return undefined
82end
83
84local ArrayMT = {}
85
86--[[
87	Return's true if the metatable marks it as an array..
88	Or false if it has no array component at all
89	Otherwise nil to get the normal detection component working
90]]
91local function IsArray(value)
92	if type(value) ~= 'table' then return false end
93	local meta = getmetatable(value)
94	local ret = meta == ArrayMT or (meta ~= nil and meta.__is_luajson_array)
95	if not ret then
96		if #value == 0 then return false end
97	else
98		return ret
99	end
100end
101local function InitArray(array)
102	setmetatable(array, ArrayMT)
103	return array
104end
105
106local CallMT = {}
107
108local function isCall(value)
109	return CallMT == getmetatable(value)
110end
111
112local function buildCall(name, ...)
113	local callData = {
114		name = name,
115		parameters = {n = select('#', ...), ...}
116	}
117	return setmetatable(callData, CallMT)
118end
119
120local function decodeCall(callData)
121	if not isCall(callData) then return nil end
122	return callData.name, callData.parameters
123end
124
125local function doOptionMerge(options, nested, name, defaultOptions, modeOptions)
126	if nested then
127		modeOptions = modeOptions and modeOptions[name]
128		defaultOptions = defaultOptions and defaultOptions[name]
129	end
130	options[name] = merge(
131		{},
132		defaultOptions,
133		modeOptions,
134		options[name]
135	)
136end
137
138local json_util = {
139	printValue = printValue,
140	clone = clone,
141	merge = merge,
142	null = null,
143	undefined = undefined,
144	IsArray = IsArray,
145	InitArray = InitArray,
146	isCall = isCall,
147	buildCall = buildCall,
148	decodeCall = decodeCall,
149	doOptionMerge = doOptionMerge
150}
151
152return json_util
153