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

WFCatBinaryDownloader.lua

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

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'
local Observable = Rx.Observable

	-- WF.core.logger
local WFLogger = require 'WFLogger'

	-- WF.core.os
local KSDate = require 'KSDate'
local KSData = require 'KSData'

	-- WF.core.performance
local WFPerformanceReport = require 'WFPerformanceReport'

	-- WF.core.utils
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'

	-- WF.oz_client.managed_catalog
local WFCatFileSpec = require 'WFCatFileSpec'

	-- WF.web.http_client
local WFRxHttpClient = require 'WFRxHttpClient'

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

local check = assert( WFArguments.check )
local derefOpt = assert( KSTableUtils.derefOpt )
local WFCatFileSpec_getUrlForLinkKey = assert( WFCatFileSpec.getUrlForLinkKey )

local logger = WFLogger 'WFCatBinaryDownloader'
--	:setLevel 'debug'


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

local WFCatBinaryDownloader = {

	_catalog = false,
		-- (WFCatalog)

	_catalogSpec = false,
		-- (WFCatSpec)

	_docStore = false,
		-- (WFRxDocStore)

}

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

function WFCatBinaryDownloader:__init( catalog )

	check( 'WFCatBinaryDownloader', 'WFCatalog' )

	self._catalog = catalog

	self._catalogSpec = catalog:ozCatalogSpec()

	self._docStore = catalog:docStore()

end

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

local function downloadOneBinary( self, docId, linksKey, maybeUrl, maybeRevId, downloadPriority, downloadCost )

	logger:debugf( "downloadOneBinary assetId = %s links key = %s",
				   docId, linksKey )

	local startTime = KSDate.timeNowForAnimation()
	local savedUrl
	local revId, binaryUrl
	
	local urlObservable
	if maybeUrl then
		urlObservable = Observable.just( maybeUrl, maybeRevId )
	else
		urlObservable = self._docStore:docValue( docId )
			:map( function( value )
				return WFCatFileSpec_getUrlForLinkKey( value, linksKey ),
						derefOpt( value, '_revId' )
			end )
	end
	
	return urlObservable
		:flatMapLatest( function( fullUrl, revisionId )
			revId = revisionId
			binaryUrl = fullUrl
			if fullUrl  then

				savedUrl = fullUrl

				logger:infof( "downloadOneBinary assetId = %s links key = %s downloading from URL %s",
							  docId, linksKey, fullUrl )

				local options = {
					requestPriority = downloadPriority or WFRxHttpClient.requestPriorityMedium,
					requestCost = downloadCost or WFRxHttpClient.requestCostDefault,
				}

				return self._catalogSpec:getBinary( fullUrl, options )

			else

				return Observable.throw(
					string.format( "No URL provided by Oz for asset ID %s link %s",
								   docId, linksKey ) )

			end

		end )

		:flatMapLatest( function( result, headers )

			local headerContentLength = tonumber( derefOpt( headers, "Content-Length" ) ) or 0
			local headerContentEncoding = tostring( derefOpt( headers, "Content-Encoding" ) ) 
			local dataLength = KSData.length( result )
			
			if dataLength ~= headerContentLength and headerContentEncoding ~= 'gzip' then
				logger:errorf( "downloadOneBinary assetId = %s data length (%d) doesn't match headers.Content-Length (%d)",
							  docId, dataLength, headerContentLength )
				return Observable.throw( "Error downloading file, dataLength doesn't match headers.Content-Length", headers )
			end

			return Observable.just( result, revId, headers )

		end )

		:tap(
			function( result, revId )

				logger:infof( "downloadOneBinary assetId = %s links key = %s downloaded from URL %s in %.2f seconds",
							  docId, linksKey, savedUrl,
							  KSDate.timeNowForAnimation() - startTime )

			end,
			function( message, headers )

				logger:errorf( "downloadOneBinary assetId = %s links key = %s downloading from URL %s\n\n"
							   .. "FAILED after %.2f seconds with message %s\n\nheaders %s\n\n",
							   docId, linksKey, savedUrl,
							   KSDate.timeNowForAnimation() - startTime,
							   message, headers )

			end )
		:catch( function( message, headers )
		
			return Observable.throw( message, headers, linksKey, binaryUrl, revId )
		
		end ) 

