-------------------------------------------------------------------------------
-- Copyright (c) 2016 Sierra Wireless. All rights reserved.
--
-- Contributors:
--     Randall Pfeifer - initial implementation
--
--	Queue Handler
--	Implements basic queue handling, including ability to efficiently persist
--	queue to disk. Imported verbatim from AVTA.
-------------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- REQUIRES
-- ----------------------------------------------------------------------------

local persist = require 'persist'

-- ----------------------------------------------------------------------------
-- CONSTANTS
-- ----------------------------------------------------------------------------

-- ----------------------------------------------------------------------------
-- METHODS
-- ----------------------------------------------------------------------------

------------------------------------------------------------------
-- queue_push - push data into queue
-- 
-- Inputs:
--
-- Returns:
--
-- Notes
------------------------------------------------------------------
local function queue_push (queue, newdata)
	queue.data.head = queue.data.head + 1
	queue.data[queue.data.head] = newdata
end

------------------------------------------------------------------
-- queue_pop - pop data from queue
-- 
-- Inputs:
--
-- Returns:
-- 	queued data or nil if empty
--
-- Notes
------------------------------------------------------------------
local function queue_pop (queue)
	if queue.data.tail == queue.data.head then
		return nil
	end

	queue.data.tail = queue.data.tail + 1
	ret = queue.data[queue.data.tail]
	
	local clear = queue.data.tail - queue.trailsz
	if clear >= 1 then
		queue.data[clear] = nil
		queue.data.trail = clear
	end
	return ret
end

------------------------------------------------------------------
-- queue_peep - peep at next data from queue
-- 
-- Inputs:
--
-- Returns:
-- 	queued data or nil if empty
--
-- Notes
--	Peeps at next data to come off queue without actually removing it
------------------------------------------------------------------
local function queue_peep (queue)
	if queue.data.tail == queue.data.head then
		return nil
	end
	return queue.data[queue.data.tail + 1]
end

------------------------------------------------------------------
-- queue_get - get items from the queue (non-destructive)
-- 
-- Inputs:
--	n		- max number of elements to get (all if not numeric or <= 0)
--
-- Returns:
-- 	normalized array of all items currently in the queue (maybe empty)
--
-- Notes
--	peeps at entire content of queue.
------------------------------------------------------------------
local function _queue_get(queue)
	local ret = {}

	for tail = queue.data.tail+1, queue.data.head do
		table.insert(ret, queue.data[tail])
	end

	return ret
end

local function queue_get(queue, n)
	local ret = {}
	local max = queue:len()
	
	if tonumber(n) and n > 0 and max > n then
		max = n
	end

	for i = 1, max do
		table.insert(ret, queue.data[queue.data.tail + i])
	end

	return ret
end

------------------------------------------------------------------
-- queue_trail - get old items from the queue ythat have been popped (non-destructive)
-- 
-- Inputs:
--
-- Returns:
-- 	normalized array of old "trail" items (maybe empty)
--
-- Notes
--	queue can retain old popped data for debugging purposes. set
--	trailsz to > 0 to set number of deleted items to retain.
------------------------------------------------------------------
local function queue_trail(queue)
	local ret = {}

	for trail = queue.data.trail+1, queue.data.tail do
		if queue.data[trail] then
			table.insert(ret, queue.data[trail])
		end
	end

	return ret
end

------------------------------------------------------------------
-- queue_settrail - set size of debug trail (see trail)
-- 
-- Inputs:
--	new size of the trail
--
-- Returns:
--
-- Notes
--	Remove excess queue trail data if making smaller to prevent
--	retaining orphaned junk.
------------------------------------------------------------------
local function queue_settrail(queue, n)

	if n >= 0 then
		while queue.data.trail < queue.data.tail - n do
			queue.data[queue.data.trail + 1] = nil
			queue.data.trail = queue.data.trail + 1
		end
		queue.trailsz = n
	end
end

------------------------------------------------------------------
-- queue_len - get number of data points in queue
-- 
-- Inputs:
--
-- Returns:
-- 	number of pieces of data in queue
--
-- Notes
------------------------------------------------------------------
local function queue_len (queue)
	return queue.data.head - queue.data.tail
end

------------------------------------------------------------------
-- queue_save - save queue to persisted storage
-- 
-- Inputs:
-- 	persist			- name of persist file
-- 	force			- force save (always) if true
--
-- Returns:
-- 	true if data saves, else false
--
-- Notes
-- Save the data queue to NV storage if necessary. If nothing
-- has changed since last save, do nothing
-- specify force = true to force save
-- Uses persistance API
------------------------------------------------------------------
local function queue_save(queue, persistname, force)
	local save = force
--	if save then
--		print("Save: Forced")
--	end

    if queue.data.head == queue.data.tail then
    	if queue.lasthead ~= queue.lasttail then
    		-- queue became empty since last save
    		save = true
	    end
	else
		if queue.data.head ~= queue.lasthead or queue.data.tail ~= queue.lasttail then
			-- queue changed since last save
			save = true
		end
    end
	queue.lasthead = queue.data.head
	queue.lasttail = queue.data.tail
    
    if save then
        persist.save(persistname, queue.data)
	end

	return save
end

------------------------------------------------------------------
-- queue_restore - restore queue from persisted storage
-- 
-- Inputs:
-- 	persist			- name of persist file
--
-- Returns:
--
-- Notes
--	Restores data queue from persisted stoprage if it exists, else
--	sets an empty queue.
------------------------------------------------------------------
local function queue_restore(queue, persistname)
	local q = persist.load(persistname, queue.data)
	if q then
		queue.data = q
	end

	queue.lasthead = queue.data.head
	queue.lasttail = queue.data.tail
end


------------------------------------------------------------------
-- queue_new - create a new data queue
-- 
-- Inputs:
--
-- Returns:
-- 	new queue
--
-- Notes
-- 	All queue state is saved in element data. This can be streamed to
-- 	and from a file to make the queue persistent. 
------------------------------------------------------------------
local function queue_new ()
	local queue = {}
	local data = {}
	queue.push = queue_push
	queue.pop = queue_pop
	queue.peep = queue_peep
	queue.get = queue_get
	queue.trail = queue_trail
	queue.settrail = queue_settrail
	queue.len = queue_len
	queue.save = queue_save
	queue.restore = queue_restore
	queue.data = data
	queue.data.head = 1
	queue.data.tail = 1
	queue.data.trail = 1
	queue.trailsz = 0
	return queue
end

return queue_new
