--[[----------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2017 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

----------------------------------------------------------------------------]]--

--[[

	This namespace implements a simple storage mechanism for undo states.
	It can be used to save a copy of any table data, receiving a token in return
	that uniquely identifies it. This token can be used to fetch the data again
	or delete it.
	
	When the wf model is about to perform an operation that should be undoable,
	it can save a copy of whatever data it needs to reverse that change into an undo 
	state and return its token back to the client. Later, if the client later wants 
	to undo this change, it only needs to send this token back to the wf model and it
	can be used to fetch the prevous data and restore the previous state.
	
	If the client no longer needs an undo state (ex: when it reaches its undo stack 
	limit, or if a state is undone and then a new change is made), the client must
	tell the wf model to delete it.

]]--

--==============================================================================

local Rx = require 'Rx'

local Observable = Rx.Observable

local KSTableUtils = require "KSTableUtils"

--==============================================================================

local _undoStates = {}
local _currentUndoStateToken = 1

--------------------------------------------------------------------------------

local function saveUndoState( t )
	
	local token = _currentUndoStateToken
	
	if Observable:isClassOf( t ) then

		_undoStates[ token ] = t
		_currentUndoStateToken = _currentUndoStateToken + 1

		return token
		
	elseif type( t ) == "table" then

		_undoStates[ token ] = KSTableUtils.deepCopy( t )
		_currentUndoStateToken = _currentUndoStateToken + 1

		return token
	end
	
end

--------------------------------------------------------------------------------

local function getUndoState( undoStateToken )

	assert( undoStateToken, "undo state token is required" )
	local undoState = assert( _undoStates[ undoStateToken ], "no undo state found for token:", undoStateToken )				

	return undoState
end

--------------------------------------------------------------------------------

local function countKeys( t )
	local count = 0
	table.foreach( t, function( k ) count = count + 1 end )
	return count
end

--------------------------------------------------------------------------------

local function getUndoStateCount()
	return countKeys( _undoStates )
end

--------------------------------------------------------------------------------

local function deleteUndoState( undoStateToken )

	assert( undoStateToken, "undo state token is required" )
	assert( _undoStates[ undoStateToken ], "unknown undo state token" )

	_undoStates[ undoStateToken ] = nil
	
	return Observable.just( true )
end

--------------------------------------------------------------------------------

local function performUndo( undoStateToken )
	
	assert( undoStateToken, "undo state token is required" )
	local undoState = assert( _undoStates[ undoStateToken ], "no undo state found for token:", undoStateToken )	
	
	if Observable:isClassOf( undoState ) then
		return undoState
	else
		return Observable.throw( "undo state not an Observable" )
	end
end

--==============================================================================

return {
	saveUndoState = saveUndoState,
	getUndoState = getUndoState,
	getUndoStateCount = getUndoStateCount,
	deleteUndoState = deleteUndoState,
	
	performUndo = performUndo
}