end

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

local function createDownloadStats( result, revId, headers )
	local stats = {}
	if headers then
		stats.fileSize = tonumber( headers[ 'Content-Length' ] ) or 0
	end
	return stats
end
	
--------------------------------------------------------------------------------

function WFCatBinaryDownloader:getRendition( docId, renditionType, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '+string', '?number' )
	
	local downloadCost = WFRxHttpClient.requestCostDownloadRendition
	
	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: rendition_' .. renditionType, 
					downloadOneBinary( self, docId, '/rels/rendition_type/' .. renditionType, nil, nil, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getRenditionByUrl( docId, renditionType, url, revId, downloadPriority )
	
	check( 'WFCatBinaryDownloader', '+string', '+string', '+string', '+string', '?number' )	
	
	local downloadCost = WFRxHttpClient.requestCostDownloadRendition
	
	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: rendition_' .. renditionType, 
					downloadOneBinary( self, docId, '/rels/rendition_type/' .. renditionType, url, revId, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )
end

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

function WFCatBinaryDownloader:getProxy( docId, proxyType, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadNegative

	if WFRxHttpClient.requestPriorityHighest == downloadPriority then
		downloadCost = WFRxHttpClient.requestCostMaximizeBandwidth
	end

	return  WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: proxy_' .. proxyType, 
					downloadOneBinary( self, docId, '/rels/proxy_type/' .. proxyType, nil, nil, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getProxyByUrl( docId, proxyType, url, revId, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '+string', '+string', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadNegative

	if WFRxHttpClient.requestPriorityHighest == downloadPriority then
		downloadCost = WFRxHttpClient.requestCostMaximizeBandwidth
	end

	return  WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: proxy_' .. proxyType, 
					downloadOneBinary( self, docId, '/rels/proxy_type/' .. proxyType, url, revId, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getMaster( docId, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadNegative

	if WFRxHttpClient.requestPriorityHighest == downloadPriority then
		downloadCost = WFRxHttpClient.requestCostMaximizeBandwidth
	end

	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: master', 
					downloadOneBinary( self, docId, '/rels/master', nil, nil, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getMasterByUrl( docId, url, revId, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '+string', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadNegative

	if WFRxHttpClient.requestPriorityHighest == downloadPriority then
		downloadCost = WFRxHttpClient.requestCostMaximizeBandwidth
	end

	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: master', 
					downloadOneBinary( self, docId, '/rels/master', url, revId, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getXmp( docId, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadXmp

	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: xmp', 
					downloadOneBinary( self, docId, '/rels/xmp', nil, nil, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getXmpByUrl( docId, url, revId, downloadPriority )

	check( 'WFCatBinaryDownloader', '+string', '+string', '+string', '?number' )

	local downloadCost = WFRxHttpClient.requestCostDownloadXmp

	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: xmp', 
					downloadOneBinary( self, docId, '/rels/xmp', url, revId, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )

end

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

function WFCatBinaryDownloader:getXmpDevelop( docId, downloadPriority )
	
	check( 'WFCatBinaryDownloader', '+string', '?number' )
	
	local downloadCost = WFRxHttpClient.requestCostDownloadXmp
	
	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: xmp_develop',
					downloadOneBinary( self, docId, '/rels/xmp/develop', nil, nil, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )
					
end


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

function WFCatBinaryDownloader:getXmpDevelopByUrl( docId, url, revId, downloadPriority )
	
	check( 'WFCatBinaryDownloader', '+string', '+string', '+string', '?number' )
	
	local downloadCost = WFRxHttpClient.requestCostDownloadXmp
	
	return WFPerformanceReport.wrapObservable( 
					'WFCatBinaryDownloader: xmp_develop',
					downloadOneBinary( self, docId, '/rels/xmp/develop', url, revId, downloadPriority, downloadCost ),
					{ onNext = createDownloadStats } )
					
end

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

return Rx.Class 'WFCatBinaryDownloader'( WFCatBinaryDownloader )
