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

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

NOTICE: All information contained herein is, and remains 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 are protected by all applicable
intellectual property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

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

	-- WF.core.os
local WFDeviceInfo = require 'WFDeviceInfo'

	-- WF.oz_client.catalog
local WFOzContentTypes = require 'WFOzContentTypes'
local WFCatIndexUtils =  require 'WFCatIndexUtils'
local WFCatFileSpec = require 'WFCatFileSpec'
local OzCatalogUploadStatus = require 'OzCatalogUploadStatus'

	-- WF.core.os
local KSPathUtils = require "KSPathUtils"
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.core.logging
local WFLogger = require "WFLogger"
	-- WF.lrmobile
local LrmConfig = require 'LrmConfig'

local WFCatTrashUtils = require 'WFCatTrashUtils'

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

local derefOpt = KSTableUtils.derefOpt

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

LrmConfig.registerBinaryStrategyCheck()

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

local LrmAssetUtils = {}

local logger = WFLogger "LrmCatUserModel"

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

function LrmAssetUtils.getLocalUrl( docValue )

	local localUrl = derefOpt( docValue, '_localOnly', 'localUrl' )
			
	if not localUrl then
	
		local deviceId = WFDeviceInfo.uniqueDeviceId()
		
		local importSourceDeviceId = derefOpt( docValue, 'importSource', 'uniqueDeviceId' )
		local localAssetId = derefOpt( docValue, 'importSource', 'localAssetId' )
		
		if localAssetId and ( importSourceDeviceId == deviceId ) then
		
			localUrl = localAssetId
			
		end
		
	end
	
	return localUrl
	
end

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

function LrmAssetUtils.hasProxyOnOz( docValue )

	local proxy_href = docValue
				and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/proxy_type/" ..  WFCapabilities.callFilters( "OzCatalogService.defaultProxyType", 2560 ) ]
				and docValue._serverProvidedMetadata.links[ "/rels/proxy_type/" .. WFCapabilities.callFilters( "OzCatalogService.defaultProxyType", 2560 ) ].href

	-- another preference: Fetch url from trash section of localOnly
	if not proxy_href then

		-- If OzUrl is not present in spm/links, It can happen for restored assets in expired account state or paused sync state, then fetch it from localOnly section

		local proxyLinkKey = '/rels/proxy_type/' .. WFCapabilities.callFilters( "OzCatalogService.defaultProxyType", 2560 )
		local WFCatTrashUtils_getOriginalAssetUrlForLinkKey = assert( WFCatTrashUtils.getOriginalAssetUrlForLinkKey )
		local proxyLink = WFCatTrashUtils_getOriginalAssetUrlForLinkKey( docValue, proxyLinkKey )

		proxy_href = proxyLink

	end

	if proxy_href then
		return true
	end

	return false
	
end

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

function LrmAssetUtils.hasProxyLocally( docValue )
	
	local localPath
	if WFPlatformInfo.isiOS then
		localPath = derefOpt( docValue, '_localOnly', 'files', 'proxy', 'path' )
	else
		localPath = derefOpt( docValue, '_localOnly', 'proxy', 'filePath' )
	end
	if localPath then
		return true
	end
	
	return false
	
end

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

function LrmAssetUtils.getProxyPath( docValue )

	-- LrmAssetUtils and WFCatAssetUtils files may be consolidated in future

	local proxyPath
	
	if WFPlatformInfo.isiOS then
		proxyPath = derefOpt( docValue, '_localOnly', 'files', 'proxy', 'path' )
	else
		proxyPath = derefOpt( docValue, '_localOnly', 'proxy', 'filePath' )
	end

	return proxyPath

end

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

function LrmAssetUtils.getMasterPath( docValue )

	local masterPath
	
	if WFPlatformInfo.isiOS then
	
		masterPath = derefOpt( docValue, '_localOnly', 'files', 'master', 'path' ) or
			derefOpt( docValue, '_localOnly', 'files', 'latestMaster', 'path' )

	else
	
		masterPath = derefOpt( docValue, '_localOnly', 'originalFilePath' )
		if not masterPath then
			masterPath = derefOpt( docValue, '_localOnly', 'latestOriginal', 'filePath' )
		end
		
		masterPath = OzCatalogUploadStatus.checkAndCorrectLocalPath( masterPath )
	end

	return masterPath
