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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
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.reactive
local KSReactive = require 'KSReactive'

	-- WF.core.utils
local WFAnalytics = require 'WFAnalytics'
local WFUnknown = require 'WFUnknown'
local KSIdGenerator = require 'KSIdGenerator'
local WFCloseSupport = require 'WFCloseSupport'
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local WFArguments = require "WFArguments"
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.core.rxlua
local WFRxEventBridge = require 'WFRxEventBridge'
local WFRxPromiseBridge = require 'WFRxPromiseBridge'

	-- WF.oz_client.catalog
local OzDocStoreViaWFRxDocStore = require 'OzDocStoreViaWFRxDocStore'
local OzEpoch = require 'OzEpoch'
local OzFilePaths = require 'OzFilePaths'

local KSDataCache = require 'KSDataCache'

	-- WF.sqlite.index_store
local WFRxIndexStore = require 'WFRxIndexStore'

	-- WF.sqlite.wfrxdatabase
local WFRxDatabase = require 'WFRxDatabase'


	-- WF.lrmobile
local LrmCatProxies = require "LrmCatProxies"
local LrmCatOriginals = require "LrmCatOriginals"
local WFCapabilities = require 'WFCapabilities'

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

local asKSEvent = WFRxEventBridge
local asKSPromise = WFRxPromiseBridge

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

local CatalogPrototype = {
	
	_wfCatalogStream = false,
	
	_docStoreProxy = false,
	
	_indexStoreProxy = false,
	
	_ozUser = false,
	
	_syncClient = false,
	
	_binaryDownloader = false,

	_dataCache = false,
	
	_accessErrorB = false,
	
	WFCloseSupport.addCloseSupportMethods,

	KSReactive.addReactiveSupportMethods,
}

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

local ProxyMT = {

	__index = function( self, k )
	
		local reactiveMode = string.sub( k, k:len() )
		
		local fn = rawget( self, k )
		
		if reactiveMode == 'P' then
			
			return function( this, ... )
			
				local nOrigArgs = select( '#', ... )
				local origArgs = { ... }
				
				return self._upstreamObservable
				
					:call( asKSPromise )
					:mapDataP_new( function( docStoreOrIndexStore )
					
						local r = 	docStoreOrIndexStore[ k ]( 
										docStoreOrIndexStore, 
										unpack( origArgs, 1, nOrigArgs ) 
									)
						return r
						
					end )
				
			end
			
		elseif reactiveMode == 'B' then
			
			return function( this, ... )
			
				local nOrigArgs = select( '#', ... )
				local origArgs = { ... }
		
				return self._upstreamObservable
				
					:call( asKSEvent )
					:closingMapSwitchE( function( docStoreOrIndexStore )
					
						local r = 	docStoreOrIndexStore[ k ]( 
										docStoreOrIndexStore, 
										unpack( origArgs, 1, nOrigArgs ) 
									)
						return r
						
					end )
					:closingHoldB( WFUnknown.value() )
					
			end
			
		elseif k == 'halt' then
		
			return function( this, ... )
			
				return self._upstreamObservable
					
					:call( asKSPromise )
					:whenResolvedDo( function( _, docStoreOrIndexStore )
					
						docStoreOrIndexStore[ k ]( docStoreOrIndexStore ) 
						
					end )
					
					:runUntilDone()
					
			end
					
		elseif fn ~= nil then
		
			return fn
		
		else
		
			return function( this, ... )
			
				local nOrigArgs = select( '#', ... )
				local origArgs = { ... }
		
				return self._upstreamObservable
					:flatMapLatest( function( docStoreOrIndexStoreOrRxDatabase )
					
						if not ( WFRxIndexStore:isClassOf( docStoreOrIndexStoreOrRxDatabase ) 
								or WFRxDatabase:isClassOf( docStoreOrIndexStoreOrRxDatabase ) ) then
							docStoreOrIndexStoreOrRxDatabase = docStoreOrIndexStoreOrRxDatabase:rxDocStore()
						end
						
						local r = 	docStoreOrIndexStoreOrRxDatabase[ k ]( 
										docStoreOrIndexStoreOrRxDatabase, 
										unpack( origArgs, 1, nOrigArgs ) 
									)
						return r
						
					end )
			end
			
		end
		
		
	end
}

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

