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

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

NOTICE: All information contained herein is, and remain 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 may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

	-- RxLua
local Rx = require 'Rx'

	-- WF.core.utils
local WFCloseSupport = require 'WFCloseSupport'
local WFArguments = require 'WFArguments'
local KSTableUtils = require 'KSTableUtils'
local WFUnknown = require 'WFUnknown'

	-- WF.core.reactive
local KSPromises = require 'KSPromises'

	-- WF.core.rxlua
local WFRxPromiseBridge = require 'WFRxPromiseBridge'
local WFRxEventBridge = require 'WFRxEventBridge'

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

local check = WFArguments.check

local asKSPromise = WFRxPromiseBridge
local asKSEvent = WFRxEventBridge

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

local docStoreMethods = {
	
	_docStore = false,
		-- (WFRxDocStore) backing doc store
		
	WFCloseSupport.addCloseSupportMethods,
}

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

function docStoreMethods:__init( docStore )

	check( 'OzDocStore', 'WFRxDocStore' )
	
	self._docStore = docStore
	
--	WFCloseSupport.mustClose( self )
	
end

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

function docStoreMethods:__tostring()

	check( 'OzDocStore' )

	return string.format( "OzDocStore wrapper for %s", tostring( self._docStore ) )

end

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

function docStoreMethods:rxDocStore()

	check( 'OzDocStore' )
	
	return self._docStore
	
end

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

function docStoreMethods:path()

	return self._docStore:path()
	
end

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

function docStoreMethods:halt()

	self._docStore:halt()
	
end

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

-- @mark local documents (not synced with Oz)

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

function docStoreMethods:localStateP( name )

	check( 'OzDocStore', '+string' )

	return self._docStore:localDocValue( name )
		:pluck( 'value' )
		:call( asKSPromise )

end

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

function docStoreMethods:localStateB( name )

	check( 'OzDocStore', '+string' )

	return self._docStore:ongoingLocalDocValue( name )
		:call( asKSEvent )
		:closingHoldB( WFUnknown.value() )

end

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

function docStoreMethods:setLocalStateP( name, value )

	check( 'OzDocStore', '+string', '_' )

	return self._docStore:setLocalDocValue( {
		_id = name,
		value = value,
	} )
	:call( asKSPromise )
	
end

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

-- @mark :docValue

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

local function mapWFRxDocStoreValueToOzDocStoreValue( wfDocStoreValue )

	local result = {}
	
	for k, v in pairs( wfDocStoreValue ) do
	
		if k == '_id' then
			result._docId = v
			-- ignore
		elseif k == '_hash' then
			-- ignore
		elseif k == '_localId' then
			result._localId = v
			-- ignore
		elseif k == '_empty' then
			result._deleted = v
			-- ignore
		elseif k == '_revId' then
			result._revId = v
			-- ignore
		elseif k == 'type' then
			result._docType = v
			result.type = v
		else
			result[ k ] = v
		end
	
	end
	
	return result

end

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

local function mapOzDocStoreValueToWFRxDocStoreValue( ozDocStoreValue, docType )

	local result = {}
	
	for k, v in pairs( ozDocStoreValue ) do
	
		if k == '_docId' then
		elseif k == '_docType' then
		elseif k == '_ozMetadataUploadStatus' then
			-- Discard this. Hacky!
		elseif k == '_conflict' then
		else
			result[ k ] = v
		end
	
	end
	
	if docType then
	
		result.type = docType
		
--		if not result.subtype then
--		
--			if docType == 'asset' then
--			
--				result.subtype = 'image'
--			
--			elseif docType == 'album' then
--			
--				result.subtype = 'collection'
--				
--			elseif docType == 'album_asset' then
--			
--			elseif docType == 'space' then
--			
--				result.subtype = 'shared'
--			
--			elseif docType == 'space_album' then
--			
--			
--			elseif docType == 'gen-album' then
--			
--				
--			else
--			
--				assert( false, 'Not supported doc type' )
--			
--			end
--			
--		end
		
	end
	
	return result

end

--------------------------------------------------------------------------------
 		
function docStoreMethods:docValueOnceP( docType, docId, opts )

	check( 'OzDocStore', '+string', '+string', '?table' )

	if opts and opts.separateValues then
		error( "OzDocStoreViaWFRxDocStore doesn't support separateValues option", 2 )
	end

	return self._docStore:docValue( docId )
		:map( function( docValue )
		
			if docValue._empty or docValue._deleted then
				docValue.type = docType
			end
			
			return mapWFRxDocStoreValueToOzDocStoreValue( docValue )
			
		end )
		:call( asKSPromise )
	