end

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

function LrmAssetUtils.hasProxy( docValue )

	local hasProxy = LrmAssetUtils.hasProxyOnOz( docValue )
	
	if not hasProxy then
		hasProxy = LrmAssetUtils.hasProxyLocally( docValue )
	end
	
	return hasProxy
	
end

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

function LrmAssetUtils.hasMasterOnOz( docValue )

	local master_href = derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'href' )

	-- another preference: Fetch url from trash section of localOnly
	if not master_href then

		-- If OzUrl is not present in spm/links, It can happen for restored assets in expired account state or paused sync state, then fetch it from localOnly section

		local masterLinkKey = '/rels/master'
		local WFCatTrashUtils_getOriginalAssetUrlForLinkKey = assert( WFCatTrashUtils.getOriginalAssetUrlForLinkKey )
		local masterLink = WFCatTrashUtils_getOriginalAssetUrlForLinkKey( docValue, masterLinkKey)

		master_href = masterLink

	end
	
	local isSpaceAsset = not not docValue._spaceId
	local isSpaceAssetWithImpliedMaster =  isSpaceAsset and derefOpt( docValue, '_serverProvidedMetadata',
											'links',
											'/rels/master',
											'exists' ) == true
											 

	if master_href or isSpaceAssetWithImpliedMaster then
		return true
	end
	
	return false
	
end

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

function LrmAssetUtils.hasDownloadableMasterOnOz( docValue )

	local master_href = docValue
				and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/master" ]
				and docValue._serverProvidedMetadata.links[ "/rels/master" ].href

	-- another preference: Fetch url from trash section of localOnly
	if not master_href then

		-- If OzUrl is not present in spm/links, It can happen for restored assets in expired account state or paused sync state, then fetch it from localOnly section

		local masterLinkKey = '/rels/master'
		local WFCatTrashUtils_getOriginalAssetUrlForLinkKey = assert( WFCatTrashUtils.getOriginalAssetUrlForLinkKey )
		local masterLink = WFCatTrashUtils_getOriginalAssetUrlForLinkKey( docValue, masterLinkKey)

		master_href = masterLink

	end

	return not not master_href

end

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

function LrmAssetUtils.hasMasterLocally( docValue )


	local localPath

	if WFPlatformInfo.isiOS then
		localPath = derefOpt( docValue, '_localOnly', 'files', 'master', 'path' ) or
			derefOpt( docValue, '_localOnly', 'files', 'latestMaster', 'path' )
	else
		localPath = derefOpt( docValue, '_localOnly', 'originalFilePath' ) or
			derefOpt( docValue, '_localOnly', 'latestOriginal', 'filePath' )
	end

	if localPath then
		return true
	end

	return false
	
end

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

function LrmAssetUtils.hasMasterForDcpLocally( docValue )


	local localPath

	if WFPlatformInfo.isiOS then
		localPath = derefOpt( docValue, '_localOnly', 'files', 'presetProfile', 'path' )
	else
		localPath = derefOpt( docValue, '_localOnly', 'files', 'original', 'master', 'path' )
	end

	if localPath then
		return true
	end

	return false

end

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

function LrmAssetUtils.getDcpProfilePath( docValue )


	local localPath

	if WFPlatformInfo.isiOS then
		localPath = derefOpt( docValue, '_localOnly', 'files', 'presetProfile', 'path' )
	else
		localPath = derefOpt( docValue, '_localOnly', 'files', 'original', 'master', 'path' )
	end

	return localPath

end

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

function LrmAssetUtils.hasMaster( docValue )

	local hasMaster = LrmAssetUtils.hasMasterOnOz( docValue )
	
	if not hasMaster then
		hasMaster = LrmAssetUtils.hasMasterLocally( docValue )
	end
	
	return hasMaster
	
end

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

function LrmAssetUtils.hasPreviewLocally( docValue )

	local hasPreview
	
	if WFPlatformInfo.isiOS then
		hasPreview = derefOpt( docValue, '_localOnly', 'files', 'preview' )
	else
		hasPreview = derefOpt( docValue, '_localOnly', 'preview' )
	end

	if hasPreview then
		return true
	else
		return false
	end
	
end

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