function CatalogPrototype:__init( wfCatalog )

	local indexStoreStream = wfCatalog
		:map( function( wfCatalog )
						
			return wfCatalog:indexStore()
			
		end )
		
	local docStoreStream = wfCatalog
		:map( function( wfCatalog )
		
			return OzDocStoreViaWFRxDocStore( wfCatalog:docStore() )
			
		end )
		
	local IndexStoreProxy = {
		_upstreamObservable = indexStoreStream,
		
	}
	
	setmetatable( IndexStoreProxy, ProxyMT )
	
	local this = self
	
	local DocStoreProxy = {
		_upstreamObservable = docStoreStream,
		
		_docStorePathUUID = KSIdGenerator.uuidInLowercaseHex(),
		
		path = function( self )
			
--			debug.print( 'WARNING: OzDocStoreProxy:path() called: ' .. debug.traceback() )
			
			return self._docStorePathUUID
			
		end,
		
		addCloseTarget = function( self, obj )
		
			this:addCloseTarget( obj )
			
		end
		
	}
	setmetatable( DocStoreProxy, ProxyMT )
	
	self._indexStoreProxy = IndexStoreProxy
	self._docStoreProxy = DocStoreProxy
	
	self._wfCatalogStream = wfCatalog
		
	self:setOwned {
		_accessErrorB = KSReactive.variableB( false )
	}
	
end

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

function CatalogPrototype:setSyncClient( syncClient )

	self:setOwned {
		_syncClient = syncClient,
	}
	
end

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

function CatalogPrototype:getSyncClient()

	return self._syncClient
	
end

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

function CatalogPrototype:setBinaryDownloader( binaryDownloader )

	self:setOwned {
		_binaryDownloader = binaryDownloader,
	}

end

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

function CatalogPrototype:getBinaryDownloader()

	return self._binaryDownloader

end

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

function CatalogPrototype:ozUserIdP( badParam )

	assert( badParam == nil, 'No args expected' )

	return self._wfCatalogStream
		
		:map( function( wfCatalog )
		
			local user = wfCatalog:ozUser()
			
			if user then
				return user:userId()
			end
			
		end )
		
		:call( asKSPromise )
		
end

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

function CatalogPrototype:ozUserIdB()

	return self._wfCatalogStream
		
		:map( function( wfCatalog )
		
			local user = wfCatalog:ozUser()
			
			return user:userId()
			
		end )
		
		:call( asKSEvent )
		
		:closingHoldB( WFUnknown.value() )
		
end

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

function CatalogPrototype:ozUserP()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			return wfCatalog:ozUser()
			
		end )
		
		:call( asKSPromise )
		
end

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

function CatalogPrototype:photoInfoB( photoId )

	if photoId == nil then
		return KSReactive.constantB( nil )
	else
		return self:docStore()
			:docValueB( 'asset', photoId )
			:closingMapB( function( docValue )
			
				return docValue
				
			end )
	end

end

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

function CatalogPrototype:getCatUser()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			return wfCatalog:ozUser()
			
		end )
		
end

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

function CatalogPrototype:getWfCatalog()

	return self._wfCatalogStream

	:map( function( wfCatalog )

		return wfCatalog

	end )

end

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

function CatalogPrototype:photoInfoP( photoId )

	return self:docStore():docValueOnceP( 'asset', photoId )

end

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

function CatalogPrototype:getCacheEpochB()
	return OzEpoch.cacheEpochB( self )
end

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

function CatalogPrototype:post( url, data, optArgs )

	return self._wfCatalogStream
	
		:flatMapLatest( function( wfCatalog )
		
			optArgs.requestMethod = 'POST'
			return wfCatalog:ozUser():put( url, data, optArgs )
			
		end )
end

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

function CatalogPrototype:getP( url, optArgs )

	return self._wfCatalogStream
	
		:flatMapLatest( function( wfCatalog )
		
			return wfCatalog:ozUser():get( url, optArgs )
			
		end )
	
		:call( asKSPromise )
		
end

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

