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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 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.oz_client.catalog
local OzCatalogServiceViaWFCatalog = require 'OzCatalogServiceViaWFCatalog'

	-- WF.oz_client.managed_catalog
local WFCatalog = require 'WFCatalog'
local WFOzSyncConstants = require "WFOzSyncConstants"

	-- WF.core.utils
local KSLuaUtils = require "KSLuaUtils"
local KSPathUtils = require 'KSPathUtils'
local WFArguments = require 'WFArguments'

	-- WF.lrmobile
local LrmSyncManager = require "LrmSyncManager"
local LrmLibraryModelFactory = require 'LrmLibraryModelFactory'
local LrmSyncClient = require 'LrmSyncClient'

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

local runUntilDone = require 'WFRxRunUntilDone'

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

local LrmTHLibraryLoaded, _ = KSLuaUtils.requireIfExists 'LrmTHLibrary'

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

local LriCatLibraryModel = {}

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

local currentWFCatalog

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

function LriCatLibraryModel.makeLibraryModel( catSpec, isSyncEnabled, binaryPreference, locale )

	WFArguments.check( 'table', 'boolean', 'binaryStrategy', 'string' )

	local indexSpecScript = 'LrmCatalogIndexDefinitions.lua'

	local catalogId

	local wfCatalogStream = catSpec
		:take( 1 )
		:map( function( wfCatSpec )

			catalogId = wfCatSpec:catalogId()

			local catalogOptions = {
				indexSpecScript = indexSpecScript,
				noCatalogSearch = true,
				indexStoreReadThreads = 2,
				docStoreInitProgressRequested = true,
			}

			local rootCatalogPath = KSPathUtils.getStandardFilePath 'carouselDocuments'

			local cat = WFCatalog( wfCatSpec, rootCatalogPath, catalogOptions )

			if currentWFCatalog then
				currentWFCatalog:close()
			end

			cat:getSimpleDocStoreUpdater():start()

			currentWFCatalog = cat

			return cat

		end )
		:shareReplay( 1 )


	local catalog = OzCatalogServiceViaWFCatalog( wfCatalogStream )

	local binaryDownloadStrategy = Rx.ReplaySubject( 1 )
	binaryDownloadStrategy:onNext( binaryPreference )

	local syncEnabled = Rx.ReplaySubject( 1 )
	syncEnabled:onNext( isSyncEnabled )

	local updateAcrProfileDatabase = Rx.ReplaySubject( 1 )

	local isWiFiOnlySync = Rx.ReplaySubject( 1 )
	isWiFiOnlySync:onNext( true )

	local isReachable = Rx.ReplaySubject( 1 )
	local isInLoupe = Rx.ReplaySubject( 1 )
	isInLoupe:onNext( false )

	local isAppInBackground = Rx.ReplaySubject( 1 )
	isAppInBackground:onNext( false )

	local forceUpgrade = Rx.ReplaySubject( 1 )
	forceUpgrade:onNext( false )

	local user = wfCatalogStream
		:map( function( wfCatalog )

			return wfCatalog:ozUser()

		end )
		:shareReplay( 1 )

	local entitlement = user

		:flatMap(function( wfCatUser )

			return wfCatUser:entitlement()

		end )

		:map(function( entitlement )

			-- update created accounts to enable space sync
			if entitlement and entitlement.status == 'created' then

					syncEnabled:onNext( true )
					isWiFiOnlySync:onNext( false )

			end

			return entitlement

		end )

	local hasValidConnectionToOz = user
		:flatMap( function ( wfCatUser )

			if wfCatUser:userId() == WFOzSyncConstants.kGlobalDocumentId then
				return Rx.Observable.just( false )
			end

			return wfCatUser:hasValidConnectionToOz()

		end )

	local isGeoRestrictedAccountStream = user
		:flatMap( function( wfCatUser )
			return wfCatUser:getCountryCodeForUser()
		end )
		:map( function( countryCode )
			local chinaCountryCode = 'CN'
			return countryCode == chinaCountryCode
		end )

	local purgeSubscription, syncManager
	
	local syncSubscription = wfCatalogStream

		:flatMap( function( wfCatalogObj )

			syncManager = LrmSyncManager {
					catalog = catalog,
					catalogId = wfCatalogObj:ozCatalogId(),
					isWiFiOnlySync = isWiFiOnlySync,
					isReachable = isReachable,
					forceUpgrade = forceUpgrade,
					isAuthenticatedUser = hasValidConnectionToOz,
					entitlement = entitlement,
					syncEnabled = syncEnabled,
					isAppInBackground = isAppInBackground,
					isGeoRestrictedAccountStream = isGeoRestrictedAccountStream
				}

			local syncClient = catalog:getSyncClient()

			if not syncClient then

				syncClient = LrmSyncClient( wfCatalogObj, indexSpecScript, locale, nil )
				
				syncClient:setBinaryStrategy( binaryPreference ):call( runUntilDone )
				
				catalog:setSyncClient( syncClient )

			end

			local accountIsUnderMaintenanceStream = syncClient:getOzMaintenanceNotificationStream()

				:filter( function( ozMaintenanceInfo )

					return ozMaintenanceInfo and ozMaintenanceInfo.id == 'catalog'

				end )

				:map( function( ozMaintenanceInfo )

					return ozMaintenanceInfo.isUnderMaintenance

				end )

			return Rx.Observable.combineLatest(

				function( strategy, accountIsUnderMaintenance, shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground )

					if hasOzCatalog and not isResidualMode then
						purgeSubscription = syncClient:startContinuousPurge():subscribe()
					end

					shouldSync = shouldSync and not accountIsUnderMaintenance

					local modifySyncObservable = LrmSyncManager.syncClientManager( syncClient, shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground )

					return modifySyncObservable, wfCatalogObj, strategy

				end,
				binaryDownloadStrategy,
				accountIsUnderMaintenanceStream:startWith( false ),
				syncManager:getSyncActivityControl():startWith( false ) )

				:flatMapLatest( function( modifySyncObservable, wfCatalogObj, strategy )

					return modifySyncObservable

						:merge( syncClient:setBinaryStrategy( strategy ) )

						:map( function()
							return wfCatalogObj, strategy
						end )

				end )

		end ):subscribe()

	local documentObserver

	if LrmTHLibraryLoaded then

		local docStoreFromCatalog = wfCatalogStream
			:map( function( catalog )
				return catalog:docStore()
			end )
			:shareReplay( 1 )

		documentObserver = docStoreFromCatalog
			:flatMap( function( docStore )
				return docStore:scannedFilteredMappedChangesFeed( 'LrmTHLibrary', 'scanFilterMap', 'THLibrary-scanToken' )
			end )
			:subscribe()

	end

	--------------------------------------------------------------------------------
	
	local closeAndClear		-- defined below to minimize the Git diff
	
	local result = {
		_syncSubscription = syncSubscription,
		catalog = catalog,
		_wfCatalog = currentWFCatalog,
		_wfCatalogStream = wfCatalogStream,
		_avatarServer = nil, -- lazily initialized
		_updateAcrProfileDatabase = updateAcrProfileDatabase,
		_isWiFiOnlySync = isWiFiOnlySync,
		_isReachable = isReachable,
		_isInLoupe = isInLoupe,
		_isAppInBackground = isAppInBackground,
		_documentObserver = documentObserver,
		_purgeSubscription = purgeSubscription,

		_binaryDownloadStrategy = binaryDownloadStrategy,
		_syncEnabled = syncEnabled,

		_syncManager = syncManager,

		value = Rx.ReplaySubject( 1 ),

		models = LrmLibraryModelFactory.makeLibrarySubModels(),

		events = {
			init = function()

			end,

			shutdownDatabases = function( self )

				closeAndClear(self)
				
			end,

			applyOverlayToDocument = function( parent, docType, docId, overlay )

				overlay.type = docType
				overlay._id = docId

				parent.catalog:docStore():applyOverlay( overlay )

				:call( runUntilDone )

			end,

			applyOverlay = function( parent, overlay )

				parent.catalog:docStore():applyOverlay( overlay )
					:call( runUntilDone )

			end,

			bulkApplyOverlay = function( parent, overlay )

				parent.catalog:docStore():bulkApplyOverlay( overlay )
					:call( runUntilDone )

			end,

			setSyncEnabled = function( self, isEnabled )

				self._syncEnabled:onNext( isEnabled )

			end,

		},

		close = function( self )

			closeAndClear( self )

		end
	}
	
	closeAndClear = function( self )

			if self._syncSubscription then
				self._syncSubscription:dispose()
				self._syncSubscription = nil
			end

			if self._binaryDownloaderSubscription then
				self._binaryDownloaderSubscription:dispose()
				self._binaryDownloaderSubscription = nil
			end

			if self._binaryDownloader then
				self._binaryDownloader:close()
				self._binaryDownloader = nil
			end

			if self._purgeSubscription then
				self._purgeSubscription:dispose()
				self._purgeSubscription = nil
			end
			
			if self._updateAcrProfileDatabase then
				self._updateAcrProfileDatabase:dispose()
				self._updateAcrProfileDatabase = nil
			end

			if self._isWiFiOnlySync then
				self._isWiFiOnlySync:dispose()
				self._isWiFiOnlySync = nil
			end
			
			if self._isReachable then
				self._isReachable:dispose()
				self._isReachable = nil
			end

			if self._isInLoupe then
				self._isInLoupe:dispose()
				self._isInLoupe = nil
			end

			if self._isAppInBackground then
				self._isAppInBackground:dispose()
				self._isAppInBackground = nil
			end

			if self._binaryDownloadStrategy then
				self._binaryDownloadStrategy:dispose()
				self._binaryDownloadStrategy = nil
			end

			if self._syncEnabled then
				self._syncEnabled:dispose()
				self._syncEnabled = nil
			end

			self._enableBinaryDownloader = nil

			if self._syncManager then
				self._syncManager:close()
				self._syncManager = nil
			end

			if currentWFCatalog then
				currentWFCatalog:close()
				currentWFCatalog = nil
				self._wfCatalog = nil
			end

			if self._documentObserver then
				self._documentObserver:dispose()
				self._documentObserver = nil
			end

			if self._avatarServer then
				self._avatarServer:close()
				self._avatarServer = nil
			end

			if catalog then
				catalog:close()
				catalog = nil
				self.catalog = nil
			end

			collectgarbage( "collect" )
			collectgarbage( "collect" )
			collectgarbage( "collect" )

		end
	
	return result
	
end

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

return LriCatLibraryModel