function LrmAssetUtils.hasThumbnailLocally( docValue )

	local hasThumbnail

	if WFPlatformInfo.isiOS then
		hasThumbnail = derefOpt( docValue, '_localOnly', 'files', 'thumbnail2x' )
	else
		hasThumbnail = derefOpt( docValue, '_localOnly', 'thumbnail2x' )
	end

	if hasThumbnail then
		return true
	else
		return false
	end
	
end

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

function LrmAssetUtils.isVideo( docValue )

	local video = docValue.type == 'asset' and docValue.subtype == 'video'
	
	if video then
		return true
	else
		return false
	end
	
end

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

function LrmAssetUtils.isStack( docValue )

	return not not ( docValue.subtype == 'stack' )

end

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

function LrmAssetUtils.getContentTypeNumber( docValue )

	local contentTypeNumber = 0
		
	local contentType = derefOpt( docValue, 'importSource', 'contentType' )
		or derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'content_type' )
	if contentType then
		contentTypeNumber = WFOzContentTypes.getContentTypeNumberForContentType( contentType )
	else
		local subtype = derefOpt( docValue, '_serverProvidedMetadata', 'subtype' )
		contentTypeNumber = WFOzContentTypes.getContentTypeNumberRangeForSubType( subtype )
	end
	
	return contentTypeNumber
	
end

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

function LrmAssetUtils.isVideoContentTypeNumber( contentTypeNumber )

	-- if we don't get a valid content type number we fall back to image
	
	if contentTypeNumber then
	
		local videoLow, videoHigh = WFOzContentTypes.getContentTypeNumberRangeForSubType( 'video' )
			
		if contentTypeNumber >= videoLow and contentTypeNumber <= videoHigh then
		
			return true
			
		end
		
	end
	
	return false

end

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

function LrmAssetUtils.getXmp( assetInfo )

	local xmp = assetInfo.xmp or {}
	local copyright = KSTableUtils.derefOpt( xmp, "dc", "rights" )
	local exposureTime = KSTableUtils.derefOpt( xmp, "exif", "ExposureTime" )
	local fnumber = KSTableUtils.derefOpt( xmp, "exif", "FNumber" )
	local apertureValue = KSTableUtils.derefOpt( xmp, "exif", "ApertureValue" )
	local focalLength = KSTableUtils.derefOpt( xmp, "exif", "FocalLength" )
	local isoSpeedRatings = KSTableUtils.derefOpt( xmp, "exif", "ISOSpeedRatings" )
	local flashFired = KSTableUtils.derefOpt( xmp, "exif", "FlashFired" )
	local make = KSTableUtils.derefOpt( xmp, "tiff", "Make" )
	local model = KSTableUtils.derefOpt( xmp, "tiff", "Model" )
	local lens = KSTableUtils.derefOpt( xmp, "aux", "Lens" )
	
	-- exposure
	if exposureTime and type( exposureTime ) == 'table' and exposureTime[ 1 ] ~= nil then
		local expTime = exposureTime[ 1 ]
		if #exposureTime == 2 then
			local denominator = exposureTime[ 2 ]
			if denominator ~= 1 then
				if expTime == 1 then
					expTime = expTime .. "/" .. exposureTime[2]
				else
					if denominator ~= 0 then
						expTime = string.format ( "%.1f", expTime / denominator )
					end
				end
			end
		end
		exposureTime = tostring( expTime )
	else
		exposureTime = ""
	end
	
	-- f number
	if fnumber and type( fnumber ) == 'table' and fnumber[ 1 ] ~= nil then
		local denominator = 1
		if #fnumber == 2 and fnumber[ 2 ] ~= 0 then
			denominator = fnumber[ 2 ]
		end
		
		fnumber = string.format( "%.1f", fnumber[1] / denominator )
	else
		fnumber = ""
	end
	
	-- aperture
	if apertureValue and type( apertureValue ) == 'table' and apertureValue[ 1 ] ~= nil then
		local denominator = 1
		if #apertureValue == 2 and apertureValue[ 2 ] ~= 0 then
			denominator = apertureValue[ 2 ]
		end
		apertureValue = string.format( "%.1f", apertureValue[ 1 ] / denominator )
	else
		apertureValue = ""
	end
	
	-- focal length
	if focalLength and type( focalLength ) == 'table' and focalLength[ 1 ] ~= nil then
		local denominator = 1
		if #focalLength == 2 and focalLength[ 2 ] ~= 0 then
			denominator = focalLength[ 2 ]
		end
		local focalLengthValue = focalLength[1] / denominator;

		local _, fractionalPart = math.modf(focalLengthValue)
		if fractionalPart == 0 then
			focalLength = string.format( "%.0f",  focalLengthValue )
		else
			focalLength = string.format( "%.2f",  focalLengthValue )
		end
	else
		focalLength = ""
	end
	
	-- iso speed ratings
	local iso = ""
	if isoSpeedRatings and type( isoSpeedRatings ) == 'number' then
		iso = tostring( isoSpeedRatings )
	end

	-- flash fired
	local flash = false
	if flashFired and flashFired == true then
		flash = true
	end

	local xmpTable = {
		copyright = copyright,
		exposureTime = exposureTime,
		fnumber = fnumber,
		apertureValue = apertureValue,
		focalLength = focalLength,
		isoSpeedRatings = iso,
		flashFired = flash,
		cameraMake = make,
		cameraModel = model,
		make = make,
		model = model,
		lens = lens,
	}
	
	return xmpTable
	