function CatalogPrototype:getBinaryP( url, optArgs )

	optArgs = optArgs or {}

	local enableDirectDownload = WFPlatformInfo.isAndroid 
			and WFCapabilities.getCapability( "LrmCatLibraryModel.enableDirectDownload" ) ~= "false"

	--iOS uses its native implemention in terms of direct downloading

	if enableDirectDownload then

		optArgs['requestHeaders'] = {
			{ header = 'Prefer', value = 'download-direct' }
		}

		optArgs['followRedirects'] = true

	end

	return self._wfCatalogStream

		:flatMapLatest( function( wfCatalog )

			return wfCatalog:ozUser():getBinary( url, optArgs )

		end )

		:catch( function( message, headers, ... )

			if url then

				local failureInfo = {
					url = url,
					responseData = message,
					headers = headers,
					isDirectDownload = enableDirectDownload,
				}

				WFAnalytics.trackEventDownloadFailure( failureInfo )

			end

			return Rx.Observable.throw( message, headers, ... )

		end )

		:call( asKSPromise )

end

--------------------------------------------------------------------------------
		
function CatalogPrototype:docStore()

	return self._docStoreProxy
	
end
	
--------------------------------------------------------------------------------

function CatalogPrototype:indexStore()

	return self._indexStoreProxy

end

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

function CatalogPrototype:getCatalogIdP()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			return wfCatalog:ozCatalogSpec():catalogId()
			
		end )
		
		:call( asKSPromise )
		
end

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

function CatalogPrototype:getCatalogBasePath()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			return OzFilePaths.getCatalogBasePath( wfCatalog:ozCatalogSpec():catalogId() )
						
		end )

end

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

function CatalogPrototype:getDataCachePath()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			local documentsDir = KSPathUtils.getStandardFilePath 'carouselDocuments'
		
			local catalogDir = KSPathUtils.child( documentsDir, wfCatalog:ozCatalogSpec():catalogId() )
			catalogDir = KSPathUtils.child( catalogDir, "renditions" )
	
			WFFileUtils.mkdir( catalogDir )
	
			local cachePath = KSPathUtils.child( catalogDir, "previewCache" )
			
			return cachePath
			
		end )
	
end

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

function CatalogPrototype:getDataCachePathP()

	return self:getDataCachePath()
	
		:call( asKSPromise )
		
end


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

function CatalogPrototype:getDataCache()

	if not self._dataCache then
	
		self._dataCache = self:getDataCachePath()
		
			:map( function( path )
			
				local dataCache = KSDataCache( path )
				self:addCloseTarget( dataCache )
				
				return dataCache
				
			end )
			
			:shareReplay( 1 )
			
	end
	
	return self._dataCache
	
end

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

function CatalogPrototype:getDataCacheP()

	return self:getDataCache()
	
		:call( asKSPromise )
	
end

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

function CatalogPrototype:getOriginalsDocumentsFolderP()

	return self._wfCatalogStream
	
		:map( function( wfCatalog )
		
			local catalogId = wfCatalog:ozCatalogSpec():catalogId()

			if WFPlatformInfo.isAndroid then
				-- Needed as android has different originals path for freemium/created users
				return OzFilePaths.getStandardOriginalFolderDocumentsPath( catalogId )
			else
				return OzFilePaths.getCatalogBasePath( catalogId )
			end
			
		end )
	
		:call( asKSPromise )
		
end

--------------------------------------------------------------------------------
		
function CatalogPrototype:setPurgeEnabled( enable )
	
	error( 'setPurgeEnabled() is not and should not be implemented here' )
	
end

--------------------------------------------------------------------------------
		
function CatalogPrototype:setSyncEnabled( enable )

	error( 'setSyncEnabled() is not and should not be implemented here ' .. debug.traceback() )
	
end

--------------------------------------------------------------------------------
	
function CatalogPrototype:getCatalogId()
	
	error( 'getCatalogId() is not and should not be implemented here ' .. debug.traceback() )
	
end

--------------------------------------------------------------------------------
	
function CatalogPrototype:dataCache()

	error( 'dataCache() is not and should not be implemented here ' .. debug.traceback() )
	
end

function CatalogPrototype:proxyDataPathP( photoId, doNotFetch, proxySize, priority )

    WFArguments.check( 'OzCatalogService', 'string', 'boolean', 'number', 'number' )
			
    return LrmCatProxies.proxyDataPathP( self, photoId, doNotFetch, proxySize, priority )
    
end

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

function CatalogPrototype:originalDataPathP( photoId, doNotFetch, priority )

    WFArguments.check( 'OzCatalogService', 'string', 'boolean', 'number' )
	
			
    return LrmCatOriginals.originalDataPathP( self, photoId, doNotFetch, priority )
    
end
	
--------------------------------------------------------------------------------

return Rx.Class( 'OzCatalogService', CatalogPrototype )
