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

NmbDocValueToRenderInfo.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.

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

NmbDocValueToRenderInfo consumes a sequence of array-valued tables of asset IDs
and reports the "render info" of each corresponding document either as it first
appears in the array, or as it changes in value while still in the array.

Asset IDs may be either numeric (local) or string (global) document IDs.

Render info is expressed as a table similar to the following:

	{
		_id = "(uuid / global ID)",
		_localId = (number),
		originalPath = "(path)",
		proxyPath = "(path)",
		develop = {
			-- develop settings as from Oz (http://belt.sea.adobe.com/asset/develop.html)
		}
	}

------------------------------------------------------------------------------]]

	-- WF.core.utils
local KSTableUtils = require 'KSTableUtils'
local WFDiagnosticLog = require "WFDiagnosticLog"

	-- WF.oz_client
local WFLocalDocUtils = require "WFLocalDocUtils"

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

	-- WF.oz_client.managed_catalog.develop
local NmbDocValueDevelopSettingsUtils = 
		require "NmbDocValueDevelopSettingsUtils"

	-- pixel provider
local WFCatRenderCapabilities = require 'WFCatRenderCapabilities'

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

local derefOpt = assert( KSTableUtils.derefOpt )

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

local WFLocalDocUtils_currentRevIdIsOnOz =
		assert( WFLocalDocUtils.currentRevIdIsOnOz, 
				"WFLocalDocUtils.currentRevIdIsOnOz does not exist" )

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

local WFCatIndexUtils_isVideoDocument = 
		assert( WFCatIndexUtils.isVideoDocument,
				"WFCatIndexUtils.isVideoDocument is missing" )

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

local logger = require 'WFLogger'( 'NmbDocValueToRenderInfo' )

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

local function wrapInTag( tag, str )

	return string.format( "<%s>%s</%s>", tag, str, tag )

end

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

local function maybeGetSize( sizeURI )

	local maybeSize = sizeURI:gsub( ".*/(.*)", "%1" )

	if maybeSize == "" then

		return

	end

	return maybeSize

end

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

local function reduceT2( ret, f, t, prevK, ... )

	local k, v = next( t, prevK )

	if not k then

		return ret

	end

	return reduceT2( f( ret, k, v, ... ), f, t, k, ... )

end

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

local function reduceT( ret, f, t, ... )

	return reduceT2( ret, f, t, nil, ... )

end

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