end

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

-- This method is no longer applicable to LriOS as the revId is no longer stored with
-- the rendition files on this platform.
-- But it makes a certain test (LrmAssetInfoModelTest.testCustomData) usuable
-- on both Android and iOS, so for now we let it stand and just log a deprecation message.

function LrmAssetUtils.getRenditionRevision( docValue, ozRenditionName )

	local localDescription

	if WFPlatformInfo.isiOS then
		logger:warnf("LrmAssetUtils.getRenditionRevision() is deprecated on iOS")
		localDescription = KSTableUtils.derefOpt( docValue, '_localOnly', 'files', ozRenditionName  )
	else
		localDescription = KSTableUtils.derefOpt( docValue, '_localOnly', ozRenditionName  )
	end

	local localRenditionIsForRev = localDescription and localDescription.revId
	
	return localRenditionIsForRev
	
end

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

function LrmAssetUtils.getLensProfileInfo( docValue )

	local info = KSTableUtils.derefOpt( docValue, 'develop', 'profiles', 'lens'  )
	if info and info.href and info.filename and string.sub( info.href, 1, 1 ) == "/" then
	
		local masterUrl = info.href
		return {
			filename = info.filename,
			href = masterUrl,
			uniqueId = info.href
		}
	end
	
	return nil
	
end

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

function LrmAssetUtils.getCameraProfileInfo( docValue )

	local info = KSTableUtils.derefOpt( docValue, 'develop', 'profiles', 'camera'  )
	if info and info.href and info.filename and string.sub( info.href, 1, 1 ) == "/" then
	
		local masterUrl = info.href
		return {
			filename = info.filename,
			href = masterUrl,
			uniqueId = info.href
		}
	end
	
	return nil
	
end

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

function LrmAssetUtils.isDeveloped( docValue )

	if WFPlatformInfo.isiOS then
		error('API not available on iOS.')
	end

	local xmpCameraRaw = KSTableUtils.derefOpt( docValue, 'develop', 'xmpCameraRaw' )
	
	if xmpCameraRaw and (type(xmpCameraRaw) == 'table' or xmpCameraRaw ~= "") then
		return true
	end
	
	return false
	
end

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

function LrmAssetUtils.hasExternalXmpDevelop( docValue )

	local xmpCameraRaw = KSTableUtils.derefOpt( docValue, 'develop', 'xmpCameraRaw' )
	
	if xmpCameraRaw and type(xmpCameraRaw) == 'table' and xmpCameraRaw.sha256 then
		return true
	end
	
	return false
	
end

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

function LrmAssetUtils.hasDownloadableExternalXmpDevelop( docValue )

	if not LrmAssetUtils.hasExternalXmpDevelop( docValue ) then
		return false
	end

	local spmXmpDevelop = KSTableUtils.derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop' )

	if spmXmpDevelop and type(spmXmpDevelop) == 'table' and spmXmpDevelop.href then
		return true
	end

	return false

end

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

