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

OzCatalogUploadStatus.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2011 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.

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

	-- WF.core.logging
local WFLogger = require 'WFLogger'
local KSTableUtils = require 'KSTableUtils'

	-- WF.core.utils
local KSPathUtils = require 'KSPathUtils'

local WFOzSyncConstants = require "WFOzSyncConstants"
local WFCapabilities = require 'WFCapabilities'
local WFOzContentTypes = require 'WFOzContentTypes'

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

local derefOpt = KSTableUtils.derefOpt

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

local logger = WFLogger 'OzCatalogUploadStatus'
--	:setLevel 'DEBUG'

local usingWFDocStore = WFCapabilities.getCapability 'OzDocStore.disable'

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

local OzCatalogUploadStatus = {}

local WFPlatformInfo = require 'WFPlatformInfo'

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

local kRevisionStartIndex = 0 - ( 32 + # "/renditions/" + 32 )
local kRevisionEndIndex = 0 - ( # "/renditions/" + 32 + 1 )

assert( kRevisionStartIndex == -76, "Revision String Substitution Index May be incorrect" )
assert( kRevisionEndIndex == -45, "Revision String Substitution Ending Index May be incorrect" )

local kUUIDPattern = "^/var/mobile/Applications/([a-fA-F0-9-]+)/"
local kUUIDPatternIOS8 = "^/var/mobile/Containers/Data/Application/([a-fA-F0-9-]+)/"

local kUUIDPatternSimulator = "/Applications/([a-fA-F0-9-]+)/"
local kUUIDPatternIOS8Simulator = "/Containers/Data/Application/([a-fA-F0-9-]+)/"


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

local function isRawContentType( docValue )

	local contentType = derefOpt( docValue, 'importSource', 'contentType' )
	
	return WFOzContentTypes.isRawImage( contentType )
	
end

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

function OzCatalogUploadStatus.shouldUploadRendition( devSettingsLastChanged, localDescription,
					serverLinksForRendition )

	-- Don't upload if we're not current.

	-- FIXME [scouten 2011-07-29]: We need Oz changes to avoid a possible race
	-- condition. If the server state advances quickly enough that we don't tag the
	-- develop change as coming from our rev, then we will never think we're
	-- current in that sense, and never upload the renditions. Obscure, but possible,
	-- race condition. (MattA and I discussed since this and agreed that leaving
	-- thumbnails out of non-current nodes in the revision history isn't a fatal
	-- flaw.)

	local localRenditionIsForRev = localDescription and localDescription.revId
	
	if localRenditionIsForRev == 'pending' and not serverLinksForRendition then
		return true
	end
	
	if not localRenditionIsForRev
		or devSettingsLastChanged ~= localRenditionIsForRev
		or not localDescription
		or localDescription.md5 == 'missing'
		or localDescription.md5 == nil
	then
		return false
	end

	-- We're current. Does Oz need this rendition?
	
	if not serverLinksForRendition then return true end
	
	if serverLinksForRendition.invalid then return true end
	
	if type( serverLinksForRendition.href ) == 'string' and
			string.sub( serverLinksForRendition.href, kRevisionStartIndex,kRevisionEndIndex ) ~= localDescription.revId then
		return true
	end
	
	return false

end

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

function OzCatalogUploadStatus.calculateUploadStatus( docValue )
	
	local firstPassWithAsset = false
	local docType = docValue._docType
	local uploadedRollup = docValue._ozMetadataUploadStatus
	local localOnly = docValue._localOnly or {}
	
	if localOnly._uploadedValues then
		uploadedRollup = 'uploaded'
	end

	if not uploadedRollup then
		if docType == 'asset' then
			firstPassWithAsset = true
		end
		uploadedRollup = 'notUploaded'
	end
	
	local originalUploadedRollup = uploadedRollup

	if (
			-- TODO [arahn 2012-10-11]: We should consolidate the list of
			-- supported, generic document types, so that this if statement
			-- doesn't need to be updated everytime we start handling
			-- a new kind of document.
			docType == 'asset' or
			docType == 'album' or
			docType == 'album_asset' or
			docType == 'space' or
			docType == 'comment' or
			docType == 'spacefavorite'
		
		) and docValue._deleted and WFOzSyncConstants.isNotUploaded( originalUploadedRollup ) then
	
		-- This is a document we need to delete
		uploadedRollup = 'deleted'

	elseif docValue._deleted then
		
		-- original uploaded rollup stands; no rendition uploads permitted

	elseif docValue._conflict then

		logger:infof( "detected conflict on %s %s :: _conflict = %s",
							docType, docValue._docId, docValue._conflict )

		uploadedRollup = '_conflict'

	elseif localOnly.oz_last_error_status == '415' then

		logger:infof( "detected that oz rejected document on %s %s",
							docType, docValue._docId )

		uploadedRollup = '_conflict'

	end

	if uploadedRollup == 'notUploaded' and firstPassWithAsset then
		uploadedRollup = 'notUploaded_metadataOnly'
	end

	logger:debugf( "ozUploadStatus rollup:\n\n  original rollup: %s\n\nuploaded: %s\n",
		originalUploadedRollup, uploadedRollup )
	logger:debugf( "\n  docValue: %s\n\n", docValue )

	return uploadedRollup

end

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

function OzCatalogUploadStatus.assetOriginalIsValid( docValue )

	local ozUrl = docValue
				and docValue._localOnly
				and docValue._localOnly.original
				and docValue._localOnly.original.ozUrl
				
	local spm_master_href = docValue
				and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/master" ]
				and docValue._serverProvidedMetadata.links[ "/rels/master" ].href
				
	local spm_first_master = docValue
			    and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/first_master" ]
				and docValue._serverProvidedMetadata.links[ "/rels/first_master" ].href
		
	
	-- if we don't have server metadata this original hasn't been uploaded yet and
	-- we return true
	
	if not spm_master_href then
		return true
	end
	-- If there is a value for the URL this original was downloaded from, we compare its value to
	-- the value from the server for the master
	if ozUrl then
	
		-- jrechtie: 2017/01/19
		-- LRMI-4187104: due to a botched merge in Dec. 2016, ozUrls for non JPG assets
		-- imported from the camera roll and uploaded to Oz, might be in (broken) V1 format.
		-- Patch broken entries stored between the V2 migration and LrM version 3.1.0 (inclusive).

		-- Should we fix these entries in the docStore?
		if ozUrl:sub(1,5) == '/v1.0' then
			ozUrl = ozUrl:gsub( '^/v1.0', '/v2' )
			local _, cutoff = ozUrl:find( 'master' )
			ozUrl = ozUrl:sub(1, cutoff)
		end

		local isLocalMasterValid = ozUrl == spm_master_href
		
		if isRawContentType( docValue ) then
			return true
		end
		
		return isLocalMasterValid
		
	elseif spm_first_master then
		return spm_master_href == spm_first_master
	else
		return true
	end
				
end

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

function OzCatalogUploadStatus.assetLatestOriginalIsValid( docValue )

	local ozUrl_latestOriginal = docValue
				and docValue._localOnly
				and docValue._localOnly.latestOriginal
				and docValue._localOnly.latestOriginal.ozUrl
				
	local spm_master_href = docValue
				and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/master" ]
				and docValue._serverProvidedMetadata.links[ "/rels/master" ].href

	
		
	if ozUrl_latestOriginal and isRawContentType( docValue ) then
		return true
	end
	
	-- if the URL we downloaded the latest original from matches the master
	-- it is the latest
	
	return ozUrl_latestOriginal == spm_master_href
		
end

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

-- FIXME [sdaniel 2012-02-07]: Needs to check whether proxy corresponds to latest original (check by revision?)

function OzCatalogUploadStatus.assetProxyIsValid( docValue, --[[optional string]] proxySize )

	proxySize = proxySize or 2048
	
	local proxy
	
	if usingWFDocStore then
		proxy = derefOpt( docValue, '_localOnly', 'proxy', tostring( proxySize ) )
	else
		proxy = derefOpt( docValue, '_localOnly', 'proxy' )
	end
	
	local localPath = proxy and proxy.filePath
	local ozUrl = proxy and proxy.ozUrl
				
	local spm_proxy_href = docValue
				and docValue._serverProvidedMetadata
				and docValue._serverProvidedMetadata.links
				and docValue._serverProvidedMetadata.links[ "/rels/proxy_type/" .. tostring( proxySize ) ]
				and docValue._serverProvidedMetadata.links[ "/rels/proxy_type/" .. tostring( proxySize ) ].href
		
	
	-- if we don't have server metadata this proxy hasn't been uploaded or generated on Oz yet and
	-- we return true if it is available locally
	
	if not spm_proxy_href and localPath then
		return true
	end
	-- If there is a value for the URL this proxy was downloaded from, we compare its value to
	-- the value from the server for the master
	if ozUrl then
		return ozUrl == spm_proxy_href
	else
		return true
	end
	
	--[[
		 Even if we (or Oz) has a proxy if someone else uploads an original with destructive edits
		then our proxy is no longer valid
	--]]
				
end

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

function OzCatalogUploadStatus.checkAndCorrectLocalPath( localPath )

	-- if the path passed in was nil, return nil
	if not localPath then return localPath end
	
	local renamedPath = localPath
	
	-- On Mac and only if the library/caches has been moved in 2.0, return the corrected path

	if WFPlatformInfo.isMac then
		if renamedPath:match( "Adobe Carousel Library.cr" ) then
			renamedPath = renamedPath:replace( "Adobe Carousel Library.cr", "Adobe Revel/Adobe Revel Library.rvl")
			
		elseif renamedPath:match( "Adobe Revel Library.cr" ) then
			renamedPath = renamedPath:replace( "Adobe Revel Library.cr", "Adobe Revel/Adobe Revel Library.rvl")
		-- For caches, substitute 'Adobe Carousel' with 'Adobe Revel'
		
		elseif renamedPath:match( "Adobe Carousel" ) then
			renamedPath = renamedPath:replace( "Adobe Carousel", "Adobe Revel")
		end
	
	elseif WFPlatformInfo.isiOS then
	
		-- Optmitization for iOS Simulator to omit the following code
				
		if renamedPath:find( "/var/mobile/" ) == 1 then
		
			-- SPAM [pkamin 2014-08-05]: Apple is so kind to change the Application UUID after an App update
			-- which breaks our model to store absolute file urls in the DocStore. The following workaround
			-- fixes the stored url until we migrate to relative urls in WF
			
			-- SPAM [pkamin 2014-10-13]: With iOS8 Apple also changed the root path where it stores app specific
			-- data. They also switch App GUIDs with every new App LAUNCH! forcing us to construct the path essentially
			-- every time
			
			local dbAppUUID = renamedPath:match( kUUIDPatternIOS8 )
			
			if dbAppUUID == nil then
				dbAppUUID = renamedPath:match( kUUIDPattern )
			end
			
			local guidLength = dbAppUUID:len()
			
			dbAppUUID = dbAppUUID:replace( "-", "%-" )
			local index = renamedPath:find( dbAppUUID )
			
			local newRelativePath = renamedPath:sub( index + guidLength )
			
			local appResourcesPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
			appResourcesPath = KSPathUtils.parent( appResourcesPath )
			
			renamedPath = KSPathUtils.child( appResourcesPath, newRelativePath )
			
		elseif renamedPath:find( '/Users/' ) == 1 then
		
			local dbAppUUID = renamedPath:match( kUUIDPatternIOS8Simulator )
			
			if dbAppUUID == nil then
				dbAppUUID = renamedPath:match( kUUIDPatternSimulator )
			end
			
			if dbAppUUID == nil then
			
				return renamedPath
				
			end
			
			local guidLength = dbAppUUID:len()
			
			dbAppUUID = dbAppUUID:replace( "-", "%-" )
			local index = renamedPath:find( dbAppUUID )
			
			local newRelativePath = renamedPath:sub( index + guidLength )
			
			local appResourcesPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
			appResourcesPath = KSPathUtils.parent( appResourcesPath )
			
			renamedPath = KSPathUtils.child( appResourcesPath, newRelativePath )
			
		end
	
    elseif WFPlatformInfo.isAndroid then

        debug.print('localPathSDSDNew=', localPath)

        if string.find(localPath, 'carouselDocuments') ~= nil then
		  local currentPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
		  local s, e = string.find(localPath, 'carouselDocuments')
		  renamedPath = currentPath .. string.sub(localPath, e+1)
		  debug.print('RenamePathSDSDDocs=', renamedPath)
        end

        if string.find(localPath, 'carouselCaches') ~= nil then
		  local currentPath = KSPathUtils.getStandardFilePath( 'carouselCaches' )
		  local s, e = string.find(localPath, 'carouselCaches')
		  renamedPath = currentPath .. string.sub(localPath, e+1)
		  debug.print('RenamePathSDSDCaches=', renamedPath)
        end
    end
	
	return renamedPath
	
end

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

return OzCatalogUploadStatus
