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

WFCatStyleSynchronizer.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2018 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.

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

local Rx = require 'Rx'
local WFCloseSupport = require 'WFCloseSupport'
local WFRxScheduler = require 'WFRxScheduler'
local WFCatDelete = require 'WFCatDelete'
local WFCatStyleBinaryFetcher = require 'WFCatStyleBinaryFetcher'
local NmbStdProfileBinaryFetcher = require 'NmbStdProfileBinaryFetcher'
local NmbCatPreset = require 'NmbCatPreset'
local NmbStyleFavorites = require 'NmbStyleFavorites'
local WFConfiguration = require "WFConfiguration"
	-- TODO [ashj 2018-04-18]: Need to remove
	-- calling of Nmb namespace from WFCat namespace.
	-- But not sure WFCatStyleSynchronizer should be
	-- converted to a Nmb only namespace.
local NmbLocalCameraProfileStatus = require 'NmbLocalCameraProfileStatus'

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

local ENABLE_ON_DEMAND_STD_PROFILE = not not WFConfiguration.styleSync.enableOnDemandStdProfile
local STYLE_REFRESH_CALM_INTERVAL = 5 --In secs. Arbitrary value.
local CAMERA_PROFILE_REFRESH_CALM_INTERVAL = 10 -- In secs. Relatively higher value.

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

local function getStyleDeletionStream( catalog )

	return WFCatDelete.getDeletionStream()
		:where( function( success, path )
			
			-- Is there any way to identify otherthan
			-- path?
			
			return success and catalog:fileSpec():maybeStylePath( path )
		end )
end

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

local WFCatStyleSynchronizer = {
	
	_styleBinaryFetcher = false,
	
	_stdProfileBinaryFetcher = false,
	
	_styleRefreshStream = false,
	
	_cameraProfileRefreshStream = false,
	
	_cameraProfileStatus = false,
	
	_isSyncEnabledStream = false,
	
	_compositeSubscription = false,
	
	_ingestingStream = false,
	
	_cameraProfileLinkProcessor = false,
	
	WFCloseSupport.addCloseSupportMethods,
}

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

function WFCatStyleSynchronizer:__init( catalog, profileCatalog, workerThreadAgrs )

	self._styleBinaryFetcher = WFCatStyleBinaryFetcher( catalog )
	self._styleRefreshStream = Rx.Subject()
	self._cameraProfileRefreshStream = Rx.Subject()
	self._ingestingStream = Rx.ReplaySubject( 1 )
	self._isSyncEnabledStream = Rx.ReplaySubject( 1 )
	
	self._cameraProfileStatus = NmbLocalCameraProfileStatus( workerThreadAgrs )
	self._cameraProfileStatus:populateMissingProfileInfo()
	
	self._stdProfileBinaryFetcher = NmbStdProfileBinaryFetcher( catalog, profileCatalog, self._cameraProfileStatus )
	
	-- The preset refresh stream should fire when
	-- any new preset files are downloaded or
	-- anyone does an explicit call to trigger
	-- refresh
	
	local styleDownloadStreamSubscription = 
		self._styleBinaryFetcher:getRefreshStream()
			:merge( getStyleDeletionStream( catalog ) )
			:subscribe( function()
				self._styleRefreshStream:onNext()
			end )
			
	local cameraProfileStreamSubscription = 
		self._cameraProfileStatus:getCameraProfileRefreshStream()
			:merge( self._stdProfileBinaryFetcher:getRefreshStream() )
			:subscribe( function()
				self._cameraProfileRefreshStream:onNext()
			end )
	self._compositeSubscription = Rx.CompositeDisposable( styleDownloadStreamSubscription, cameraProfileStreamSubscription )
	
	-- SPAM [matthewj 2018-04-06]: This maybe should be done differently with more ownership by WFCatStyleSynchronizer
	NmbCatPreset.maybeDoProfilePresetSlurp( catalog, self )
	NmbStyleFavorites.maybeSlurpFavorites( catalog )
end

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

function WFCatStyleSynchronizer:initCameraProfileLinkProcessor( wfCatalog,
																profileCatalog )

	self._cameraProfileLinkProcessor = require 'NmbDCPDocumentProcessor'(
													wfCatalog, profileCatalog )

end

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

function WFCatStyleSynchronizer:getPresetProfileIngestInProgressStream()
	return self._ingestingStream
end

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