function LrmAssetUtils.hasExternalXmpDevelopLocally( docValue )

	local xmpCameraRawSha256 = KSTableUtils.derefOpt( docValue, 'develop', 'xmpCameraRaw', 'sha256' )

	local xmpLocalFiles = KSTableUtils.derefOpt( docValue, '_localOnly', 'files', 'xmp_develop' )
	
	local contentidOrSha256

	if WFPlatformInfo.isiOS then
		if xmpLocalFiles and xmpLocalFiles.contentId then
			contentidOrSha256 = xmpLocalFiles.contentId
		end
	else
		if xmpLocalFiles and xmpLocalFiles.sha256 then
			contentidOrSha256 = xmpLocalFiles.sha256
		end
	end

	if xmpCameraRawSha256 and contentidOrSha256 and xmpLocalFiles and xmpLocalFiles.path
	  and contentidOrSha256 == xmpCameraRawSha256 then
		return true
	end
	
	return false
	
end

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

function LrmAssetUtils.getDefaultAssetRating( docValue )

	local rating = 0
	local ratings = KSTableUtils.derefOpt( docValue, 'ratings' )
	if ratings and type( ratings ) == 'table' then
		for k, v in pairs( ratings ) do
			if v  and type( v ) == 'table' then
				if #k == 32 then
					if v.rating ~= nil then
						rating = v.rating
					end
					break
				end
			end
		end
	end
	
	return rating
	
end

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

function LrmAssetUtils.getDefaultAssetReview( docValue )

	local flag = 'unflagged'
	local reviews = KSTableUtils.derefOpt( docValue, 'reviews' );
	if reviews and type( reviews ) == 'table' then
		for k, v in pairs( reviews ) do
			if v  and type( v ) == 'table' then
				if #k == 32 then
					if v.flag ~= nil then
						flag = v.flag
					end
					break
				end
			end
		end
	end
	
	return flag
	
end

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

function LrmAssetUtils.getCapturedWithAdobeCamera( docValue )

	-- LrmiOS, LrA: release 5.0.0 introduced the 'IsLrCameraCapture' field
	local isCapturedWithAdobeCameraFlag = derefOpt( docValue, 'xmp', 'aux', 'IsLrCameraCapture' )

	if ( isCapturedWithAdobeCameraFlag  ) then
		return true
	else
		-- Legacy assets (images captured with releases < 5.0.0):
		-- various values of the xmp.tiff.CreatorTool field were used over time
		-- to indicate Adobe Camera Captured Photos.

		-- Known historical CreatorTool values:
		--		'Adobe Camera 1.1':		LriOS version < 4.4.0
		-- 		'Thio':					LriOS (historical value)
		--      'Lightroom CC (iOS)':	HDR and Long Exposure
		-- 		'Lightroom'				LrA 4.3.0, LrA 4.4.0, LriOS 4.4.0

		-- For reference: current CreatoreTool value:
		--		'Adobe Lightroom x.y.z (p)': LriOS, LrA version >= 5.0.0
		--      	x.y.z: version number
		--			p: platform (iOS, Android, etc...)

		-- Note: the string 'Adobe Lightroom' can't be used to indicate an Adobe Camera captured
		-- image (release >= 5.0.0) because it is also used for images edited with Photoshop from
		-- within LrDesktop (LRMI-4193702).

		local creatorTool = derefOpt( docValue, 'xmp', 'xmp', 'CreatorTool' )

	    if creatorTool and type( creatorTool ) == 'string' and (
			string.find( creatorTool, 'Adobe Camera' ) == 1 or	-- LriOS version < 4.4.0
			string.find( creatorTool, 'Thio' ) == 1 or			-- LriOS (historical value)
			string.find( creatorTool, 'Lightroom' ) == 1		-- HDR, Long Exposure, LrA 4.3.0, 4.4.0, LriOS 4.4.0
		) then
			return true
		end
	end

	return false
end

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

function LrmAssetUtils.doesAssetNeedMasterUploaded( docValue )

	local hasMasterUrl = LrmAssetUtils.hasMasterOnOz( docValue )
	local hasLocalOriginal = LrmAssetUtils.hasMasterLocally( docValue )
	local isOzOriginalInvalid = not not derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/master', 'invalid' )
	
	local needsUpload = false

	if hasLocalOriginal and not hasMasterUrl then

		needsUpload = true

	elseif hasLocalOriginal and hasMasterUrl and isOzOriginalInvalid then

		needsUpload = true

	end

	return needsUpload

end

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

