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

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.core.utils
local WFCloseSupport = require 'WFCloseSupport'
local WFArguments = require 'WFArguments'
local KSTableUtils = require 'KSTableUtils'
local WFDeviceInfo = require "WFDeviceInfo"
local WFAnalytics = require "WFAnalytics"
local WFCapabilities = require 'WFCapabilities'
local WFProgressReporter = require 'WFProgressReporter'
local WFRxThrottledBatchedApplyOverlayToIds = require 'WFRxThrottledBatchedApplyOverlayToIds'

	-- WF.core.rxlua
local WFRxRunUntilDone = require 'WFRxRunUntilDone'
local WFRxScheduler = require 'WFRxScheduler'

	-- WF.core.lua.threading
local WFRequestDispatcher = require "WFRequestDispatcher"

	-- WF.oz_client.managed_catalog
local WFCatConflictResolver = require 'WFCatConflictResolver'
local WFCatErrorHandler = require 'WFCatErrorHandler'
local WFCatUserSynchronizer = require 'WFCatUserSynchronizer'
local WFCatStorage = require 'WFCatStorage'
local WFCatStuckSyncUtils = require 'WFCatStuckSyncUtils'
local WFCatSyncClientBase = require 'WFCatSyncClientBase'
local WFCatOzMaintenanceTracker = require 'WFCatOzMaintenanceTracker'

	-- WF.oz_client.lrmobile
local WFCatBinaryUploader = require 'WFCatBinaryUploader'
local WFOzSyncConstants = require 'WFOzSyncConstants'
local OzSyncMetadataMapper = require 'OzSyncMetadataMapper'
local LrmCatSyncStatus = require 'LrmCatSyncStatus'
local LrmCatXmpDevelop = require 'LrmCatXmpDevelop'

	-- WF.oz_client.doc_store

local OzCatalogServiceViaWFCatalog = require "OzCatalogServiceViaWFCatalog"

        -- WF.sqlite.doc_store
local WFRxDocStoreMessageProcessor = require "WFRxDocStoreMessageProcessor"

	-- WF trash
local WFCatTrash = require 'WFCatTrash'

local LrmConfig = require "LrmConfig"

local LrmOzPurgeAgent = require "LrmOzPurgeAgent"

local LrmCatAlbum = require "LrmCatAlbum"

local WFLogger = require 'WFLogger'

local logger = WFLogger( 'LrmSyncClientImp' )

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

LrmConfig.registerBinaryStrategyCheck()

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

local Observable = Rx.Observable

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

local function trackDocStoreSyncInitError()

    local errorEvent = {
        [ 'event.workflow' ] = 'LAUNCH',
        [ 'event.subcategory' ] = 'SyncClient:init',
        [ 'event.error_type' ] = 'DocStore:SchemaConflict'
    }

    WFAnalytics.trackEvent( errorEvent )

end

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

local function trackSha256MasterUploadError( puller, docValue )

	if docValue then

		local docType = derefOpt( docValue, 'type' )

		if docType == 'asset' then

			local hasSha256Error = derefOpt( docValue, 'importSource', 'status', 'error', 'all', 'reason', 'sha256' ) == "does not match"

			if hasSha256Error then

				local uniqueDeviceId = derefOpt( docValue, 'importSource', 'uniqueDeviceId' )

				if uniqueDeviceId and ( uniqueDeviceId == WFDeviceInfo.uniqueDeviceId() ) then

					local errorEvent = {}

					errorEvent['event.type'] = 'error'
					errorEvent['event.workflow'] = 'SYNC'
					errorEvent['event.subcategory'] = 'error_handler'
					errorEvent['event.error_type'] = 'Sha256MismatchError'

					errorEvent['content.id'] = docValue._id
					errorEvent['content.type'] = derefOpt( docValue, 'subtype' )
					errorEvent['content.mimetype'] = derefOpt( docValue, 'importSource', 'contentType' )
					errorEvent['content.size'] = derefOpt( docValue, 'importSource', 'fileSize' )
					errorEvent['event.error_desc'] = derefOpt( docValue, 'importSource', 'sha256' )

					WFAnalytics.trackEvent( errorEvent )

				end

			end

		end

	end


end

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

local function albumAssetsForFilteredAssetsQueryFn( catalog, assetIds )

	return catalog:indexStore():queryOnce {
				sql = [[SELECT album_asset.docId, album_asset.assetId FROM album_asset WHERE album_asset.assetId IN CARRAY(?, ?, 'char*') ]],

				assetIds,
				#assetIds
		}

end

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

