_G._ = require 'lodash'
local GLOBAL = false
local LOCAL = true
_G.TESMODE = true

--package.path = package.path .. ";../?.lua"
local scriptPath = ''
package.path = package.path .. ";../?.lua;" .. scriptPath .. '/?.lua;../device-adapters/?.lua;./data/?.lua;./generated_scripts/?.lua'
local Time = require('Time')

local clock = os.clock
function sleep(n)  -- seconds
	local t0 = clock()
	while clock() - t0 <= n do end
end

local function keys(t)
	local keys = _.keys(t)
	return _.sortBy(keys, function(k)
		return tostring(k)
	end)
end

local function values(t)
	local values = _.values(t)
	table.sort(values)
	return values
end

describe('event helper storage', function()
	local EventHelpers, helpers, utils, domoticz

	setup(function()
		local settings = {
			['Domoticz url'] = 'http://10.0.0.8:8080',
			['Log level'] = 1
		}

		_G.TESTMODE = true

		_G.dataFolderPath = './data'
		_G.generatedScriptsFolderPath = './generated_scripts'

		_G.globalvariables = {
			Security = 'sec',
			['radix_separator'] = '.',
			['script_path'] = scriptPath,
			['dzVents_log_level'] = 1,
			['domoticz_listening_port'] = '8080',
			['currentTime'] = '2017-08-17 12:13:14.123'
		}

		domoticz = {
			['settings'] = {},
			['name'] = 'domoticz', -- used in script1
			['time'] = Time('2017-06-03 12:04:00'),
			['devices'] = function(id)
				local devs = {
					['device1'] = { name = '' },
					['onscript1'] = { name = 'onscript1', id = 1 },
					['onscript4'] = { name = 'onscript4', id = 4 },
					['on_script_5'] = { name = 'on_script_5', id = 5 },
					['wildcard'] = { name = 'wildcard', id = 6 },
					['someweirddevice'] = { name = 'someweirddevice', id = 7 },
					['mydevice'] = { name = 'mydevice', id = 8 },
					['someswitch'] = { name = 'someswitch', id = 9 },
				}
				return devs[id]
			end
		}

		EventHelpers = require('EventHelpers')
	end)

	teardown(function()
		helpers = nil
	end)

	before_each(function()
		helpers = EventHelpers(domoticz)
		utils = helpers._getUtilsInstance()
		utils.print = function() end
		os.remove('../tests/data/__data_script_data.lua')
		os.remove('../tests/data/__data_global_data.lua')
		os.remove('../tests/data/__data_internal1.lua')
	end)

	after_each(function()
		helpers = nil
		utils = nil
	end)


	it('should get a default local storage context', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]
		local context = helpers.getStorageContext(script_data.data, script_data.dataFileName)

		assert.is_same({'a','b','c','d','e','g', 'initialize'}, keys(context))
		-- note that 'h' is not here because it is nil and then it is ommited from the table
		assert.is_same('', context.a)
		assert.is_same(1, context.b)
		assert.is_same({x=1, y=2}, context.c)
		assert.is_same(666, context.g)

	end)

	it('should re-initialize a variable in a context', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]
		local context = helpers.getStorageContext(script_data.data, script_data.dataFileName)

		assert.is_same({x=1, y=2}, context.c)

		context.c = 'bla'

		assert.is_same('bla', context.c)

		context.initialize('c')

		assert.is_same({x=1, y=2}, context.c)

	end)

	it('should write a storage context', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]
		local context = helpers.getStorageContext(script_data.data, script_data.dataFileName)

		context['a'] = 'a new value'
		context['b'] = 100
		context['c'] = {x=12, y=23 }
		context['d'].setNew(100)
		context['e'].setNew(200)
		context['g'] = 22
		context['p'] = 'should not be stored'
		context['h'] = 'somestuff'

		--helpers.writeStorageContext(script_data, context, LOCAL)
		helpers.writeStorageContext(
			script_data.data,
			script_data.dataFilePath,
			script_data.dataFileName,
			context)

		local exists = utils.fileExists(script_data.dataFilePath)

		assert.is_true(exists)
		-- check if it was properly stored

		local newContext = helpers.getStorageContext(script_data.data, script_data.dataFileName)
		assert.is_same({'a','b','c', 'd', 'e', 'g', 'h', 'initialize'}, keys(newContext))
		assert.is_same('a new value', newContext.a)
		assert.is_same(100, newContext.b)
		assert.is_same({x=12, y=23}, newContext.c)
		assert.is_same(100, newContext.d.getLatest().data)
		assert.is_same(200, newContext.e.getLatest().data)
		assert.is_same(22, newContext.g)
		assert.is_same('somestuff', newContext.h)
	end)

	it('should write local storage inside the script', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]

		local res = helpers.callEventHandler(script_data,{name = 'somedevice'})

		-- should pass the arguments to the execute function
		-- and catch the results from the function
		local newContext = helpers.getStorageContext(script_data.data, script_data.dataFileName)
		assert.is_same({'a','b','c','d','e','g', 'initialize'}, keys(newContext))
		assert.is_same('this is set from script', newContext.a)
		assert.is_same(245, newContext.b)
		assert.is_same(123, newContext.d.getLatest().data)
		assert.is_same({num=456}, newContext.e.getLatest().data)
		assert.is_same(87, newContext.g)
		assert.is_same({x=10, y=20}, newContext.c)
	end)

	it('should write local storage inside the script for internal scripts', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['mySwitch'][1]

		local res = helpers.callEventHandler(script_data, { name = 'someswitch' })

		-- should pass the arguments to the execute function
		-- and catch the results from the function
		local newContext = helpers.getStorageContext(script_data.data, script_data.dataFileName)
		assert.is_same({ 'initialize', 'x' }, keys(newContext))
		assert.is_same(14, newContext.x)
	end)

	it('should have a default global context', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]
		local context = helpers.getStorageContext(helpers.globalsDefinition, '__data_global_data')

		assert.is_same({'g','h', 'initialize'}, keys(context))
		assert.is_same(666, context.g)
		assert.is_same(true, context.h)
	end)

	it('should write a global storage context', function()
		local bindings = helpers.getEventBindings()
		local context = helpers.getStorageContext(helpers.globalsDefinition, '__data_global_data')

		context['g'] = 777
		context['h'] = false
		context['d'] = 'should not be stored'

		helpers.writeStorageContext(
			helpers.globalsDefinition,
			helpers.dataFolderPath .. '/__data_global_data.lua',
			helpers.dataFolderPath .. '/__data_global_data',
			context)


		local exists = utils.fileExists('../tests/data/__data_global_data.lua')

		assert.is_true(exists)
		-- check if it was properly stored

		local newContext = helpers.getStorageContext(helpers.globalsDefinition, '__data_global_data')
		assert.is_same({'g','h', 'initialize'}, keys(newContext))
		assert.is_same(777, newContext.g)
		assert.is_same(false, newContext.h)
		assert.is_nil(newContext.d)
	end)

	it('should write global storage after running script', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]

		local res = helpers.callEventHandler(script_data,{name = 'somedevice'})

		-- should pass the arguments to the execute function
		-- and catch the results from the function
		local localContext = helpers.getStorageContext(script_data.data,script_data.dataFileName)
		local globalContext = helpers.getStorageContext(helpers.globalsDefinition, '__data_global_data')

		assert.is_same({'a','b','c','d','e','g', 'initialize'}, keys(localContext))
		assert.is_same('this is set from script', localContext.a)
		assert.is_same(245, localContext.b)
		assert.is_same(87, localContext.g)
		assert.is_same({x=10, y=20}, localContext.c)

		assert.is_same({'g', 'h', 'initialize'}, keys(globalContext))
		assert.is_same(456, globalContext.g)
		assert.is_same(false, globalContext.h)
	end)

	it('should allow you to add/remove variables from an existing context', function()
		local bindings = helpers.getEventBindings()
		local script_data = bindings['somedevice'][1]

		local def = {
			x = { initial = 1},
			y = { initial = 2},
			'a'
		}

		local context = helpers.getStorageContext(def, script_data.dataFileName)

		-- just checking
		assert.is_same({ 'initialize', 'x', 'y' }, keys(context)) -- a is not there yet as it is nill

		-- create some new data
		context['x'] = 10
		context['y'] = 20
		context['a'] = 'this is a'

		-- write the data

		helpers.writeStorageContext(def,
			script_data.dataFilePath,
			script_data.dataFileName,
			context)

		-- just checking again
		local exists = utils.fileExists(script_data.dataFilePath)

		-- now add a new var to the def

		def['z'] = { initial = 3 }

		context = helpers.getStorageContext(def, script_data.dataFileName)

		assert.is_same({ 'a', 'initialize', 'x', 'y', 'z' }, keys(context))
		assert.is_same(10, context.x) -- the updated old ones
		assert.is_same(20, context.y)
		assert.is_same('this is a', context.a) -- this is created after the first assignment of a (and a is part of the def so it may persist)
		assert.is_same(3, context.z) -- the new one with initial value


		-- now remove a var from the def and check if the old var isn't still in the
		-- file data

		def['z'] = nil

		context = helpers.getStorageContext(def, script_data.dataFileName)

		helpers.writeStorageContext(def,
			script_data.dataFilePath,
			script_data.dataFileName,
			context)

		-- require the file
		fileStorage = require(script_data.dataFileName)
		assert.is_same({ 'a', 'x', 'y'}, keys(fileStorage)) -- z is no longer there
	end)

	describe('Historical storage', function()
		local HS = require('HistoricalStorage')
		local data = {}

		local function getTime(minHours)
			local past = os.date('!*t', os.time() - minHours * 3600)
			local raw = tostring(past.year) .. '-' ..
					tostring(past.month) .. '-' ..
					tostring(past.day) .. ' ' ..
					tostring(past.hour) .. ':' ..
					tostring(past.min) .. ':' ..
					tostring(past.sec) .. '.123'

			return raw
		end

		before_each(function()
			local i
			for i=0, 9 do
				table.insert(data, {
					time = getTime(i),
					data = (10-i)
				})
			end
		end)

		after_each(function()
			data = {}
		end)

		it('should instantiate with nothing', function()
			local hs = HS()
			assert.is_same(0, hs.size)
		end)

		it('should instantiate with data', function()
			local hs = HS(data)
			assert.is_same(10, hs.size)
		end)

		it('should respect max items', function()
			local hs = HS(data,2)
			assert.is_same(2, hs.size)
		end)

		it('should respect max hours', function()
			local hs = HS(data,nil, 3)
			assert.is_same(4, hs.size)
			assert.is_same(0, hs.storage[1].time.hoursAgo)
			assert.is_same(1, hs.storage[2].time.hoursAgo)
			assert.is_same(2, hs.storage[3].time.hoursAgo)
			assert.is_same(3, hs.storage[4].time.hoursAgo)
		end)

		it('should set new data', function()
			local hs = HS(data)
			hs.add(10)
			assert.is_same(10, hs.storage[1].data)

			-- empty
			hs = HS()
			hs.add(10)
			assert.is_same(10, hs.storage[1].data)
		end)

		it('should return the new data', function()
			local hs = HS(data)
			hs.add(10)
			assert.is_same(10, hs.getLatest().data)

			-- empty
			hs = HS()
			hs.add(10)
			assert.is_same(10, hs.getLatest().data)
		end)

		it('should return stored data', function()
			local hs = HS(data)
			local item = hs.get(1)
			assert.is_same(data[1].time, item.time.raw)
			assert.is_same(data[1].data, item.data)

			item = hs.get(4)
			assert.is_same(data[4].time, item.time.raw)
			assert.is_same(data[4].data, item.data)

			-- empty
			hs = HS()
			item = hs.get(1)
			assert.is_nil(item)
		end)

		it('should get the latest', function()
			local hs = HS(data)
			local item = hs.getLatest()
			assert.is_same(data[1].time, item.time.raw)
			assert.is_same(data[1].data, item.data)

			-- empty
			hs = HS()
			item = hs.getLatest()
			assert.is_nil(item)
		end)

		it('should get the oldest', function()
			local hs = HS(data)
			local item, index = hs.getOldest()

			assert.is_same(data[10].time, item.time.raw)
			assert.is_same(data[10].data, item.data)
			assert.is_same(index, hs.size)

			-- empty
			hs = HS()
			item = hs.getOldest()
			assert.is_nil(item)
		end)

		it('should reset', function()
			local hs = HS(data)
			assert.is_same(10, hs.size)
			hs.reset()
			assert.is_same(0, hs.size)
			assert.is_nil(hs.getLatest())
			assert.is_same({}, hs._getForStorage())
		end)

		it('should return a subset', function()
			local hs = HS(data)
			local sub = hs.subset(2,4)
			local values = {}
			local count = sub.reduce(function(acc, s)
				acc = acc + 1
				return acc
			end, 0)

			sub.forEach(function(item)
				table.insert(values, item.data)
			end)

			assert.is_same(3, count)
			assert.is_same({9,8,7}, values)

			-- empty
			hs = HS()
			sub, length = hs.subset(2,4)
			assert.is_same(0, length)
		end)

		it('should return some more subsets', function()
			local hs = HS(data)

			assert.is_same(hs.storage, hs.subset(nil,nil, false))
			assert.is_same({}, hs.subset(2,1, false))
			assert.is_same({}, hs.subset(12,nil, false))
			assert.is_same(hs.storage, hs.subset(1,12, false))
		end)

		it('should return a subset since period ago', function()
			local hs = HS(data)
			local subset = hs.subsetSince('2:0:0', false)
			assert.is_same({10,9,8}, _.pluck(subset, {'data'}))

			subset = hs.subsetSince('0:59:60', false)
			assert.is_same({10,9}, _.pluck(subset, {'data'}))

			subset = hs.subsetSince('1:59:60', false)
			assert.is_same({10,9,8}, _.pluck(subset, {'data'}))

			subset = hs.subsetSince('0:59:0', nil , false)
			assert.is_same({10}, _.pluck(subset, {'data'}))

			subset = hs.subsetSince('1000:0:0', false)
			assert.is_same({10,9,8,7,6,5,4,3,2,1}, _.pluck(subset, {'data'}))

			subset = hs.subsetSince('0:0:0' , false)
			assert.is_same({10}, _.pluck(subset, {'data'}))

			hs = HS()
			local subset, length = hs.subsetSince('0:0:0', false)
			assert.is_same({}, subset)
			assert.is_same(0, length)
		end)

		it('should return data for storage', function()
			local hs = HS(data)
			hs.setNew(11)
			-- oldest value is ditched, rest is shifted, new one is number 1 now
			local newData = hs._getForStorage()
			assert.is_same(11, newData[1].data)
			assert.is_same(10, _.size(newData))
			assert.is_same(2, newData[10].data)

			hs = HS()
			assert.is_same({}, hs._getForStorage())
		end)

		it('should return all when no new value is set', function()
			local hs = HS(data)
			local newData = hs._getForStorage()
			assert.is_same(data, newData)
		end)

		it('should have iterators: forEach', function()
			local hs = HS(data)

			local sum = 0
			hs.forEach(function(item)
				sum = sum + item.data
			end)
			assert.is_same(55, sum)

			local hs = HS()
			local sum = 0
			hs.forEach(function(item)
				sum = sum + item.data
			end)
			assert.is_same(0, sum)

			-- should stop when return false
			hs = HS(data)
			sum = 0
			hs.forEach(function(item)
				sum = sum + item.data
				if (sum > 0) then return false end
			end)
			assert.is_same(10, sum)

		end)

		it('should have iterators: filter', function()
			local hs = HS(data)

			local res =	hs.filter(function(item)
				return ((item.data/2) == math.floor(item.data/2)) -- even numbers
			end)

			local sum = 0
			res.forEach(function(item)
				sum = sum + item.data
			end)
			assert.is_same(10, sum) -- all even values added

			hs = HS()
			res =	hs.filter(function(item)
				return ((item.data/2) == math.floor(item.data/2))
			end)
			sum = 0
			res.forEach(function(item)
				sum = sum + item.data
			end)
			assert.is_same(0, sum)
		end)

		it('should have iterators: reduce', function()
			local hs = HS(data)

			local sum =	hs.reduce(function(acc, item)
				return acc + item.data
			end, 0)
			assert.is_same(55, sum)

			hs = HS()
			local sum =	hs.reduce(function(acc, item)
				return acc + item.data
			end, 0)
			assert.is_same(0, sum)

		end)

		it('should have iterators: find', function()
			local hs = HS(data)

			local item, index
			item, index = hs.find(function(item, i, collection)
				return (item.data == 5)
			end)
			assert.is_same(5, item.data)

			-- inverse
			item, index = hs.find(function(item, i, collection)
				return (item.data == 2)
			end, -1)
			assert.is_same(9, index)
			assert.is_same(2, item.data)

			-- should return nil when not found
			item, index = hs.find(function(item, i, collection)
				return (item.data == 554)
			end)

			assert.is_nil(index)
			assert.is_nil(item)

			hs = HS()
			item, index = hs.find(function(item, i, collection)
				return (item.data == 5)
			end)
			assert.is_nil(item)
		end)

		it('should have reduce a filtered set', function()
			local hs = HS(data)

			local res =	hs.filter(function(item)
				return ((item.data/2) == math.floor(item.data/2))
			end)

			local sum =	res.reduce(function(acc, item)
				return acc + item.data
			end, 0)
			assert.is_same(10, sum) -- all even values added
		end)

		it('should use a valueGetter', function()
			local getter = function(item)
				return item.data.num
			end

			local hs = HS(nil, nil, nil, nil, getter)
			hs.setNew({num=10})

			assert.is_same(10, hs.sum(1,1))
		end)

		it('valueGetter should return nil', function()
			local getter = function(item)
				return 'bla' -- a string and not a number, should trigger error
			end

			local hs = HS(nil, nil, nil, nil, getter)
			hs.setNew({num=10})

			assert.is_nil(hs.sum(1,1))

			hs = HS(nil, nil, nil, nil, 'bla') -- not a function
			hs.setNew({num=10})

			assert.is_nil(hs.sum(1,1))

			hs = HS(nil, nil, nil, nil, function() return nil..1 end) -- error
			hs.setNew({num=10})

			assert.is_nil(hs.sum(1,1))
		end)

		it('should return the average', function()
			local hs = HS(data)
			assert.is_same(5.5, hs.avg(1,10))

			assert.is_same(10, hs.avg(1,1))
			assert.is_same(6, hs.avg(3,7))

			hs = HS()
			assert.is_same(0, hs.avg(1,10))

			assert.is_same(0, hs.avg())
		end)

		it('should return average over a time period', function()
			local hs = HS(data)
			local avg = hs.avgSince('2:0:0')
			assert.is_same(9, avg) -- 10,9,8

			hs = HS()
			assert.is_same(0, hs.avgSince('0:10:1'))
		end)

		it('should return the minimum value of a range', function()
			data[5].data = -20
			local hs = HS(data)
			local min, item = hs.min()
			assert.is_same(-20,min)
			assert.is_same(hs.storage[5], item)

			min, item = hs.min(1, 4)
			assert.is_same(7,min) -- 10,9,8,7
			assert.is_same(hs.storage[4], item)

			hs = HS()
			assert.is_nil(hs.min(1, 4))
		end)

		it('should return the minimum value over a period', function()
			data[4].data = -20
			local hs = HS(data)
			local min = hs.minSince('4:0:0') -- since 2 hours
			assert.is_same(-20,min)

			min = hs.minSince('0:120:0') -- since 3 hours
			assert.is_same(8,min)

			hs = HS()
			assert.is_nil(hs.minSince('0:4:1'))

		end)

		it('should return the closest (local) minimum ', function()
			data[5].data = -20
			data[3].data = -10
			data[4].data = -9
			local hs = HS(data)
			local min, item = hs.localMin()
			assert.is_same(-10,min)
			assert.is_same(hs.storage[3], item)

			local min, item = hs.localMin(1)  -- 9.5, 3, -3.333, -13, -8
			assert.is_same(-13,min)
			assert.is_same(hs.storage[4], item)
		end)

		it('should return the closest (local) maximum', function()
			local hs = HS(data)
			local max, item = hs.localMax()
			assert.is_same(10,max)
			assert.is_same(hs.storage[1], item)
			data[1].data = 1 -- sm(1): 1.5
			data[2].data = 2 -- 2
			data[3].data = 3 -- 4.5 <-- smoothed max
			data[4].data = 4 -- 3.5
			data[5].data = 0 -- 4.5
			data[6].data = 5 -- 5

			hs = HS(data)
			local max, item = hs.localMax()
			assert.is_same(4,max)
			assert.is_same(hs.storage[4], item)

			max, item = hs.localMax(1)
			assert.is_same(3, max)
			assert.is_same(hs.storage[3], item)
		end)

		it('should return the maximum value of a range', function()
			data[5].data = 20
			local hs = HS(data)
			local min = hs.max()
			assert.is_same(20,min)

			min = hs.max(1, 4)
			assert.is_same(10,min) -- 10,9,8,7

			hs = HS()
			assert.is_nil(hs.max())
		end)

		it('should return the maximum value over a period', function()
			data[5].data = 20
			local hs = HS(data)
			local max = hs.maxSince('3:60:0')
			assert.is_same(20,max)

			hs = HS()
			assert.is_nil(hs.maxSince(''))
		end)

		it('should return the sum of a range', function()
			local hs = HS(data)
			local sum = hs.sum()
			assert.is_same(55,sum)

			sum = hs.sum(1, 4)
			assert.is_same(34,sum) -- 10,9,8,7
		end)

		it('should return 0 as the sum over an empty set', function()
			local hs = HS()
			local sum = hs.sum()
			assert.is_same(0, sum)
		end)

		it('should return the sum over a period', function()
			data[5].data = 20
			local hs = HS(data)
			local sum = hs.sumSince('3:60:0') -- since 2 hours
			assert.is_same(54,sum)

			hs = HS()
			assert.is_same(0, hs.sumSince('0:0:1'))
		end)

		it('should smooth an item with its neighbours', function()
			local hs = HS(data)
			local avged = hs.smoothItem(5, 3)
			assert.is_same(6, avged)

			avged = hs.smoothItem(2, 2)
			assert.is_same(8.5, avged)

			avged = hs.smoothItem(9, 2)
			assert.is_same(2.5, avged)

			avged = hs.smoothItem(1, 0)
			assert.is_same(10, avged)

			avged = hs.smoothItem(1000, 0)
			assert.is_nil(avged)

			avged = hs.smoothItem(1, 2)
			assert.is_same(9, avged)

			hs = HS()
			assert.is_nil(hs.smoothItem(1,1))
		end)

		it('should return the delta value', function()
			local hs = HS(data)
			hs.setNew(20)
			-- 20[1] 10[2], 9[3], 8[4], 7[5], 6[6], 5[7], 4[8], 3[9], 2[10] dropped: 1

			local nosmooth = hs.delta(1, 6)  -- 6 > 20 = 14
			assert.is_same(14, nosmooth)
			local smooth = hs.delta(1, 10, 2)  -- 3 > 13  = 10
			assert.is_same(10, smooth)

			smooth = hs.delta(2, 10, 2)  -- 3 > 11.75  = 8.75
			assert.is_same(8.75, smooth)

			-- check against an empty storage
			hs = HS()
			assert.is_nil(hs.delta(1, 4))
			assert.is_same(100, hs.delta(1, 4, nil, 100))

		end)

		it('should return a delta value since a specifc time without start-smoothing', function()
			local hs = HS(data)
			hs.setNew(20)
			-- 20[1] 10[2], 9[3], 8[4], 7[5], 6[6], 5[7], 4[8], 3[9], 2[10] dropped: 1

			local nosmooth, refFrom, refTo = hs.delta2(1, 6)  -- 6 > 20 = 14
			assert.is_same(14, nosmooth)
			assert.is_same(refFrom, 20)
			assert.is_same(refTo, 6)

			local smooth, refFrom, refTo = hs.delta2(1, 10, 0, 2)  -- 3 > 20  = 17
			assert.is_same(17, smooth)
			assert.is_same(refFrom, 20)
			assert.is_same(refTo, 3)

			smooth, refFrom, refTo = hs.delta2(2, 10, 0, 2)  -- 3 > 10  = 7
			assert.is_same(7, smooth)
			assert.is_same(refFrom, 10)
			assert.is_same(refTo, 3)

			smooth, refFrom, refTo = hs.delta2(2, 10, 2, 2)  -- 3 > 11.75  = 8.75
			assert.is_same(8.75, smooth)
			assert.is_same(refFrom, 11.75)
			assert.is_same(refTo, 3)

			smooth, refFrom, refTo = hs.delta2(2, 10, 2, 2)  -- 3 > 11.75  = 8.75
			assert.is_same(8.75, smooth)
			assert.is_same(refFrom, 11.75)
			assert.is_same(refTo, 3)

			-- check against an empty storage
			hs = HS()
			assert.is_nil(hs.delta2(1, 4))
			assert.is_same(100, hs.delta2(1, 4, 0, 0, 100))
		end)

		it('should return a delta value since a specifc time', function()
			local hs = HS(data)
			local smooth, refFrom, refTo = hs.deltaSince('5:0:0', 2)
			assert.is_same(4, smooth)
			assert.is_same(refFrom, 9)
			assert.is_same(refTo, 5)

			smooth, refFrom, refTo = hs.deltaSince('15:0:0', 2, 22)
			-- beyond the limits, return default value (22)
			assert.is_same(22, smooth)
			assert.is_same(refFrom, 22)
			assert.is_same(refTo, 22)
		end)

		it('should return a delta value since a specifc time or oldest', function()
			local hs = HS(data)
			-- 10[1], 9[2], 8[3], 7[4], 6[5], 5[6], 4[7], 3[8], 2[9], 1[10]

			local smooth, refFrom, refTo = hs.deltaSinceOrOldest('5:0:0', 2, 2) -- 5[6] > 10[1] (smoothed: 5 > 9)
			assert.is_same(4, smooth)
			assert.is_same(refFrom, 9)
			assert.is_same(refTo, 5)

			smooth, refFrom, refTo = hs.deltaSinceOrOldest('15:0:0')
			-- beyond the limits, use oldest value
			assert.is_same(9, smooth) -- 1 > 10
			assert.is_same(refFrom, 10)
			assert.is_same(refTo, 1)

			hs = HS()
			assert.is_same(33, hs.deltaSinceOrOldest('15:0:0', nil, nil, 33)) -- empty set
		end)


		it('should return an item at a specific time', function()
			local hs = HS(data)
			local item, index = hs.getAtTime('2:2:0')

			assert.is_same(3, index)
			assert.is_same(7200, item.time.secondsAgo)

			item, index = hs.getAtTime('2:30:0')
			assert.is_same(4, index)
			assert.is_same(10800, item.time.secondsAgo)

			item, index = hs.getAtTime('1:89:60')
			assert.is_same(4, index)
			assert.is_same(10800, item.time.secondsAgo)

			item, index = hs.getAtTime('0:0:-1')
			assert.is_same(1, index)
			assert.is_same(0, item.time.secondsAgo)

			item, index = hs.getAtTime('1200:0:0')
			assert.is_nil(index)
			assert.is_nil(item)

			hs = HS()
			assert.is_nil(hs.getAtTime('1:89:60'))
		end)

		it('should allow you to add strings to the set', function()
			local hs = HS()
			hs.setNew('some value')
			hs.setNew('some value2')
			assert.is_same('some value2', hs.get(1).data)
			assert.is_same('some value', hs.get(2).data)
			assert.is_same(2, hs.size)
		end)

		it('It should do fifo', function()
			local hs = HS(nil, 4)
			hs.setNew('a')
			hs.setNew('b')
			hs.setNew('c')
			hs.setNew('d')
			assert.is_same({'d','c','b', 'a'} , _.pluck(hs.storage, {'data'}))
			hs.setNew('e')
			assert.is_same({'e','d','c','b'} , _.pluck(hs.storage, {'data'}))
		end)
	end)

end)
