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

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 WFOzRenditionServer = require "WFOzRenditionServer"
local OzCatalogServiceViaWFCatalog = require 'OzCatalogServiceViaWFCatalog'

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

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

	-- WF.core.os
local WFFileUtils = require "WFFileUtils"


	-- 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
local currentRenditionServer

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

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,
--				docStorePath = '',
				noFileAccessDb = true,
				noCatalogSearch = true,
				indexStoreReadThreads = 2,
				docStoreInitProgressRequested = true,
			}
			
			local rootCatalogPath = KSPathUtils.getStandardFilePath 'carouselDocuments'
	
			local cat = WFCatalog( wfCatSpec, rootCatalogPath, catalogOptions )
			
			if currentWFCatalog then
				currentWFCatalog:close()
			end
			
			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 renditionServer = Rx.Observable.combineLatest( function( wfCatalog, dataCache )
	
			local ozRenditionServer = WFOzRenditionServer( catalog,
										wfCatalog:ozUser(),
										wfCatalog:ozCatalogId(),
										dataCache, wfCatalog )
					
			currentRenditionServer = ozRenditionServer
			
			return ozRenditionServer	
			
		end, wfCatalogStream, catalog:getDataCache() )
	
		:shareReplay( 1 )

	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 )
	
	-- INFO [pkamin 2014-09-22]: do not backup the previewCache as it can also become very large
	-- INFO [jrechtie 2017-06-27]: Freemium case: we want to backup the previewCache, because renditions can't be recreated.

	Rx.Observable.combineLatest( function ( path, wfCatUser )

		if wfCatUser:userId() == WFOzSyncConstants.kGlobalDocumentId then
			-- old installations up to release 2.7.4 might have the backup flag disabled
			-- explicitly re-enable it here for the Freemium case.
				WFFileUtils.removeDoNotBackUpFlag( path )
				WFFileUtils.removeDoNotBackUpFlag( path .. '-shm' )
				WFFileUtils.removeDoNotBackUpFlag( path .. '-wal' )
			else
				WFFileUtils.setDoNotBackUpFlag( path )
				WFFileUtils.setDoNotBackUpFlag( path .. '-shm' )
				WFFileUtils.setDoNotBackUpFlag( path .. '-wal' )
			end
			return path
		end,
		catalog:getDataCachePath(), user )
		:take(1)
		:call( runUntilDone )
	
	local hasValidConnectionToOz = user
		:flatMap( function ( wfCatUser )
		
			if wfCatUser:userId() == WFOzSyncConstants.kGlobalDocumentId then
				return Rx.Observable.just( false )
			end
			
			return wfCatUser:hasValidConnectionToOz()
		
		end )

	local syncManager = LrmSyncManager {
		catalog = catalog,
		catalogId = catalogId,
		isWiFiOnlySync = isWiFiOnlySync,
		isReachable = isReachable,
		forceUpgrade = forceUpgrade,
		isAuthenticatedUser = hasValidConnectionToOz,
		entitlement = entitlement,
		syncEnabled = syncEnabled,
		isAppInBackground = isAppInBackground,
	}
	
	local purgeSubscription

	local syncSubscription = wfCatalogStream
	
		:flatMap( function( wfCatalogObj )
		
			local syncClient = catalog:getSyncClient()
			
			if not syncClient then
				
				syncClient = LrmSyncClient( wfCatalogObj, indexSpecScript, locale, nil )
				
				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 )

					purgeSubscription = syncClient:startContinuousPurge( strategy ):subscribe()

					shouldSync = shouldSync and not accountIsUnderMaintenance

					require 'LrmSyncManager'.syncClientManager( syncClient, shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground )
					
					return wfCatalogObj, strategy
				
				end,
				binaryDownloadStrategy,
				accountIsUnderMaintenanceStream:startWith( false ),
				syncManager:getSyncActivityControl():startWith( false ) )
		
		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

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

	return {
		_syncSubscription = syncSubscription,
		catalog = catalog,
		_wfCatalog = currentWFCatalog,
		_wfCatalogStream = wfCatalogStream,
		_renditionServer = renditionServer,
		_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 )
			
			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 )
			
			if self._syncSubscription then
				self._syncSubscription:dispose()
			end
			
			if self._binaryDownloaderSubscription then
				self._binaryDownloaderSubscription:dispose()
			end
			
			if self._binaryDownloader then
				self._binaryDownloader:close()
			end
			
			if self._purgeSubscription then
				self._purgeSubscription:dispose()
			end
				
			self._updateAcrProfileDatabase:dispose()
			
			self._isWiFiOnlySync:dispose()
			self._isReachable:dispose()
			self._isInLoupe:dispose()
			self._isAppInBackground:dispose()
			
			self._binaryDownloadStrategy:dispose()
			self._syncEnabled:dispose()
			
			self._binaryDownloader = nil
			self._updateAcrProfileDatabase = nil
			self._enableBinaryDownloader = nil
			self._isWiFiOnlySync = nil
			self._isReachable = nil
			self._isInLoupe = nil
			self._isAppInBackground = nil
			self._binaryDownloadStrategy = nil
			
			if self._syncManager then
				self._syncManager:close()
			end
			
			if currentWFCatalog then
				currentWFCatalog:close()
				currentWFCatalog = nil
				self._wfCatalog = nil
			end
			
			if currentRenditionServer then
				currentRenditionServer:close()
				currentRenditionServer = nil
			end

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

			self._syncManager = nil
			
			if self._avatarServer then
				self._avatarServer:close()
				self._avatarServer = nil
			end
						
			catalog:close()
			
			collectgarbage( "collect" )
			collectgarbage( "collect" )
			collectgarbage( "collect" )
			
		end
	}
end

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

return LriCatLibraryModel