function WFCatStyleSynchronizer:getStyleRefreshStream()
	
	return self._styleRefreshStream
		:calm( STYLE_REFRESH_CALM_INTERVAL, WFRxScheduler.sharedScheduler() )
end

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

function WFCatStyleSynchronizer:isDownloadingStyles()
	return self._styleBinaryFetcher:isDownloading() or
		self._stdProfileBinaryFetcher:isDownloading()
end

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

function WFCatStyleSynchronizer:getCameraProfileRefreshStream()
	
	return self._cameraProfileRefreshStream
		:calm( CAMERA_PROFILE_REFRESH_CALM_INTERVAL, WFRxScheduler.sharedScheduler() )
end

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

function WFCatStyleSynchronizer:triggerStyleRefresh()
	
	self._styleRefreshStream:onNext( true )
end

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

function WFCatStyleSynchronizer:triggerCameraProfileRefresh()
	
	self._cameraProfileRefreshStream:onNext( true )
end

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

function WFCatStyleSynchronizer:makeAdjustablePriorityASAPRequestForCameraModel( cameraModelName )
	
	return self._isSyncEnabledStream
		:filter( function( isSyncEnabled )
			return isSyncEnabled
		end )
		:take( 1 )
		:flatMap( function()
		
			-- Process the camera model and add new request if needed.
			-- Return a function which will reduce the priority of
			-- the pending requests, if any.
	
			return self._stdProfileBinaryFetcher:makeAdjustablePriorityASAPRequestForCameraModel( cameraModelName )
				:combineLatest( self._styleBinaryFetcher:makeAdjustablePriorityASAPRequestForCameraModel( cameraModelName ) )
				:map( function( stdReqCancelFn, customReqCancelFn )
					-- Return cancel fn
					return function()
						if stdReqCancelFn then
							pcall( stdReqCancelFn )
						end
						
						if customReqCancelFn then
							pcall( customReqCancelFn )
						end
					end
				end )
		end )
	
end

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

function WFCatStyleSynchronizer:ensureCameraProfileExists( catalogId, href  )
	
	if not ENABLE_ON_DEMAND_STD_PROFILE then
		return Rx.Observable.just( true )
	end
	
	local isCustom = href:match( catalogId )
	if isCustom then
		return self._styleBinaryFetcher:ensureCustomCameraProfileExists( href )
	end
	
	return self._stdProfileBinaryFetcher:ensureStandardCameraProfileExists( href )
	
end

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

function WFCatStyleSynchronizer:start()
	
	self._isSyncEnabledStream:onNext( true )
	self._styleBinaryFetcher:start()
	if ENABLE_ON_DEMAND_STD_PROFILE then
		self._stdProfileBinaryFetcher:start()
	end
end

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

function WFCatStyleSynchronizer:stop()

	self._isSyncEnabledStream:onNext( false )
	self._styleBinaryFetcher:stop()
	if ENABLE_ON_DEMAND_STD_PROFILE then
		self._stdProfileBinaryFetcher:stop()
	end
end

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

function WFCatStyleSynchronizer:close()

	if self._styleRefreshStream then
		self._styleRefreshStream:dispose()
		self._styleRefreshStream = nil
	end
	
	if self._cameraProfileRefreshStream then
		self._cameraProfileRefreshStream:dispose()
		self._cameraProfileRefreshStream = nil
	end
	
	if self._cameraProfileStatus then
		self._cameraProfileStatus:close()
		self._cameraProfileStatus = nil
	end
	
	if self._ingestingStream then
		self._ingestingStream:dispose()
		self._ingestingStream = nil
	end

	if self._cameraProfileLinkProcessor then
		self._cameraProfileLinkProcessor:close()
		self._cameraProfileLinkProcessor = nil
	end
	
	if self._isSyncEnabledStream then
		self._isSyncEnabledStream:dispose()
		self._isSyncEnabledStream = nil
	end
end

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

function WFCatStyleSynchronizer:reportStatusOfVolumes( volumeStatuses )

	if self._styleBinaryFetcher then
		self._styleBinaryFetcher:reportStatusOfVolumes( volumeStatuses )
	end
	
	if self._stdProfileBinaryFetcher then
		self._stdProfileBinaryFetcher:reportStatusOfVolumes( volumeStatuses )
	end
	
end

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

return Rx.Class 'WFCatStyleSynchronizer'( WFCatStyleSynchronizer )
