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

WFCatStacksProcessor.lua

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

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.

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

	-- Rx
local Rx = require 'Rx'
local Observable = Rx.Observable

local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFRxDocValueWatcher = require 'WFRxDocValueWatcher'
local WFCloseSupport = require 'WFCloseSupport'
local WFLogger = require 'WFLogger'

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

local logger = WFLogger( 'WFCatStacksProcessor' )

local check = assert( WFArguments.check )
local derefOpt = assert( KSTableUtils.derefOpt )

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

local WFCatStacksProcessor = {
	
	_outOfDateStacksSubscription = false,
	
	WFCloseSupport.addCloseSupportMethods,

}

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

local function stackCoverAssetIdStream( indexStore )
	return indexStore:query {
			sql = [[SELECT stackId, ozCoverId FROM stacks INNER JOIN assets ON stacks.ozCoverId = assets.assetId]],
		}
		:map( function( rows )
			local stackIdsByCoverId = {}
			local coverIds = {}
			for _, row in ipairs( rows ) do
				local stackId = row[ 1 ]
				local coverId = row[ 2 ]
				local maybeStacksWithCover = stackIdsByCoverId[ coverId ]
				if maybeStacksWithCover then
					table.insert( maybeStacksWithCover, stackId )
					stackIdsByCoverId[ coverId ] = maybeStacksWithCover
				else
					stackIdsByCoverId[ coverId ] = { stackId }
					table.insert( coverIds, coverId )
				end
			end
			return coverIds, stackIdsByCoverId
		end )
end

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

function WFCatStacksProcessor:__init( docStore, indexStore )
	
	check( 'WFCatStacksProcessor', 'WFRxDocStore', 'WFRxIndexStore' )
	
	local stackIdsByCoverId
	
	self._outOfDateStacksSubscription = stackCoverAssetIdStream( indexStore )
		:map( function( coverIds, stacksByCover )
			stackIdsByCoverId = stacksByCover
			return coverIds
		end )
		:call( WFRxDocValueWatcher.make, docStore )
		:flatMap( function( updatedCoverDoc )
			
			-- get doc id and revision id
			local docId = updatedCoverDoc._id
			local revisionId = updatedCoverDoc._revId
			
			if not docId then
				return Observable.just()
			end
			
			-- get stacks this is a cover for
			local maybeStackIds = stackIdsByCoverId[ docId ]
			if not maybeStackIds then
				return Observable.just()
			end
			
			return docStore:bulkDocValue( maybeStackIds )
				:map( function( stackDocuments )
					local overlays = {}
					
					for _, stackDoc in ipairs( stackDocuments ) do
						
						updatedCoverDoc._replace = true -- this is so the cover doc is not merged into the existing cover
						local stackCoverId = derefOpt( stackDoc, 'cover', 'id' )
						local stackCoverRevisionId = derefOpt( stackDoc, '_localOnly', 'cover', '_revId' )
						
						if stackCoverId == docId and
							stackCoverRevisionId ~= revisionId then
							-- check whether stack has the latest revision of the correct doc in it
							-- if not, create overlay to splat into stack
							local overlay = { 
								_id = stackDoc._id,
								_localOnly = {
									cover = updatedCoverDoc,
								},
							}
							
							logger:debugf( "updating stack %s cover to %s, revision %s", stackDoc._id, stackCoverId, stackCoverRevisionId )
							
							table.insert( overlays, overlay )
						end
					end
					
					return overlays				
				end )
				:flatMap( function( stackCoverOverlays )
					if next( stackCoverOverlays ) then
						return docStore:bulkApplyOverlay( stackCoverOverlays )
					else
						return Observable.just()
					end
				end )
		end )
		:subscribe( function( result )
			logger:debugf( "WFCatStacksProcessor:onNext called %s", tostring( result ) )
		end,
		function( errorMsg )
			logger:debugf( "WFCatStacksProcessor:onError called %s", errorMsg )
		end,
		function()
			logger:debugf( "WFCatStacksProcessor:onComplete called" )
		end )
	
end

function WFCatStacksProcessor:close()
	
	if self._outOfDateStacksSubscription then
		self._outOfDateStacksSubscription:dispose()
		self._outOfDateStacksSubscription = nil
	end

end

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

return Rx.Class 'WFCatStacksProcessor'( WFCatStacksProcessor )