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

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.oz_client.managed_catalog
local WFOzSyncConstants = require "WFOzSyncConstants"

	-- WF.core.utils
local WFCloseSupport = require 'WFCloseSupport'
local KSPrefs = require "KSPrefs"
local KSTableUtils = require 'KSTableUtils'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'
local WFRxScheduler = require "WFRxScheduler"

	-- WF.core.os
local WFFileUtils = require 'WFFileUtils'
local WFDeviceInfo = require 'WFDeviceInfo'
local WFReachability = require 'WFReachability'
local OzCatalogUrls = require 'OzCatalogUrls'
local WFPlatformInfo = require 'WFPlatformInfo'
local WFArguments = require 'WFArguments'

	-- WF.core.diagnostic_log
local WFDiagnosticLog = require 'WFDiagnosticLog'

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

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

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

local syncStates = {
	stopped = 1,
	residual = 2,
	normal = 3,
	noBinaryUploader = 4,
	noOzCatalog = 5
}

local currentSyncState
	-- Used to keep track of the current state of the sync manager.

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

local function controlSyncActivity( catalogId, entitlement, reachability, isWiFiOnlySync, forceUpgrade, isAuthenticatedUser, 
									syncEnabled, isAppInBackground, setIsUploading, freeSpace )

	local minimumFreeSpace = 300 * 1024 * 1024
	local shouldSync = true
	local hasOzCatalog = true 
	local isResidualMode = false

	if not syncEnabled then
		shouldSync = false
	end
	
	if WFPlatformInfo.isAndroid and isAppInBackground then
		shouldSync = false
	end

	if reachability == 'unreachable' or ( reachability == 'wwan' and isWiFiOnlySync ) then
		shouldSync = false
	end

	if not WFPlatformInfo.isAndroid and derefOpt(entitlement, 'current_subs', 'sao', 'inpkg_LRMC') == "1" then
		shouldSync = false
	end

	if entitlement and ( entitlement.status == 'trial_expired' or entitlement.status == 'subscription_expired' ) then
		isResidualMode = true
	end
	
	if entitlement and entitlement.status == 'created' then
		hasOzCatalog = false
	end
	
	if catalogId == WFOzSyncConstants.kGlobalDocumentId then
		hasOzCatalog = false
	end

	if WFPlatformInfo.isAndroid and freeSpace and freeSpace < minimumFreeSpace then
		shouldSync = false
	end

	if forceUpgrade then
		shouldSync = false
	end
	
	if not isAuthenticatedUser then
		shouldSync = false
		hasOzCatalog = false
	end
	
	local isBinaryUploading = shouldSync and hasOzCatalog and not isResidualMode and setIsUploading
	
	return shouldSync, hasOzCatalog, isResidualMode, isBinaryUploading, isAppInBackground
	
end

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

local function syncClientManager( _, syncClient, shouldSync, hasOzCatalog, isResidualMode, setIsUploading, isAppInBackground )

	check( '_', 'LrmSyncClient', 'boolean', '?boolean', '?boolean', '?boolean', '?boolean' )
	
	if shouldSync then
	
		if hasOzCatalog then
		
			if isResidualMode then
			
				if currentSyncState ~= syncStates.residual then
			
					currentSyncState = syncStates.residual
				
					syncClient:stopSync()
						:concat( syncClient:startSyncInResidualMode() )
						:call( runUntilDone )
					
				end
				
			elseif setIsUploading then
			
				if currentSyncState ~= syncStates.normal then
			
					currentSyncState = syncStates.normal
				
					syncClient:stopSync()
						:concat( syncClient:startSync() )
						:call( runUntilDone )
				
				end
			
			elseif currentSyncState ~= syncStates.noUploader then
			
				currentSyncState = syncStates.noUploader
			
				syncClient:stopSync()
					:concat( syncClient:startSyncWithoutUploader() )
					:call( runUntilDone )
				
			end
			
		elseif currentSyncState ~= syncStates.noOzCatalog then
		
			currentSyncState = syncStates.noOzCatalog
		
			syncClient:stopSync()
					:concat( syncClient:startSpaceSyncWithoutOzCatalog() )
					:call( runUntilDone )
		
		end
		
	elseif currentSyncState ~= syncStates.stopped then
	
		currentSyncState = syncStates.stopped
	
		syncClient:stopSync():call( runUntilDone )
		
	end
	
	syncClient:enableBackgroundUpload( isAppInBackground ):call( runUntilDone )

end

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

local lrmSyncManagerMethods = {

	class = {
		syncClientManager = syncClientManager
	},

	_syncActivityControl = false,
	
	_isWifiOnlySync = false,
	
	_isReachable = false,
	
	_catalog = false,
	
	WFCloseSupport.addCloseSupportMethods,
	
}