local function deleteAssets( catalog, assetIds )

	local overlay = {
		docType = 'asset',
		_deleted = true
	}

	return WFRxThrottledBatchedApplyOverlayToIds( "deleteAssets", catalog:docStore(), overlay, assetIds, 100, 10 )
		:call( runUntilDone )

end
--==============================================================================

-- @mark LrmSyncClientImp namespace

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

local syncClientImpMethods = {

	_syncClientBase = false,
		-- (WFCatSyncClientBase)

	_userId = false,
		--(string)

	_storageMonitor = false,
		-- (WFCatStorage)

	_binaryUploader = false,
		-- (WFCatBinaryUploader)

	_progressReporter = false,
		-- (WFProgressReporter) Reports back processed and total bytes of files
		-- currently being uploaded.

	_purgeAgent = false,
		-- (LrmOzPurgeAgent)

	_errorHandler = false,
		-- (WFCatErrorHandler)

	_userSynchronizer = false,
		-- (WFCatUserSynchronizer)

	_conflictResolver = false,
		-- (WFCatConflictResolver)

	_catalogServiceViaWFCatalog = false,
		-- (OzCatalogServiceViaWFCatalog)

	_checkAutoPurgeSubscription = false,
		-- (Rx Disposable)

	_cloudTrashAutoPurge = false,
		-- (Rx Disposable)

	_checkOzForCatalogUpdatesSubscription = false,
		-- (Disposable) A subscription that monitors for binary upload completions and triggers
		-- the puller to check for Oz catalog updates.

	_ozMaintenanceNotificationStream = false,
		-- (Rx Subject) A stream that fires with Oz outage/maintenance updates for both the
		-- users own account and all shared-with-you spaces.

	_isNormalSyncRunningStream = false,
		-- (BehaviorSubject) A stream that fires true when sync is running, false otherwise.
		-- Note that this only fires true when sync is running it's normal mode; not in
		-- residual mode, when paused, or when there's no Oz catalog.

	_docsStuckSyncingMetadataSubscription = false,
		-- (Rx Disposable) A subscription that monitors and reports metadata related stuck syncs.

	_docsStuckUploadingOriginalSubscription = false,
		-- (Rx Disposable) A subscription that monitors and reports uploader related stuck syncs.

	attachMessageHandlers = false,

	WFCloseSupport.addCloseSupportMethods,

}

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