function LrmAssetUtils.doesAssetNeedDevelopXmpUploaded( docValue )

	local hasXmpDevelopUrl = not not derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop', 'href' )
	local hasDevelopXmpLocally = LrmAssetUtils.hasExternalXmpDevelopLocally( docValue )
	local isXmpDevelopInvalid = not not derefOpt( docValue, '_serverProvidedMetadata', 'links', '/rels/xmp/develop', 'invalid' )

	local isSpaceAsset = not not docValue._spaceId
	local isSpaceAssetWithImpliedXmpDevelop = isSpaceAsset and derefOpt( docValue, '_serverProvidedMetadata',
											 'links',
											 '/rels/xmp/develop',
											 'exists' ) == true
	
	local ozHasXmpDevelop = hasXmpDevelopUrl or isSpaceAssetWithImpliedXmpDevelop
	
	local needsUpload = false

	if  hasDevelopXmpLocally and not ozHasXmpDevelop then

		needsUpload = true

	elseif hasDevelopXmpLocally and ozHasXmpDevelop and isXmpDevelopInvalid then

		needsUpload = true

	end

	return needsUpload

end

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

function LrmAssetUtils.doesAssetNeedProxyOrMasterDownloaded( docValue )

	local hasLocalOriginal = LrmAssetUtils.hasMasterLocally( docValue )
	local hasLocalProxy = LrmAssetUtils.hasPreviewLocally( docValue )

	local needsDownload = true

	if hasLocalOriginal or hasLocalProxy then
		needsDownload = false
	end

	return needsDownload

end

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

function LrmAssetUtils.doesAssetNeedDevelopXmpDownloaded( docValue )

	local needsDownload = false

	if LrmAssetUtils.hasExternalXmpDevelop( docValue )
				and not LrmAssetUtils.hasExternalXmpDevelopLocally( docValue ) then

		return true

	end

	return needsDownload

end

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

function LrmAssetUtils.getImportSourceCameraAndLensName( docValue )

	-- These values will be empty for old assets. We might want to default them here.

	local importSourceCameraName = derefOpt( docValue, 'importSource', 'camera', 'name' )
	local importSourceLensName = derefOpt( docValue, 'importSource', 'lens', 'name' )
	return importSourceCameraName, importSourceLensName

end

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

function LrmAssetUtils.preferableBinaryForDevelop( docValue, binaryPreference )

	WFArguments.check( 'table', 'binaryStrategy' )
	
	local hasProxy = LrmAssetUtils.hasProxy( docValue )
	local hasMaster = LrmAssetUtils.hasMaster( docValue )
	
	local localUrl = KSTableUtils.derefOpt( docValue, '_localOnly', 'localUrl' )
	local hasProxyLocally = LrmAssetUtils.hasProxyLocally( docValue )
	local hasMasterLocally = LrmAssetUtils.hasMasterLocally( docValue )
	
	local hasLocalBinary = hasProxyLocally or hasMasterLocally
	
	local useBinaryFromCameraRoll = localUrl and not hasLocalBinary
	
	if useBinaryFromCameraRoll then
		return LrmConfig.StrategyPreferMaster
	end
	
	if ( hasProxy and binaryPreference == LrmConfig.StrategyPreferProxy ) or 
		( binaryPreference == LrmConfig.StrategyPreferMaster and not hasMaster ) then
		
		return LrmConfig.StrategyPreferProxy
		
	else
	
		return LrmConfig.StrategyPreferMaster
		
	end
	
end

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

function LrmAssetUtils.hasLocalProxyOrOriginal( docValue )

	return LrmAssetUtils.hasMasterLocally( docValue ) or 
		LrmAssetUtils.hasProxyLocally( docValue )

end

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

function LrmAssetUtils.createFullPath( catalogBasePath, localFilePath, binaryType )

	local fileFullPath
	
	if WFPlatformInfo.isAndroid then
	
		-- if binaryType is preset_master or master or watermark_original and if localFilePath is
		-- just a relative path then prefix with catalogBasePath
		if ( binaryType == 'preset_master' or 
			( binaryType == 'master' or binaryType == 'watermark_original' ) and
			localFilePath and
			string.find(localFilePath, 'carouselDocuments') == nil and
			string.find(localFilePath, 'carouselCaches') == nil ) then
			
			fileFullPath = catalogBasePath and localFilePath and KSPathUtils.child( catalogBasePath, localFilePath )
		
		-- if binaryType is master and if localFilePath is nil or
		-- localFilePath contains carouselDocuments or carouselCaches then it needs correction.
		elseif binaryType == 'master' then
		
			fileFullPath = OzCatalogUploadStatus.checkAndCorrectLocalPath( localFilePath )
			
		else
		
			fileFullPath = localFilePath
			
		end
		
	elseif WFPlatformInfo.isiOS then
	
		fileFullPath = catalogBasePath and localFilePath and KSPathUtils.child( catalogBasePath, localFilePath )
		
	end

	return fileFullPath