--------------------------------------------------------------------------------
local function makeDiskSpaceWatchdog( catalog )

	local freeSpace = Rx.Observable.just (0)

	if WFPlatformInfo.isAndroid then

		freeSpace = Rx.Observable.interval( 60, WFRxScheduler.sharedScheduler() )

		:startWith(0)
		:flatMapLatest(function()

			return catalog

			:getDataCachePath()

			:map( function( path )

				if WFPlatformInfo.isAndroid then
					local KSPathUtils = require 'KSPathUtils'
					path = KSPathUtils.parent (path)
				end

				local fileSystemFreeSpace, _ -- fileSystemSize
				= WFFileUtils.freeSpaceAtPath( path )

				return fileSystemFreeSpace

			end )

		end)

	end

	return freeSpace

end

function lrmSyncManagerMethods:__init( args )

	local catalog = assert( args.catalog )
	
	local catalogId = Rx.Observable.just( assert( args.catalogId ) )
	local entitlement = assert( args.entitlement )
	local isReachable = assert( args.isReachable )
	local isWiFiOnlySync = assert( args.isWiFiOnlySync )
	local forceUpgrade = assert( args.forceUpgrade )
	local isAuthenticatedUser = assert( args.isAuthenticatedUser )
	local syncEnabled = assert( args.syncEnabled )
	local isAppInBackground = assert( args.isAppInBackground )
	local setIsUploading = Rx.Observable.just( true )
	local freeSpace = makeDiskSpaceWatchdog( catalog )
	
	if WFPlatformInfo.isAndroid then
		setIsUploading = assert( args.setIsUploading )
	end
	
	local syncActivityControl = Rx.Observable.combineLatest( controlSyncActivity, 
		catalogId, entitlement, isReachable, isWiFiOnlySync, forceUpgrade, isAuthenticatedUser, 
		syncEnabled, isAppInBackground, setIsUploading, freeSpace )
		:distinctUntilChanged()

	self._syncActivityControl = syncActivityControl
	self._isWifiOnlySync = isWiFiOnlySync
	self._isReachable = isReachable
	self._catalog = catalog
	currentSyncState = nil
	
	WFDiagnosticLog.addLogProvider( self )
	
end

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

function lrmSyncManagerMethods:close()
	
end

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

function lrmSyncManagerMethods:getSyncActivityControl()

	return self._syncActivityControl

end

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

function lrmSyncManagerMethods:sendDiagnosticLogData()

    local hasUserPausedSync = KSPrefs.syncPausedByUser or false

	return {

		name = "Lrm Sync Manager",
		id = 'LrmSyncManager',
		weight = 520,

		body = {
			isWifiOnlySync = self._isWifiOnlySync,
			hasUserPausedSync = hasUserPausedSync,
			uniqueDeviceId = WFDeviceInfo.uniqueDeviceId(),
			reachability = self._isReachable,
			userInfo = self._catalog:getCatUser()
						:flatMapLatest( function( user )
							return user:userInfoCache():docValue( user:userId() )
						end ),
			systemStats = WFPlatformInfo.getSystemStats(),
			migrationStats = self._catalog:docStore():localDocValue( 'migratedFromV1' )
				:map( function( docValue )
				
					if docValue._empty then
						return {
							docstoreMigratedFromV1 = false,
						}
					end
					
					docValue._id = nil
					
					return docValue
					
				end ),
			catalogAssetCount = self._catalog:indexStore()
				:queryP( 'SELECT count(docId) from coreInfo' )
				:mapDataP_new( function( row )
				
					return row and row[ 1 ] and row[ 1 ][ 1 ] or 0
					
				end )
				:runUntilDoneP(),
			catalogAlbumCount = self._catalog:indexStore()
				:queryP( 'SELECT count(docId) from albums' )
				:mapDataP_new( function( row )
				
					return row and row[ 1 ] and row[ 1 ][ 1 ] or 0
					
				end )
				:runUntilDoneP(),
			catalogPersonCount = self._catalog:indexStore()
			:queryP( 'SELECT count(docId) from persons' )
			:mapDataP_new( function( row )

				return row and row[ 1 ] and row[ 1 ][ 1 ] or 0

			end )
			:runUntilDoneP(),
			catalogFacesCount = self._catalog:indexStore()
			:queryP( 'SELECT count(docId) from faces' )
			:mapDataP_new( function( row )

				return row and row[ 1 ] and row[ 1 ][ 1 ] or 0

			end )
			:runUntilDoneP(),
		}
	}
	
end

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

return Rx.Class( 'LrmSyncManager', lrmSyncManagerMethods )