function syncClientImpMethods:__init( options )

	check( 'LrmSyncClientImp', 'table' )

	-- Additional options for WFCatSyncClientBase

	options.noFileAccessDb = true
	options.noCatalogSearch = true
	options.pullerPrefetch = true
	options.pusherDelayBeforeSendingUpdates = 3
	options.albumAssetsForFilteredAssetsQueryFn = albumAssetsForFilteredAssetsQueryFn

	local syncClientBase = WFCatSyncClientBase( options )

	local catalog = syncClientBase:getCatalog()

	-- ConflictResolver

	local conflictResolver = WFCatConflictResolver( catalog:docStore(), catalog:indexStore() )

	-- Error Handler

	local errorHandler = WFCatErrorHandler( catalog, {
			externalizeSettings = LrmCatXmpDevelop.externalizeDevelopSettings,
			deleteAssets = deleteAssets,
			deleteIndividualAlbum = LrmCatAlbum.deleteAlbum,
			getSyncStatusOfAssetsOfAlbumAssets = LrmCatSyncStatus.getSyncStatusOfAssetsOfAlbumAssets,
			getSyncStatusOfAlbumsOfAlbumAssets = LrmCatSyncStatus.getSyncStatusOfAlbumsOfAlbumAssets,
			getSyncStatusOfParentCollectionOfAlbums = LrmCatSyncStatus.getSyncStatusOfParentCollectionOfAlbums,
			getSyncStatusOfAssetsOfVersions = LrmCatSyncStatus.getSyncStatusOfAssetsOfVersions,
			getSyncStatusOfAssetsOfComments = LrmCatSyncStatus.getSyncStatusOfAssetsOfComments,
			getSyncStatusOfAssetsOfFavorites = LrmCatSyncStatus.getSyncStatusOfAssetsOfFavorites,
		} )

	-- Custom doc handling

	local catalogPuller = syncClientBase:getCatalogPuller()

	catalogPuller:addAssetTypeHandler( 'asset', trackSha256MasterUploadError )

	local catalogPusher = syncClientBase:getCatalogPusher()

	self._userId = assert( options.userId )

	local function remapAssetDocForUpdate( _, localDoc, payload )

		OzSyncMetadataMapper.remapGlobalDocumentId( self._userId, payload )

	end

	if catalogPusher then
		catalogPusher:addUpdateDocRemapFunction( 'asset', remapAssetDocForUpdate )
	end

	-- Cloud Trash Auto-Purge

	local isAuthenticatedUser = self._userId ~= WFOzSyncConstants.kGlobalDocumentId

	local catalogId = assert( options.catalogId )

	local user = catalog:ozUser()

	local hasValidConnectionToOz = (isAuthenticatedUser and
									catalogId ~= WFOzSyncConstants.kGlobalDocumentId) and	-- CatalogId check is needed for Created users as they have valid connection to Oz but no catalog on Oz.
									user:hasValidConnectionToOz() or
									Rx.Observable.just( false )

	self._checkAutoPurgeSubscription = Rx.Observable.combineLatest(

			function( isSubscriptionExpired, hasValidConnectionToOz )

				if isSubscriptionExpired or not hasValidConnectionToOz then
					return true
				end

				return false

			end,
			user:isSubscriptionExpired(), hasValidConnectionToOz
		)
		:distinctUntilChanged()
		:subscribe( function( shouldAutoPurge )

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

			if shouldAutoPurge then
				self._cloudTrashAutoPurge = WFCatTrash.autoPurge( catalog ):subscribe()
			end

		end )

	self._isNormalSyncRunningStream = Rx.BehaviorSubject( false )

	-- User Synchronizer

	local userSynchronizer = WFCatUserSynchronizer( user )

	-- Binary Uploader

	local ozCatalog = OzCatalogServiceViaWFCatalog( Rx.Observable.just( catalog ) )

	local progressReporter = WFProgressReporter( "Binary Upload Progress Report" )
	self._progressReporter = progressReporter
	
	local uploadOptions = {
		deriveCatalogBasePath = true,
		progressReporter = progressReporter,
		useDirectUploadIfPossible = WFCapabilities.getCapability( "WFCatBinaryUploader.enableDirectUpload" ) ~= "false",
		enableVideoProxies = WFCapabilities.getCapability( "WFCatBinaryUploader.enableVideoProxies" ) == true
	}

	local catSpec = syncClientBase:getCatSpec()

	-- Binary Uploader
	local binaryUploader = WFCatBinaryUploader( catalog, nil, nil, catSpec, uploadOptions )

	-- WF-357: Reset Catalog MetadataPuller polling frequency after uploading a binary.

	self._checkOzForCatalogUpdatesSubscription = binaryUploader:binaryUploadCompletionStream()
		:subscribe( function()
			syncClientBase:checkOzForUpdates()
		end )

	-- Quota Monitoring

	local storageMonitor = WFCatStorage( catalog )

	if catalogPusher then

		-- monitor quota

		local quotaMonitorSubscription = storageMonitor:isOverStorage()
			:distinctUntilChanged()
			:subscribe( function( isOverStorage )
				catalogPusher:setUserIsOverQuota( isOverStorage )
			end )

		-- monitor expiration

		local userExpiredSubscription = user:isSubscriptionExpired()
			:distinctUntilChanged()
			:subscribe( function( isExpired )
				catalogPusher:setUserIsExpired( isExpired )
			end )

		ozCatalog:addCloseAction( function()

			quotaMonitorSubscription:dispose()
			userExpiredSubscription:dispose()

		end )

	end

	local purgeAgent = LrmOzPurgeAgent( ozCatalog )

	self:setOwned {
		_syncClientBase = syncClientBase,
		_storageMonitor = storageMonitor,
		_binaryUploader = binaryUploader,
		_purgeAgent = purgeAgent,
		_errorHandler = errorHandler,
		_userSynchronizer = userSynchronizer,
		_conflictResolver = conflictResolver,
		_catalogServiceViaWFCatalog = ozCatalog
	}

	-- Stuck Sync Analytics

	local kStuckSyncAnalyticsInterval = 180 -- seconds

	self._docsStuckSyncingMetadataSubscription =
			WFCatStuckSyncUtils.getDocsStuckSyncingMetadataStream( catalog, self._isNormalSyncRunningStream )

				:calm( kStuckSyncAnalyticsInterval, WFRxScheduler.sharedScheduler() )

				:flatMapLatest( function( stuckDocIds )

					return WFCatStuckSyncUtils.resolveStuckSync( catalog, stuckDocIds )

				end )

				:subscribe()

	self._docsStuckUploadingOriginalSubscription =
			WFCatStuckSyncUtils.getDocsStuckUploadingOriginalStream( catalog, self._isNormalSyncRunningStream )

				:calm( kStuckSyncAnalyticsInterval, WFRxScheduler.sharedScheduler() )

				:subscribe( function( stuckDocIds )

					WFCatStuckSyncUtils.trackStuckBinaryDocuments( stuckDocIds )

				end )

        -- Register a tracking event for schema conflicts
        WFRxDocStoreMessageProcessor.onSchemaConflictWFRxDocStoreImp( trackDocStoreSyncInitError )

	-- WFCatSyncClientMessageProcessor.lua uses rawget, so we have to stash
	-- attachMessageHandlers here

	self.attachMessageHandlers = function( self )

		WFRequestDispatcher.onMessageDo {
			message = 'purgeImmediately',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'purgeForAlbum',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'maybePurgeExternalDevelopSettingsFile',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'startContinuousPurge',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'stopContinuousPurge',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'lockAssetForPurge',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

		WFRequestDispatcher.onMessageDo {
			message = 'unlockAssetForPurge',
			target = self._purgeAgent,
			onlyIfCallbackIsStillValid = true,
		}

	end

end

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

function syncClientImpMethods:__index( k )

	-- When code attempts to call an undefined method on a LrmSyncClientImp object,
	-- this function is called instead. If the below code determines that the
	-- WFCatSyncClientBase class has such a method, then that method is called. With
	-- this, all the class methods defined within WFCatSyncClientBase are automatically
	-- exposed as class methods of LrmSyncClientImp.

	-- Some of the WFCatSyncClientBase methods, such as startSync, require
	-- special handling within LrmSyncClientImp. You'll see these methods
	-- defined later in this file. All manually-defined methods take precedent
	-- over the metamethod mappings below.

	if not k then
		return
	end

	if self._syncClientBase:hasMethod( k ) then

		local syncClientBaseFn = function( _, ... )

			logger:infof( "Now calling WFCatSyncClientBase:%s via LrmSyncClientImp", k )

			return self._syncClientBase[ k ]( self._syncClientBase, ... )

		end

		return syncClientBaseFn

	else

		local errorFn = function( _, ... )

			local warning = string.format( "Unable to call unknown method: %s", k  )

			logger:warn( warning )

			return Rx.Observable.throw( warning )

		end

		return errorFn

	end

end

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

function syncClientImpMethods:getSyncClientBaseMethodList()

	check( 'LrmSyncClientImp' )

	-- This function returns a list of methods on the WFCatSyncClientBase object.
	-- It is used to build up the message handlers within LrmSyncClientMessageProcessor.

	return self._syncClientBase.listMethods()

end

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

function syncClientImpMethods:close()

	check( 'LrmSyncClientImp' )

	if self._checkAutoPurgeSubscription then

		self._checkAutoPurgeSubscription:dispose()
		self._checkAutoPurgeSubscription = nil

	end

	if self._checkOzForCatalogUpdatesSubscription then

		self._checkOzForCatalogUpdatesSubscription:dispose()
		self._checkOzForCatalogUpdatesSubscription = nil

	end

	if self._cloudTrashAutoPurge then

		self._cloudTrashAutoPurge:dispose()
		self._cloudTrashAutoPurge = nil

	end

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

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

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

end

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

function syncClientImpMethods:isPushingMetadata()

	-- Though this method calls a WFCatSyncClientBase method directly without
	-- any special handling, we must define it manually because the method
	-- names don't match. This is also true for isPullingMetadata.

	check( 'LrmSyncClientImp' )

	return self._syncClientBase:isUploadingMetadata()

end

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

function syncClientImpMethods:isPullingMetadata()

	check( 'LrmSyncClientImp' )

	return self._syncClientBase:isDownloadingMetadata()

end

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

function syncClientImpMethods:getDeleteFeedCallback()

    check( 'LrmSyncClientImp' )

    if self._purgeAgent then
        return self._purgeAgent:getDeleteFeedCallback()
    end

    return Observable.empty()

end

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

function syncClientImpMethods:startSync()

	check( 'LrmSyncClientImp' )

	logger:debugf( 'starting sync' )

	self._isNormalSyncRunningStream:onNext( true )

	self._binaryUploader:startContinuousUploads()

	self._purgeAgent:mayBeStartPurging()

	return self._syncClientBase:startSync()

end

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

function syncClientImpMethods:startSyncWithoutUploader()

	check( 'LrmSyncClientImp' )

	logger:debugf( 'starting sync without uploader' )

	self._isNormalSyncRunningStream:onNext( false )

	self._purgeAgent:mayBeStartPurging()

	return self._syncClientBase:startSync()

end

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

function syncClientImpMethods:startAccountsSync()

	check( 'LrmSyncClientImp' )

	logger:debugf('starting accounts sync')

	local isAuthenticatedUser = self._userId ~= WFOzSyncConstants.kGlobalDocumentId

	if isAuthenticatedUser then
		self._userSynchronizer:startContinuousUpdates()
	end

end

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

function syncClientImpMethods:stopSync()

	check( 'LrmSyncClientImp' )

	logger:debugf( 'stopping sync' )

	self._isNormalSyncRunningStream:onNext( false )

	self._binaryUploader:stopContinuousUploads()

	self._purgeAgent:mayBeStopPurging()

	return self._syncClientBase:stopSync()

end

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

function syncClientImpMethods:stopAccountsSync()

	check( 'LrmSyncClientImp' )

	logger:debugf('stopping accounts sync')

	self._userSynchronizer:stopContinuousUpdates()

end

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

function syncClientImpMethods:startSyncInResidualMode()

	check( 'LrmSyncClientImp' )

	self._isNormalSyncRunningStream:onNext( false )

	self._binaryUploader:startContinuousUploadOfOnlySpaceAssets()

	self._purgeAgent:mayBeStartPurging()

	return self._syncClientBase:startSyncInResidualMode()

end

--------------------------------------------------------------------------------
function syncClientImpMethods:startSpaceSyncWithoutOzCatalog()

	check( 'LrmSyncClientImp' )

	self._isNormalSyncRunningStream:onNext( false )

	self._binaryUploader:startContinuousUploadOfOnlySpaceAssets()

	self._purgeAgent:mayBeStartPurging()

	return self._syncClientBase:startSpaceMetadataSyncInFreemiumMode()

end

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

function syncClientImpMethods:enableBackgroundUpload( enable )

	check( 'LrmSyncClientImp', '?boolean' )

	enable = enable or false
	self._binaryUploader:enableBackgroundUpload( enable )

end

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

function syncClientImpMethods:getDetailedUploadProgress()

	check( 'LrmSyncClientImp' )

	return self._progressReporter:status()

end

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

function syncClientImpMethods:getOzMaintenanceNotificationStream( optArgs )

	check( 'LrmSyncClientImp', '?table' )

	if not self._ozMaintenanceNotificationStream then

		local catalog = self._syncClientBase:getCatalog()

		local catalogOzMaintenanceNotificationStream
				= WFCatOzMaintenanceTracker.getOzMaintenanceNotificationStream( catalog, optArgs )

		local spaceFeedsMaintenanceNotificationStream
				= self._syncClientBase:spaceFeedsMaintenanceNotificationStream()

		self:addCloseAction( function()

			catalogOzMaintenanceNotificationStream:dispose()

		end )

		self._ozMaintenanceNotificationStream =
				catalogOzMaintenanceNotificationStream
					:merge( spaceFeedsMaintenanceNotificationStream )

	end

	return self._ozMaintenanceNotificationStream

end

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

--[[!
	\brief This API that can be used gather real-time sync diagnostic data.

	\param optCalm (optional number) The maximum frequency at which the stream will
		fire with updates.

	\return Observable< table > An Observable whose value tracks a table of
		sync diagnostic data.
--]]

function syncClientImpMethods:syncDiagnostics( optCalm )

	check( 'LrmSyncClientImp', '?number' )

	return Observable.combineLatest(
			self._syncClientBase:syncDiagnostics( optCalm ),
			self._binaryUploader:syncDiagnostics( optCalm )
		)

		:map( function( metadataSyncDiagnostics,
						binaryUploaderSyncDiagnostics )

			return {

				metadataSyncDiagnostics = metadataSyncDiagnostics,
				binaryUploaderSyncDiagnostics = binaryUploaderSyncDiagnostics,

			}

		end )

end

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

--[[!
	\brief Fetch a stream that fires true when sync is running, false otherwise.

	\return Observable< boolean >
--]]

function syncClientImpMethods:isNormalSyncRunningStream()

	check( 'LrmSyncClientImp' )

	return self._isNormalSyncRunningStream

end

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

--[[!
	\brief This API provides an estimation -- in seconds -- for the time remaining
		to upload any outstanding binaries. -1 is returned in the event there is
		nothing to upload.
		
	\return Observable< number > An Observable that fires with numbers representing
		the time remaining in seconds.
--]]

function syncClientImpMethods:getEstimatedTimeRemainingForUploads()

	return self._binaryUploader:getEstimatedTimeRemaining()

end

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

function syncClientImpMethods:_DEBUG_SyncClientReturnsFoo()

	check( 'LrmSyncClientImp' )

	return Observable.just( 'foo' )

end

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

return Rx.Class( 'LrmSyncClientImp', syncClientImpMethods )