end

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

function docStoreMethods:docValueCheckTypeB( docType, docId, branchRevId )

	return self:docValueB( docType, docId )
	
end

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

function docStoreMethods:docValueCheckTypeP( docType, docId, optSlotName )
	
	return self:docValueOnceP( docType, docId )
	
end

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

function docStoreMethods:docValueB( docType, docId )
	
	check( 'OzDocStore', '+string', '+string' )

	local firstTime = true

	return self._docStore:ongoingDocValue( docId )
		:map( function( wfValue )
		
			local wasFirstTime, mappedValue
			
			if wfValue then

				if not wfValue._empty then
					wasFirstTime = firstTime
					firstTime = false
				end

				mappedValue = mapWFRxDocStoreValueToOzDocStoreValue( wfValue )
				mappedValue._docType = docType
				
			end

			if wasFirstTime then
				return mappedValue, true
			else
				return mappedValue
			end
		
		end )
		
		:call( asKSEvent )
		:closingHoldB( WFUnknown.value() )
		:autoCloseRelayB()
		
end

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

function docStoreMethods:fieldValueB( docType, docId, fieldPath )

	if type( fieldPath ) ~= 'string' then
		error( "OzDocStore:fieldValueB: fieldPath is not a string", 2 )
	end
	
	return self._docStore:ongoingFieldValue( docId, fieldPath )
		:map( function( fieldValue )

			return fieldValue
			
		end )
		
		:call( asKSEvent )
		:closingHoldB( WFUnknown.value() )
		:relayB()

end

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

-- @mark :setServerValue

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

function docStoreMethods:setServerValueP( docType, docId, serverValue, options )
	
	error 'unimplemented'
	
end

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

-- @mark :resolveConflictP

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

function docStoreMethods:resolveConflictP( docType, docId, changeCounter, resolveWith )

	error 'unimplemented'

end

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

-- @mark :applyOverlay

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

function docStoreMethods:applyOverlayP( overlay )

	local strippedOverlay = KSTableUtils.shallowcopy( overlay )
	strippedOverlay.type = nil
	strippedOverlay._id = nil
	strippedOverlay._slot = nil
	
	local ozDocArgs = {
		docType = overlay.type,
		docId = overlay._id,
		
		overlay = strippedOverlay,
	}
	
	return self:applyOverlayToDocumentP( ozDocArgs )
	
end

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

function docStoreMethods:applyOverlayToDocumentP( args )
	
	check( 'OzDocStore', 'table' )
	
	local docIds = assert( args.docIds or args.docId,
		"OzDocStore:applyOverlayToDocument: missing docId or docIds argument" )

	assert( args.overlays or args.overlay,
		"OzDocStore:applyOverlayToDocument: missing overlay or overlays argument" )
	
	if type( docIds ) == 'string' then
		docIds = { docIds }
	end
	
	local overlays = { }
	
	local function overlayForIndex( i )
		return args.overlay
	end
	
	if args.overlays then
		function overlayForIndex( i )
			return args.overlays[ i ]
		end
	end
	
	for i, docId in ipairs( docIds ) do
	
		local overlay = overlayForIndex( i )
		
		assert( not overlay._replace,
			"OzDocStore: no support for _replace at top level" ) 
				
		overlays[ docId ] = overlay
		
	end
	
	local function applyOverlayToOneDocP( docId )
	
		local wfOverlay = mapOzDocStoreValueToWFRxDocStoreValue( overlays[ docId ], args.docType )
	
		wfOverlay._id = docId
	
		return self._docStore
			:applyOverlay( wfOverlay )
            :call( asKSPromise )
			:runUntilDoneP()
	
	end
	
	if #docIds > 1 then

		return KSPromises.allP( applyOverlayToOneDocP, docIds )
	
	else
	
		return applyOverlayToOneDocP( docIds[ 1 ] )
	
	end

end

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

-- @mark change notification

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

function docStoreMethods:docChangeCounterB()

	error 'unimplemented'

end

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

function docStoreMethods:docChangeCounterP()

	error 'unimplemented'

end

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

function docStoreMethods:changedDocsP( args )

	error 'unimplemented'

end

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

return Rx.Class 'OzDocStore'( docStoreMethods )