local function addToURIList( downloadableBinaryInfo, uriListName, href, size )

	local uriList = downloadableBinaryInfo[ uriListName ] or {}

	uriList[ #uriList + 1 ] = 
		{
			
			href = href,

			size = size,

		}

	downloadableBinaryInfo[ uriListName ] = uriList

	return downloadableBinaryInfo

end

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

local function maybeAddDownloadableBinaryInfo( 
				downloadableBinaryInfo, uriType, uriInfoTable, 
				ozHasCurrentRevId, isVideo )

	if type( uriType ) ~= "string" or type( uriInfoTable ) ~= "table" then

		-- no valid link info, do nothing
		return downloadableBinaryInfo

	end

	local href = uriInfoTable.href

	if not href then

		-- ignore it if no link

		return downloadableBinaryInfo

	end

	local size = maybeGetSize( uriType )

	local isVideoProxyURI =
			isVideo and not not uriType:find( 'rendition_type/720p' )

	local isRenditionURI = not isVideoProxyURI and 
							uriType:find( 'rendition_type' )
	
	if isRenditionURI and size and uriInfoTable.invalid then

		-- add invalid renditions links 
		return addToURIList( downloadableBinaryInfo, 'invalid_renditions', href, size )

	end

	if uriInfoTable.invalid then

		-- ignore invalid non rendition links
		return downloadableBinaryInfo

	end

	if uriType:find( 'rendition_generate/fullsize' ) then

		-- add generate full size link
		downloadableBinaryInfo.createFullRenditionURITemplate = uriInfoTable.href

		return downloadableBinaryInfo

	end
		
	if uriType == '/rels/master' then

		-- add master link
		downloadableBinaryInfo.master = uriInfoTable.href

		return downloadableBinaryInfo

	end

	if not size then

		-- ignore any other links without a size 
		return downloadableBinaryInfo

	end

	if ozHasCurrentRevId and isRenditionURI then

		if size == "fullsize" then

			-- add up to date fullsize rendition link
			downloadableBinaryInfo.downloadFullRenditionURI = href

			return downloadableBinaryInfo

		end

		-- add up to date rendition links to table
		return addToURIList( downloadableBinaryInfo, 'renditions', href, size )

	end

	if uriType:find( 'proxy_type' ) or isVideoProxyURI then

		-- add up to non video proxy links to table
		return addToURIList( downloadableBinaryInfo, 'proxies', href, size )

	end

	return downloadableBinaryInfo

end

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

local function getDownloadableBinaryInfo( docValue )

	local links = WFCatFileSpec.getLinksFromDocValue( docValue )

	links = type( links ) == "table" and links or {}

	local downloadableBinaryInfo = 
			reduceT( {}, 
				maybeAddDownloadableBinaryInfo, 
					links, WFLocalDocUtils_currentRevIdIsOnOz( docValue ),
					WFCatIndexUtils_isVideoDocument( docValue ) )

	return downloadableBinaryInfo.renditions or {}, 
			downloadableBinaryInfo.invalid_renditions or {},
			downloadableBinaryInfo.proxies or {},
			downloadableBinaryInfo.master,
			downloadableBinaryInfo.createFullRenditionURITemplate,
			downloadableBinaryInfo.downloadFullRenditionURI

end

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

local function maybeGetCameraProfileInfo( docValue )

	local cameraProfileDigest = derefOpt( docValue, "_localOnly", "cameraProfileInfo", "profileDigest" )
	cameraProfileDigest = cameraProfileDigest ~= "NA" and cameraProfileDigest or nil
	
	local cameraProfileMissing = derefOpt( docValue, "_localOnly", "cameraProfileInfo", "isMissing" )
	local hasStandardProfile = derefOpt( docValue, "_localOnly", "cameraProfileInfo", "isStandard" )
	
	return cameraProfileDigest, cameraProfileMissing, hasStandardProfile
end

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

local NmbDocValueDevelopSettingsUtils_getSanitizedDocValueDevelopStanza =
		assert( NmbDocValueDevelopSettingsUtils.getSanitizedDocValueDevelopStanza,
				"missing NmbDocValueDevelopSettingsUtils.getSanitizedDocValueDevelopStanza" )

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

local function shouldDisallowLocalEdits( docValue )

	local canEditAsset = require 'WFCatSharedWithYouUtils'.canEditAsset( docValue )
	return not canEditAsset

end

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

local function extractRenderInfoFromDocValue( docValue )
	
	local renditions, invalid_renditions, proxies, master,
			createFullRenditionURITemplate, downloadFullRenditionURI = 
				getDownloadableBinaryInfo( docValue )
	
	local ozProxyLink, ozProxySize = WFCatFileSpec.getProxyLinkFromDocValue( docValue )
	
	local xmpDevelopInfo = WFCatFileSpec.getXmpDevelopInfoFromDocValue( docValue ) or {}
	
	local cameraProfileDigest, cameraProfileMissing, hasStandardProfile = maybeGetCameraProfileInfo( docValue )
	
	local originalCannotBeRendered = WFCatFileSpec.getOriginalCannotBeRendered( docValue )

	local disallowLocalEdits = nil

	if shouldDisallowLocalEdits( docValue ) then

		-- don't allow editing shared album assets unless marked editable
		disallowLocalEdits = true

	end

	return {

		_id = docValue._id,

		_localId = docValue._localId,

		_revId = docValue._revId or docValue._newRevId,
			-- In the case where the docValue has no revId, which can happen if the document
			-- hasn't yet been written in to the doc store, attempt to use the _newRevId field
			-- which will eventually populate the revId field.
			
		_spaceId = docValue._spaceId,
		
		_changeCounter = docValue._changeCounter,
		
		subtype = docValue.subtype,

		originalPath = WFCatFileSpec.buildOriginalPathFromDocValue( docValue ),

		proxyPath = WFCatFileSpec.buildProxyPathFromDocValue( docValue ),
		
		pathToXmpDevelop = xmpDevelopInfo.path,
		
		pathToXmpDevelopSha256 = xmpDevelopInfo.sha256,

		pathToXmpSidecar = WFCatFileSpec.getXmpSidecarPathFromDocValue( docValue ),

		xmpSidecarExists = WFCatFileSpec.getXmpSidecarExistsFromDocValue( docValue ),

		sha256 = derefOpt( docValue, 'importSource', 'sha256' ),

		localOriginalSha256 = WFCatFileSpec.getLocalOriginalSha256WithFallback( docValue ),

		ozMasterLink = WFCatFileSpec.getOriginalLinkFromDocValue( docValue ),
		
		localProxySha256 = WFCatFileSpec.getLocalProxySha256( docValue ),
		
		ozProxyLink = ozProxyLink,
		
		ozProxySize = ozProxySize,
		
		ozXmpDevelopLink = WFCatFileSpec.getXmpDevelopLinkFromDocValue( docValue ),

		ozXmpLink = WFCatFileSpec.getXmpLinkFromDocValue( docValue ),

		develop = NmbDocValueDevelopSettingsUtils_getSanitizedDocValueDevelopStanza(
					docValue ),
		
		renditions = renditions,

		invalid_renditions = invalid_renditions,
		
		proxies = proxies,
		
		master = master,

		createFullRenditionURITemplate = createFullRenditionURITemplate,

		downloadFullRenditionURI = downloadFullRenditionURI,
		
		cameraProfileDigest = cameraProfileDigest,
		
		cameraProfileMissing = cameraProfileMissing,
		
		hasStandardProfile = hasStandardProfile,
		
		willNotUploadMaster = derefOpt( docValue, 'importSource', 'willNotUploadMaster' ),
		
		originalCannotBeRendered = originalCannotBeRendered,

		disallowLocalEdits = disallowLocalEdits,
		
}

end

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

local _didNotRenderCounts = {}

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

local function concatReasonRow( rows, reason, count )

	rows[ #rows + 1 ] =
		wrapInTag( "tr", 
			wrapInTag( "td", count ) ..
			wrapInTag( "td", reason ) )

	return rows
end

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

local function maybeReportDidNotRenderCounts()

	if not next( _didNotRenderCounts ) then

		return

	end

	local reasonRows = reduceT( {}, concatReasonRow, _didNotRenderCounts )

	local skippedRenderReasonsTable =
			wrapInTag( "table",
				wrapInTag( "tr",
							wrapInTag( "th", "count" ) ..
							wrapInTag( "th", "reason" ) ) ..
				table.concat( reasonRows ) )
	
	WFDiagnosticLog.addSectionToLog( {

		id = 'CannotRenderReasons',

		name = "Skipped Render Reasons",

		weight = 420,

		body = skippedRenderReasonsTable,

	} )

end

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

WFDiagnosticLog.addLogSectionFunc( maybeReportDidNotRenderCounts )

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

local function maybeExtractRenderInfoFromDocValue( docValue )

	local canRender, maybeCannotRenderReason =
			WFCatRenderCapabilities.isRenderableAsset( docValue ) 

	if not canRender then

		if logger.will_info then

			logger:infof( "not extracting render info for docValue._localId = %s because: %s", 
							docValue._localId or 
								( docValue._deleted and "deleted docValue" ) or
								"missing _localId",
							maybeCannotRenderReason )

		end

		_didNotRenderCounts[ maybeCannotRenderReason ] =
			( _didNotRenderCounts[ maybeCannotRenderReason ] or 0 ) + 1

		return

	end

	return extractRenderInfoFromDocValue( docValue )

end

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

local function getAvailableBinaryStatusForRenderInfo( renderInfo )

	if renderInfo.master or renderInfo.originalPath then

		return 'has_master'

	end

	if renderInfo.proxyPath or #renderInfo.proxies > 0 then

		return 'has_proxy'

	end

	if renderInfo.createFullRenditionURITemplate or
			renderInfo.downloadFullRenditionURI then

		return 'full_size_rendition_only'

	end
	
	if renderInfo.willNotUploadMaster and renderInfo.renditions then
		for _, renditionInfo in ipairs( renderInfo.renditions ) do
			if renditionInfo.size == '2048' then
				return '2048_rendition_only'
			end
		end
	
	end

	return 'no_binaries'

end

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

local function getAvailableBinaryStatusForDocValue( docValue )

	local renderInfo = extractRenderInfoFromDocValue( docValue )

	return getAvailableBinaryStatusForRenderInfo( renderInfo )

end

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

local function noLocalNegativeAvailable( renderInfo )

	local binaryStatus = getAvailableBinaryStatusForRenderInfo( renderInfo )

	return binaryStatus ~= 'has_master' and binaryStatus ~= 'has_proxy'

end

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

local function disallowLocalEditsForDocValue( docValue )

	local renderInfo = extractRenderInfoFromDocValue( docValue )

	return renderInfo.disallowLocalEdits == true or
			noLocalNegativeAvailable( renderInfo )

end

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

return {

	extractRenderInfoFromDocValue = extractRenderInfoFromDocValue,

	maybeExtractRenderInfoFromDocValue = maybeExtractRenderInfoFromDocValue,

	getAvailableBinaryStatusForDocValue = getAvailableBinaryStatusForDocValue,

	disallowLocalEditsForDocValue = disallowLocalEditsForDocValue,

}