end

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

function LrmAssetUtils.hasPresetLocally( docValue )

	--- Both IOS and Android uses same path
	local localPath
	local presetProfileFile = derefOpt( docValue, '_localOnly', 'files', 'presetProfile')
	
	if presetProfileFile and not presetProfileFile.uploadStatus then
		localPath = presetProfileFile.path
	end

	if localPath then
		return true
	end

	return false

end

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

function LrmAssetUtils.getPresetPath( docValue )

	--- Both IOS and Android uses same path
	local localPath
	local presetProfileFile = derefOpt( docValue, '_localOnly', 'files', 'presetProfile')

	if presetProfileFile and not presetProfileFile.uploadStatus then
		localPath = presetProfileFile.path
	end

	return localPath

end

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

function LrmAssetUtils.isValidPreset( docValue )

	if docValue and not docValue._deleted then
		if docValue.type == "asset" and docValue.subtype == "preset" then
			return true
		end
	end
	return false

end

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

function LrmAssetUtils.isValidProfile( docValue )

	if docValue and not docValue._deleted then
		if docValue.type == "asset" and docValue.subtype == "profile" then
			return true
		end
	end
	return false

end

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

function LrmAssetUtils.buildOriginalPathFromDocValue( docValue )

	local originalPath

	if LrmAssetUtils.isValidPreset( docValue ) or LrmAssetUtils.isValidProfile( docValue ) then
		originalPath = LrmAssetUtils.getPresetPath( docValue )
	elseif WFCatIndexUtils.isNonDeletedDCPDocument( docValue ) then
		originalPath = LrmAssetUtils.getDcpProfilePath( docValue )
	else
		originalPath = LrmAssetUtils.getMasterPath( docValue )
	end

	return originalPath

end

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

function LrmAssetUtils.getlocalOriginalSha256( docValue )

	--- Both IOS and Android uses same path
	local localOriginalSha256

	if LrmAssetUtils.isValidPreset( docValue ) and LrmAssetUtils.hasPresetLocally( docValue ) then
		localOriginalSha256 = derefOpt( docValue, '_localOnly', 'files', 'presetProfile', 'sha256' )
	elseif LrmAssetUtils.isValidProfile( docValue ) then
		localOriginalSha256 = derefOpt( docValue, '_localOnly', 'files', 'presetProfile', 'sha256' )
	elseif WFCatIndexUtils.isNonDeletedDCPDocument( docValue ) then
		localOriginalSha256 = derefOpt( docValue, '_localOnly', 'files', 'original', 'master', 'sha256' )
	else
		localOriginalSha256 = WFCatFileSpec and WFCatFileSpec.getLocalOriginalSha256WithFallback( docValue )
	end

	return localOriginalSha256

end

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

function LrmAssetUtils.doesAssetDocumentHaveLocalBinary( docValue )

	return 
			( LrmAssetUtils.isValidPreset( docValue ) and 
			LrmAssetUtils.hasPresetLocally( docValue ) )
		or
			(LrmAssetUtils.isValidProfile( docValue ) and
			LrmAssetUtils.hasPresetLocally( docValue ) )
		or
			( WFCatIndexUtils.isValidAssetDocument( docValue ) and
			LrmAssetUtils.hasLocalProxyOrOriginal( docValue ) )
		or
			( WFCatIndexUtils.isNonDeletedStyleDocument( docValue ) and
			LrmAssetUtils.hasMasterLocally( docValue ) )
		or
			( WFCatIndexUtils.isNonDeletedDCPDocument( docValue ) and
			LrmAssetUtils.hasMasterForDcpLocally( docValue ) )

end

-----------------------------------------------------------------------------------
return LrmAssetUtils
