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

ADOBE CONFIDENTIAL
------------------
Copyright 2019 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
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.

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

	-- RxLua
local Rx = require 'Rx'
local Observable = Rx.Observable

	-- WF.core.rxlua
local WFRxScheduler = require 'WFRxScheduler'

	-- WF.core.self_test
local WFTestFileUtils = require 'WFTestFileUtils'

	--WF.core.utils
local KSData = require 'KSData'
local KSDate = require 'KSDate'
local KSIdGenerator = require 'KSIdGenerator'
local KSPathUtils = require 'KSPathUtils'
local KSTableUtils = require 'KSTableUtils'
local WFFileUtils = require 'WFFileUtils'
local WFPlatformInfo = require 'WFPlatformInfo'
local WFTableMerge = require 'WFTableMerge'

	-- WF.oz_client.catalog
local WFOzContentTypes = require 'WFOzContentTypes'

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

local child = KSPathUtils.child

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

local WFCatAssetTestUtils = {}

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

local offlinePath = '/Volumes/foo/images'
local offlineVolumeGuid = KSIdGenerator.uuidInLowercaseHex()
local offlineRelativePath = '/foo/images'

if WFPlatformInfo.isWindows then
	offlinePath = 'Q:\foo\images'
	offlineRelativePath = 'foo\images'
end
	
--------------------------------------------------------------------------------

local function createXmpSidecarFile( filePath )

	local xmpDataAsStr = '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c128 79.159124, 2016/03/18-14:01:55 (debug)">\
		   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\
		      <rdf:Description rdf:about=""\
		            xmlns:xmp="http://ns.adobe.com/xap/1.0/"\
		            xmlns:dc="http://purl.org/dc/elements/1.1/"\
		            xmlns:exif="http://ns.adobe.com/exif/1.0/"\
		            xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">\
		         <xmp:ModifyDate>2020-12-12T11:10:09.876Z</xmp:ModifyDate>\
		         <xmp:Rating>1</xmp:Rating>\
		         <xmp:CreatorTool>Adobe Nimbus Test</xmp:CreatorTool>\
		         <dc:subject>\
		            <rdf:Seq>\
		               <rdf:li>mary</rdf:li>\
		               <rdf:li>had</rdf:li>\
		               <rdf:li>a little lamb</rdf:li>\
		            </rdf:Seq>\
		         </dc:subject>\
		         <exif:GPSAltitude>10000/2</exif:GPSAltitude>\
		         <exif:GPSAltitudeRef>1</exif:GPSAltitudeRef>\
		         <exif:GPSLongitude>100,5.125S</exif:GPSLongitude>\
		         <exif:GPSLatitude>20,1.5</exif:GPSLatitude>\
		         <crs:RawFileName>IMG_9876.cr2</crs:RawFileName>\
		      </rdf:Description>\
		   </rdf:RDF>\
		</x:xmpmeta>'

	local xmpData = KSData.make( xmpDataAsStr )
	KSData.saveToFile( xmpData, filePath )
	xmpData:close()
	
	return #xmpDataAsStr

end

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

WFCatAssetTestUtils.createXmpSidecarFile = createXmpSidecarFile

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

local xmpDataAsStr = '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c128 79.159124, 2016/03/18-14:01:55 (debug)">\
		   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\
		      <rdf:Description rdf:about=""\
		            xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">\
		         <crs:RawFileName>IMG_9876.cr2</crs:RawFileName>\
		      </rdf:Description>\
		   </rdf:RDF>\
		</x:xmpmeta>'

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

local function createXmpDevelopSettings( filePath )

	local xmpData = KSData.make( xmpDataAsStr )
	KSData.saveToFile( xmpData, filePath )
	xmpData:close()
	
	return #xmpDataAsStr

end

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

WFCatAssetTestUtils.createXmpDevelopSettings = createXmpDevelopSettings

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

local tempFolderForTestName = {}

local function getDefaultLocalOnlySettings( rxTest, wfCatalog, assetId )

	local mockOriginal = KSData.make( 'original_data' )
	local mockProxy = KSData.make( 'proxy_data' )
	local mockPreview = KSData.make( 'preview_data' )
	local mockThumb = KSData.make( 'thumb_data' )
	local mockPreset = KSData.make( 'preset_data' )
	
	local customId = KSIdGenerator.uuidInLowercaseHex()
	
	local testOriginalName = string.format( "original_%s.RAW", customId )
	local testProxyName = string.format( "proxy_%s.DNG", customId )
	local testSidecareName = string.format( "sidecar%s.xmp", customId )
	local testPresetName = string.format( "preset_%s.xmp", customId )
	
	local tempFolder = tempFolderForTestName[ rxTest:testName() ]
	
	if not tempFolder then
		tempFolder = WFTestFileUtils.createTestFolder( rxTest )
	end
	
	tempFolderForTestName[ rxTest:testName() ] = tempFolder
	
	local originalFilePath = KSPathUtils.child( tempFolder, testOriginalName )
	KSData.saveToFile( mockOriginal, originalFilePath )
	local originalFileSize = WFFileUtils.fileAttributes( originalFilePath ).fileSize
	local originalContentId = KSIdGenerator.uuidInLowercaseHex()
	
	local proxyFilePath = KSPathUtils.child( tempFolder, testProxyName )
	KSData.saveToFile( mockProxy, proxyFilePath )
	local proxyFileSize = WFFileUtils.fileAttributes( proxyFilePath ).fileSize
	local proxyContentId = KSIdGenerator.uuidInLowercaseHex()
	
	local xmpSidecarFilePath = KSPathUtils.child( tempFolder, testSidecareName )
	local xmpSidecarFileSize = createXmpSidecarFile( xmpSidecarFilePath )


	local developSettingsSha256 = '3bd8b936cc646bf1296e5ba4a76c410a27beb80809f525a8f5072f0115c54ccc'
	local xmpDevelopSettingsFilePath = KSPathUtils.child( tempFolder, developSettingsSha256 )
	local xmpDevelopSettingsRelPath = xmpDevelopSettingsFilePath

	if WFPlatformInfo.isMobile() then

		local OzFilePaths = require 'OzFilePaths'

		local catalogId = wfCatalog:ozCatalogId()
	
		xmpDevelopSettingsRelPath, xmpDevelopSettingsFilePath = OzFilePaths.getXmpDevelopPath( catalogId, { _id = assetId }, developSettingsSha256, true ) 
	
	end
	
	local xmpDevelopSettingsFileSize = createXmpDevelopSettings( xmpDevelopSettingsFilePath )

	-- Note: Desktop rendition information is generated in createDesktopAsset.
	
	local previewFilePath = KSPathUtils.child( tempFolder, KSIdGenerator.uuidInLowercaseHex() )
	KSData.saveToFile( mockPreview, previewFilePath )
	local previewFileSize = WFFileUtils.fileAttributes( previewFilePath ).fileSize
	local previewData = tostring( mockPreview )
	local previewContentId = KSIdGenerator.uuidInLowercaseHex()
	
	local thumbnailFilePath = KSPathUtils.child( tempFolder, KSIdGenerator.uuidInLowercaseHex() )
	KSData.saveToFile( mockThumb, thumbnailFilePath )
	local thumbnailFileSize = WFFileUtils.fileAttributes( thumbnailFilePath ).fileSize
	local thumbnailData = tostring( mockThumb )
	local thumbnailContentId = KSIdGenerator.uuidInLowercaseHex()

	local presetFilePath = KSPathUtils.child( tempFolder, testPresetName )
	KSData.saveToFile( mockPreset, presetFilePath )
	local presetFileSize = WFFileUtils.fileAttributes( presetFilePath ).fileSize
	local presetContentId = KSIdGenerator.uuidInLowercaseHex()

	mockOriginal:close()
	mockProxy:close()
	mockPreview:close()
	mockThumb:close()
	mockPreset:close()

	rxTest:finally( function()
	
		WFFileUtils.deleteFile( originalFilePath )
		WFFileUtils.deleteFile( xmpSidecarFilePath )
		WFFileUtils.deleteFile( proxyFilePath )
		WFFileUtils.deleteFile( previewFilePath )
		WFFileUtils.deleteFile( thumbnailFilePath )
		WFFileUtils.deleteFile( xmpDevelopSettingsFilePath )
		WFFileUtils.deleteFile( presetFilePath )
		
	end )

	return {
	
		originalFilePath = originalFilePath,
		originalFileSize = originalFileSize,
		originalContentId = originalContentId,
		originalSha256 = originalContentId,
		
		xmpSidecarFilePath = xmpSidecarFilePath,
		xmpSidecarFileSize = xmpSidecarFileSize,
		
		proxyFilePath = proxyFilePath,
		proxyFileSize = proxyFileSize,
		proxyContentId = proxyContentId,
		
		previewFilePath = previewFilePath,
		previewFileSize = previewFileSize,
		previewData = previewData,
		previewContentId = previewContentId,

		thumbnailFilePath = thumbnailFilePath,
		thumbnailFileSize = thumbnailFileSize,
		thumbnailData = thumbnailData,
		thumbnailContentId = thumbnailContentId,
		
		xmpDevelopSettingsFilePath = xmpDevelopSettingsFilePath,
		xmpDevelopSettingsRelPath = xmpDevelopSettingsRelPath,
		xmpDevelopSettingsFileSize = xmpDevelopSettingsFileSize,
		developSettingsSha256 = developSettingsSha256,
		xmpDevelopSettingsString = xmpDataAsStr,
		
		presetFilePath = presetFilePath,
		presetContentId = presetContentId,
		presetFileSize = presetFileSize,

	}

end

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

local function getBaseDocValue( assetId, optOverrides, localOnlySettings )

	local xmpCameraRaw = {
		sha256 = localOnlySettings.developSettingsSha256,
	}

	if optOverrides.internalXmpCameraRaw then
		xmpCameraRaw = xmpDataAsStr
	end

	local docValue = {
	
		_id = assetId,
		type = 'asset',
		subtype = optOverrides.docSubtype or 'image',
		captureDate = KSDate.timeInIso8601Format(),
		develop = {
			croppedHeight = 1000,
			croppedWidth = 1000,
			xmpCameraRaw = xmpCameraRaw,
			userOrientation = 1,
		},
		importSource = {
			sha256 = localOnlySettings.originalContentId,
		},
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master' ] = { href = '/href/bogus/testing' },
				[ '/rels/proxy_type/2560' ] = { href = '/href/bogus/proxy' },
				[ '/rels/xmp' ] = { 
					href = '/href/bogus/xmp',
					exists = true
				}
			}
		}
	}
	
	if optOverrides.noMasterLink then
		docValue._serverProvidedMetadata.links['/rels/master'] = nil
		docValue._serverProvidedMetadata.links['/rels/proxy_type/2560'] = nil
	end

	if optOverrides.invalidMasterLink then

		KSTableUtils.setNested( docValue, true,
				'_serverProvidedMetadata',
				'links',
				'/rels/master',
				'invalid' )

	end

	if optOverrides.corruptMaster then

		KSTableUtils.setNested( docValue, "does not match",
				'importSource',
				'status',
				'error',
				'all',
				'reason',
				'sha256' )

	end
	
	if optOverrides.noXmpSidecarLink then
		docValue._serverProvidedMetadata.links['/rels/xmp'] = nil
	end
	
	return docValue
end

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

local function createDesktopAsset( rxTest, wfCatalog, assetId, localOnlySettings, optOverrides )

	optOverrides = optOverrides or {}

	local originalRelativePath = WFFileUtils.removeVolumeFromPath( localOnlySettings.originalFilePath )
	local originalVolumeGuid = WFFileUtils.getVolumeGuidForPath( localOnlySettings.originalFilePath )

	if optOverrides.localOriginalIsOnDisconnectedVolume then
	
		localOnlySettings.originalFilePath = offlinePath
		originalVolumeGuid = offlineVolumeGuid
		originalRelativePath = offlineRelativePath
		
	end
	
	local proxyRelativePath = WFFileUtils.removeVolumeFromPath( localOnlySettings.proxyFilePath )
	local proxyVolumeGuid = WFFileUtils.getVolumeGuidForPath( localOnlySettings.proxyFilePath )
	
	if optOverrides.localProxyIsOnDisconnectedVolume then
	
		localOnlySettings.proxyFilePath = offlinePath
		proxyVolumeGuid = offlineVolumeGuid
		proxyRelativePath = offlineRelativePath
	
	end

	local docValue = getBaseDocValue( assetId, optOverrides, localOnlySettings )

	docValue._localOnly = { 
	
		files = {
		
			original = {
			
				master = {
					relativePath = originalRelativePath,
					volumeGuid = originalVolumeGuid,
					size = localOnlySettings.originalFileSize,
					sha256 = localOnlySettings.originalContentId,
				},
				
				xmp = {
					path = localOnlySettings.xmpSidecarFilePath,
					fileSize = localOnlySettings.xmpSidecarFileSize,
				},
				
			},
			
			proxy = {
				[ '2560' ] = {
					relativePath = proxyRelativePath,
					volumeGuid = proxyVolumeGuid,
					size = localOnlySettings.proxyFileSize,
					sha256 = localOnlySettings.proxyContentId,
				}
			},
			
			xmp_develop = {
				path = not optOverrides.internalXmpCameraRaw and localOnlySettings.xmpDevelopSettingsRelPath or nil,
				fileSize = not optOverrides.internalXmpCameraRaw and localOnlySettings.xmpDevelopSettingsFileSize or nil,
				sha256 = localOnlySettings.developSettingsSha256,
			}
			
		},
		xmpSidecarExists = not optOverrides.localXmpSidecarDoesNotExist,
	}
	
	if optOverrides.localXmpSidecarDoesNotExist then
		docValue._localOnly.files.original.xmp = nil
	end
	
	if optOverrides.noLocalOriginal then
		docValue._localOnly.files.original = nil
	end

	if optOverrides.noLocalProxy then
		docValue._localOnly.files.proxy = nil
	end

	if optOverrides.docSubtype == "preset" and not optOverrides.noLocalOriginal then
		-- on desktop a preset binary is handled like an original
		localOnlySettings.presetFilePath = localOnlySettings.originalFilePath
		localOnlySettings.presetContentId = localOnlySettings.originalContentId
		localOnlySettings.presetFileSize = localOnlySettings.originalFileSize
	end

	-- Add in rendition information. Need the fully formed doc value in order to do this.
	
	local NmbLocalRenditionDigest = require 'NmbLocalRenditionDigest'

	local renditionDigest = NmbLocalRenditionDigest.maybeComputeFromDocValue( docValue )

	if renditionDigest then
	
		local mockPreview = KSData.make( 'preview_data' )
		
		local previewFilePath = wfCatalog:fileSpec():pathForPreviewDigestAndLongEdge( 
				docValue._id, renditionDigest, 2048, false )

		local parentFolder = KSPathUtils.parent( previewFilePath )
		WFFileUtils.mkdir( parentFolder )
		KSData.saveToFile( mockPreview, previewFilePath )
		
		localOnlySettings.previewFilePath = previewFilePath
		
		mockPreview:close()
		
		local mockThumb = KSData.make( 'thumb_data' )
		
		local thumbnailFilePath = wfCatalog:fileSpec():pathForPreviewDigestAndLongEdge( 
				docValue._id, renditionDigest, 320, false )

		localOnlySettings.thumbnailFilePath = thumbnailFilePath

		parentFolder = KSPathUtils.parent( thumbnailFilePath )
		WFFileUtils.mkdir( parentFolder )
		KSData.saveToFile( mockThumb, thumbnailFilePath )

		mockThumb:close()

		rxTest:finally( function()
		
			WFFileUtils.deleteFile( previewFilePath )
			WFFileUtils.deleteFile( thumbnailFilePath )
		
		end )
	
	end

	return docValue

end

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

local function createiOSAsset( rxTest, assetId, localOnlySettings, optOverrides )

	optOverrides = optOverrides or {}

	local docValue = getBaseDocValue( assetId, optOverrides, localOnlySettings )

	docValue._localOnly = { 
		
		files = {
		
			master = {
				path = localOnlySettings.originalFilePath,
				contentId = localOnlySettings.originalContentId,
				fileSize = localOnlySettings.originalFileSize,
			},
		
			latestMaster = {
				filePath = localOnlySettings.originalFilePath,
			},
		
			proxy = {
				path = localOnlySettings.proxyFilePath,
				contentId = localOnlySettings.proxyContentId,
				fileSize = localOnlySettings.proxyFileSize,
			},
		
			thumbnail2x = {
				path = localOnlySettings.thumbnailFilePath,
				contentId = localOnlySettings.thumbnailContentId,
				fileSize = localOnlySettings.thumbnailFileSize,
			},
			
			preview = {
				path = localOnlySettings.previewFilePath,
				contentId = localOnlySettings.previewContentId,
				fileSize = localOnlySettings.previewFileSize,
			},
			
			xmp_develop = {
				path = not optOverrides.internalXmpCameraRaw and localOnlySettings.xmpDevelopSettingsRelPath or nil,
				contentId = localOnlySettings.developSettingsSha256,
				fileSize = not optOverrides.internalXmpCameraRaw and localOnlySettings.xmpDevelopSettingsFileSize or nil,
			}
			
		}
		
	}

	if optOverrides.noLocalOriginal then
		docValue._localOnly.files.latestMaster = nil
		docValue._localOnly.files.master = nil
	end

	if optOverrides.noLocalProxy then
		docValue._localOnly.files.proxy = nil
	end

	if ( optOverrides.docSubtype == "preset"
		or optOverrides.docSubtype == "profiles"
		or optOverrides.docSubtype == "camera_profile" ) and not optOverrides.noLocalOriginal then
		docValue._localOnly = {
			files = {
				presetProfile = {
					path = localOnlySettings.presetFilePath,
					sha256 = localOnlySettings.presetContentId,
					fileSize = localOnlySettings.presetFileSize,
				}
			}
		}
	end

	return docValue

end

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

local function createAndroidAsset( rxTest, assetId, localOnlySettings, optOverrides )

	optOverrides = optOverrides or {}

	local previewData = KSData.memoryMappedContentsOfFile( localOnlySettings.previewFilePath )
	local thumbnail2xData = KSData.memoryMappedContentsOfFile( localOnlySettings.thumbnailFilePath )

	localOnlySettings.previewMd5 = previewData:digest( 'MD5' )
	localOnlySettings.thumbMd5 = thumbnail2xData:digest( 'MD5' )

	previewData:close()
	thumbnail2xData:close()
	
	local revId = KSIdGenerator.uuidInLowercaseHex()

	local docValue = getBaseDocValue( assetId, optOverrides, localOnlySettings )

	docValue._localOnly = { 
		
		thumbnail2x = {
			md5 = localOnlySettings.thumbMd5,
			referenceHeight = 960,
			referenceWidth = 1280,
			epoch = 1,
			revId = revId,
			size = localOnlySettings.thumbnailFileSize
		},
		
		preview = {
			md5 = localOnlySettings.previewMd5,
			referenceHeight = 960,
			referenceWidth = 1280,
			epoch = 1,
			revId = revId,
			size = localOnlySettings.previewFileSize
		},
		
		proxy = {
			size = localOnlySettings.proxyFileSize,
			filePath = localOnlySettings.proxyFilePath,
			epoch = 1,
		},
		
		originalFilePath = localOnlySettings.originalFilePath,
		
		latestOriginal = {
			filePath = localOnlySettings.originalFilePath,
		},
		
		original = {
			epoch = 1,
			size = localOnlySettings.originalFileSize,
		},
		
		files = {

			xmp_develop = {
				path = not optOverrides.internalXmpCameraRaw and localOnlySettings.xmpDevelopSettingsRelPath or nil,
				sha256 = localOnlySettings.developSettingsSha256,
			}
			
		}
		
	}

	if optOverrides.noLocalOriginal then
		docValue._localOnly.latestOriginal = nil
		docValue._localOnly.originalFilePath = nil
		docValue._localOnly.original = nil
	end

	if optOverrides.noLocalProxy then
		docValue._localOnly.proxy = nil
	end

	if ( optOverrides.docSubtype == "preset"
		or optOverrides.docSubtype == "profile" ) and not optOverrides.noLocalOriginal then
		docValue._localOnly = {
			files = {
				presetProfile = {
					path = localOnlySettings.presetFilePath,
					sha256 = localOnlySettings.presetContentId,
					fileSize = localOnlySettings.presetFileSize,
				}
			}
		}
	end

	if optOverrides.docSubtype == "camera_profile" and not optOverrides.noLocalOriginal then
		docValue._localOnly = {
			files = {
				original = {
					master = {
						path = localOnlySettings.originalFilePath
					}
				}
			}
		}

	end

	return docValue

end

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

function WFCatAssetTestUtils.createAssetDocument( rxTest, wfCatalog, assetId, optOverrides )

	assetId = assetId or KSIdGenerator.uuidInLowercaseHex()
	optOverrides = optOverrides or {}

	local docValue = {}

	local localOnlySettings = getDefaultLocalOnlySettings( rxTest, wfCatalog, assetId )

	if WFPlatformInfo.isDesktop then
	
		docValue = createDesktopAsset( rxTest, wfCatalog, assetId, localOnlySettings, optOverrides )
	
	elseif WFPlatformInfo.isiOS then
	
		docValue = createiOSAsset( rxTest, assetId, localOnlySettings, optOverrides )
	
	elseif WFPlatformInfo.isAndroid then
	
		docValue = createAndroidAsset( rxTest, assetId, localOnlySettings, optOverrides )
	
	end

	return docValue, localOnlySettings

end

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

function WFCatAssetTestUtils.getOverlayToRemoveLocalOriginal( assetId )
	if WFPlatformInfo.isDesktop then
		
		return {
			_id = assetId,
			_localOnly = {
				files = {
					original = { _delete = true }
				}
			}
		} 
		
	elseif WFPlatformInfo.isiOS then

		return {
			_id = assetId,
			_localOnly = {
				files = {
					master = { _delete = true }
				}
			}
		} 
		
	elseif WFPlatformInfo.isAndroid then
	
		return {
			_id = assetId,
			_localOnly = {
				localUrl =  { _delete = true }, 
				originalFilePath = { _delete = true },
				latestOriginal = { _delete = true },
				original = { _delete = true },
				xmpSidecarExists = false,
			}
		} 
	
	end
	
end

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

function WFCatAssetTestUtils.getOverlayToRemoveLocalProxy( assetId )

	if WFPlatformInfo.isDesktop then
		
		return {
			_id = assetId,
				_localOnly = {
					files = {
						proxy = { _delete = true }
					}
				}
			}
		
	elseif WFPlatformInfo.isiOS then

		return {
			_id = assetId,
			_localOnly = {
				files = {
					proxy = { _delete = true }
				}
			}
		} 
		
	elseif WFPlatformInfo.isAndroid then
	
		return {
			_id = assetId,
			_localOnly = {
				proxy = { _delete = true }
			}
		} 
	
	end
end

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

function WFCatAssetTestUtils.getOriginalPathOverlay( assetId, sha256, originalFilePath )
	
	local originalFileSize = WFFileUtils.fileAttributes( originalFilePath ).fileSize
	
	local overlay = {}
	
	if WFPlatformInfo.isDesktop then
	
		overlay = {
			_id = assetId,
			_localOnly = {
				files = {
					original = {
						master = {
							relativePath = WFFileUtils.removeVolumeFromPath( originalFilePath ),
							volumeGuid = WFFileUtils.getVolumeGuidForPath( originalFilePath ),
							size = originalFileSize,
							sha256 = sha256,
						},
					},
				},
				xmpSidecarExists = false,
			}
		}
		
	elseif WFPlatformInfo.isiOS then
		
		overlay = {
			_id = assetId,
			_localOnly = { 
				files = {	
					master = {
						path = originalFilePath,
						contentId = sha256,
						fileSize = originalFileSize,
					},
				},
				xmpSidecarExists = false,
			}
		}

	elseif WFPlatformInfo.isAndroid then
		
		
		overlay = {
			_id = assetId,
			_localOnly = {
				-- this is a fake id (not the actual format of the field) and would apply only to local imports
				-- it's necessary for the binary uploader tests for missing originals
				localUrl = KSIdGenerator.uuidInLowercaseHex(), 
				originalFilePath = originalFilePath,
		
				latestOriginal = {
					filePath = originalFilePath,
				},
				
				original = {
					epoch = 1,
					size = originalFileSize,
				},
				xmpSidecarExists = false,
			}
		}
	end
	
	return overlay
	
end

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

function WFCatAssetTestUtils.addOriginalPathToDocument( baseAssetDoc, sha256, originalFilePath )
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, WFCatAssetTestUtils.getOriginalPathOverlay( baseAssetDoc._id, sha256, originalFilePath ) )
	
	return baseAssetDoc
end

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

function WFCatAssetTestUtils.getProxyPathOverlay( assetId, proxyFilePath )
	
	local proxyFileSize = WFFileUtils.fileAttributes( proxyFilePath ).fileSize
	
	local overlay = {}
	
	if WFPlatformInfo.isDesktop then
	
		overlay = {
			_id = assetId,
			_localOnly = {
				files = {
					proxy = {
						['2560'] = {
							relativePath = WFFileUtils.removeVolumeFromPath( proxyFilePath ),
							volumeGuid = WFFileUtils.getVolumeGuidForPath( proxyFilePath ),
							size = proxyFileSize,
						},
					},
				},
			}
		}
		
	elseif WFPlatformInfo.isiOS then
	
		overlay = {
			_id = assetId,
			_localOnly = { 
				files = {	
					proxy = {
						path = proxyFilePath,
						size = proxyFileSize,
						epoch = 1,
					},
				}
			}
		}
	
	elseif WFPlatformInfo.isAndroid then
		
		overlay = {
			_id = assetId,
			_localOnly = {
				proxy = {
					size = proxyFileSize,
					filePath = proxyFilePath,
					epoch = 1,
				},
			},
		}
		
	end
	
	return overlay
end

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

function WFCatAssetTestUtils.getXmpSidecarPathOverlay( assetId, xmpSidecarFilePath )
	
	local overlay
	if WFPlatformInfo.isDesktop then
		
		overlay = {
			_id = assetId,
			_localOnly = {
				files = {
					original = {
						xmp = {
							relativePath = xmpSidecarFilePath and WFFileUtils.removeVolumeFromPath( xmpSidecarFilePath ),
							volumeGuid = xmpSidecarFilePath and WFFileUtils.getVolumeGuidForPath( xmpSidecarFilePath ),
						}
					}
				},
				xmpSidecarExists = true
			}
		}

	else
		
		overlay = {}					
	
	end
	
	return overlay
end

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

function WFCatAssetTestUtils.addProxyPathToDocument( baseAssetDoc, proxyFilePath )
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, WFCatAssetTestUtils.getProxyPathOverlay( baseAssetDoc._id, proxyFilePath ) )
	
	return baseAssetDoc
end

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

function WFCatAssetTestUtils.addXmpSidecarPathToDocument( baseAssetDoc, xmpSidecarFilePath )
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, WFCatAssetTestUtils.getXmpSidecarPathOverlay( baseAssetDoc._id, xmpSidecarFilePath ) )
	
	return baseAssetDoc

end

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

local function getXmpSidecar()
	
	return [[<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        ">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:xmp="http://ns.adobe.com/xap/1.0/"
            xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
            xmlns:exifEX="http://cipa.jp/exif/1.0/"
            xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
            xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
            xmlns:dc="http://purl.org/dc/elements/1.1/"
            xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
            xmlns:exif="http://ns.adobe.com/exif/1.0/">
         <xmp:CreatorTool>8.3</xmp:CreatorTool>
         <xmp:ModifyDate>2015-06-08T08:19:58</xmp:ModifyDate>
         <xmp:CreateDate>2015-06-08T08:19:58</xmp:CreateDate>
         <xmp:MetadataDate>2015-06-08T08:19:58</xmp:MetadataDate>
         <aux:LensInfo>83/20 83/20 11/5 11/5</aux:LensInfo>
         <aux:Lens>iPhone 5s back camera 4.15mm f/2.2</aux:Lens>
         <exifEX:LensModel>iPhone 5s back camera 4.15mm f/2.2</exifEX:LensModel>
         <exifEX:LensMake>Apple</exifEX:LensMake>
         <photoshop:DateCreated>2015-06-08T08:19:58.057</photoshop:DateCreated>
         <photoshop:ColorMode>3</photoshop:ColorMode>
         <photoshop:ICCProfile>sRGB IEC61966-2.1</photoshop:ICCProfile>
         <xmpMM:DocumentID>74CEFE737E075E9148CE2592E45BDAF5</xmpMM:DocumentID>
         <xmpMM:InstanceID>74CEFE737E075E9148CE2592E45BDAF5</xmpMM:InstanceID>
         <dc:format>image/jpeg</dc:format>
         <tiff:ImageWidth>3264</tiff:ImageWidth>
         <tiff:ImageLength>2448</tiff:ImageLength>
         <tiff:BitsPerSample>
            <rdf:Seq>
               <rdf:li>8</rdf:li>
               <rdf:li>8</rdf:li>
               <rdf:li>8</rdf:li>
            </rdf:Seq>
         </tiff:BitsPerSample>
         <tiff:PhotometricInterpretation>2</tiff:PhotometricInterpretation>
         <tiff:Orientation>1</tiff:Orientation>
         <tiff:SamplesPerPixel>3</tiff:SamplesPerPixel>
         <tiff:YCbCrPositioning>1</tiff:YCbCrPositioning>
         <tiff:XResolution>72/1</tiff:XResolution>
         <tiff:YResolution>72/1</tiff:YResolution>
         <tiff:ResolutionUnit>2</tiff:ResolutionUnit>
         <tiff:Make>Apple</tiff:Make>
         <tiff:Model>iPhone 5s</tiff:Model>
         <exif:ExifVersion>0221</exif:ExifVersion>
         <exif:FlashpixVersion>0100</exif:FlashpixVersion>
         <exif:ColorSpace>1</exif:ColorSpace>
         <exif:ComponentsConfiguration>
            <rdf:Seq>
               <rdf:li>1</rdf:li>
               <rdf:li>2</rdf:li>
               <rdf:li>3</rdf:li>
               <rdf:li>0</rdf:li>
            </rdf:Seq>
         </exif:ComponentsConfiguration>
         <exif:PixelXDimension>3264</exif:PixelXDimension>
         <exif:PixelYDimension>2448</exif:PixelYDimension>
         <exif:DateTimeOriginal>2015-06-08T08:19:58</exif:DateTimeOriginal>
         <exif:ExposureTime>1/2564</exif:ExposureTime>
         <exif:FNumber>11/5</exif:FNumber>
         <exif:ExposureProgram>2</exif:ExposureProgram>
         <exif:ISOSpeedRatings>
            <rdf:Seq>
               <rdf:li>32</rdf:li>
            </rdf:Seq>
         </exif:ISOSpeedRatings>
         <exif:ShutterSpeedValue>7055/623</exif:ShutterSpeedValue>
         <exif:ApertureValue>2275007/1000000</exif:ApertureValue>
         <exif:BrightnessValue>8147/752</exif:BrightnessValue>
         <exif:ExposureBiasValue>0/1</exif:ExposureBiasValue>
         <exif:MeteringMode>3</exif:MeteringMode>
         <exif:Flash rdf:parseType="Resource">
            <exif:Fired>False</exif:Fired>
            <exif:Return>0</exif:Return>
            <exif:Mode>3</exif:Mode>
            <exif:Function>False</exif:Function>
            <exif:RedEyeMode>False</exif:RedEyeMode>
         </exif:Flash>
         <exif:FocalLength>83/20</exif:FocalLength>
         <exif:SubjectArea>
            <rdf:Seq>
               <rdf:li>1444</rdf:li>
               <rdf:li>1172</rdf:li>
               <rdf:li>610</rdf:li>
               <rdf:li>612</rdf:li>
            </rdf:Seq>
         </exif:SubjectArea>
         <exif:SensingMethod>2</exif:SensingMethod>
         <exif:SceneType>1</exif:SceneType>
         <exif:ExposureMode>0</exif:ExposureMode>
         <exif:WhiteBalance>0</exif:WhiteBalance>
         <exif:FocalLengthIn35mmFilm>29</exif:FocalLengthIn35mmFilm>
         <exif:SceneCaptureType>0</exif:SceneCaptureType>
         <exif:SubSecTimeOriginal>570</exif:SubSecTimeOriginal>
         <exif:SubSecTimeDigitized>570</exif:SubSecTimeDigitized>
         <exif:LensInfo>
            <rdf:Seq>
               <rdf:li>83/20</rdf:li>
            </rdf:Seq>
         </exif:LensInfo>
         <exif:LensMake>Apple</exif:LensMake>
         <exif:Lens>iPhone 5s back camera 4.15mm f/2.2</exif:Lens>
         <exif:GPSLatitude>47,36.7028N</exif:GPSLatitude>
         <exif:GPSLongitude>122,24.1805W</exif:GPSLongitude>
         <exif:GPSAltitudeRef>0</exif:GPSAltitudeRef>
         <exif:GPSAltitude>1153/64</exif:GPSAltitude>
         <exif:GPSTimeStamp>2015-06-08T15:19:58Z</exif:GPSTimeStamp>
         <exif:GPSSpeedRef>K</exif:GPSSpeedRef>
         <exif:GPSSpeed>383/50</exif:GPSSpeed>
         <exif:GPSImgDirectionRef>T</exif:GPSImgDirectionRef>
         <exif:GPSImgDirection>5101/146</exif:GPSImgDirection>
         <exif:GPSDestBearingRef>T</exif:GPSDestBearingRef>
         <exif:GPSDestBearing>31381/146</exif:GPSDestBearing>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>]]

end

WFCatAssetTestUtils.getXmpSidecar = getXmpSidecar

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

local xmpCameraRawStart = [[
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21        ">
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/"
   crs:Version="10.3"
   crs:ProcessVersion="10.0"
   crs:WhiteBalance="As Shot"
   crs:IncrementalTemperature="0"
   crs:IncrementalTint="0"
   crs:Saturation="0"
   crs:Sharpness="0"
   crs:LuminanceSmoothing="0"
   crs:ColorNoiseReduction="0"
   crs:VignetteAmount="0"
   crs:ShadowTint="0"
   crs:RedHue="0"
   crs:RedSaturation="0"
   crs:GreenHue="0"
   crs:GreenSaturation="0"
   crs:BlueHue="0"
   crs:BlueSaturation="0"
   crs:Vibrance="0"
   crs:HueAdjustmentRed="0"
   crs:HueAdjustmentOrange="0"
   crs:HueAdjustmentYellow="0"
   crs:HueAdjustmentGreen="0"
   crs:HueAdjustmentAqua="0"
   crs:HueAdjustmentBlue="0"
   crs:HueAdjustmentPurple="0"
   crs:HueAdjustmentMagenta="0"
   crs:SaturationAdjustmentRed="0"
   crs:SaturationAdjustmentOrange="0"
   crs:SaturationAdjustmentYellow="0"
   crs:SaturationAdjustmentGreen="0"
   crs:SaturationAdjustmentAqua="0"
   crs:SaturationAdjustmentBlue="0"
   crs:SaturationAdjustmentPurple="0"
   crs:SaturationAdjustmentMagenta="0"
   crs:LuminanceAdjustmentRed="0"
   crs:LuminanceAdjustmentOrange="0"
   crs:LuminanceAdjustmentYellow="0"
   crs:LuminanceAdjustmentGreen="0"
   crs:LuminanceAdjustmentAqua="0"
   crs:LuminanceAdjustmentBlue="0"
   crs:LuminanceAdjustmentPurple="0"
   crs:LuminanceAdjustmentMagenta="0"
   crs:SplitToningShadowHue="0"
   crs:SplitToningShadowSaturation="0"
   crs:SplitToningHighlightHue="0"
   crs:SplitToningHighlightSaturation="0"
   crs:SplitToningBalance="0"
   crs:ParametricShadows="0"
   crs:ParametricDarks="0"
   crs:ParametricLights="0"
   crs:ParametricHighlights="0"
   crs:ParametricShadowSplit="25"
   crs:ParametricMidtoneSplit="50"
   crs:ParametricHighlightSplit="75"
   crs:SharpenRadius="+1.0"
   crs:SharpenDetail="25"
   crs:SharpenEdgeMasking="0"
   crs:PostCropVignetteAmount="0"
   crs:GrainAmount="0"
   crs:LensProfileEnable="0"
   crs:LensManualDistortionAmount="0"
   crs:PerspectiveVertical="0"
   crs:PerspectiveHorizontal="0"
   crs:PerspectiveRotate="0.0"
   crs:PerspectiveScale="100"
   crs:PerspectiveAspect="0"
   crs:PerspectiveUpright="0"
   crs:PerspectiveX="0.00"
   crs:PerspectiveY="0.00"
   crs:AutoLateralCA="0"
   crs:Exposure2012="0.00"
   crs:Contrast2012="0"
   crs:Highlights2012="0"
   crs:Shadows2012="0"
   crs:Whites2012="0"
   crs:Blacks2012="0"
   crs:Clarity2012="0"
   crs:DefringePurpleAmount="0"
   crs:DefringePurpleHueLo="30"
   crs:DefringePurpleHueHi="70"
   crs:DefringeGreenAmount="0"
   crs:DefringeGreenHueLo="40"
   crs:DefringeGreenHueHi="60"
   crs:Dehaze="0"
   crs:ToneMapStrength="0"
   crs:ConvertToGrayscale="False"
   crs:OverrideLookVignette="False"
   crs:ToneCurveName="Linear"
   crs:ToneCurveName2012="Linear"
   crs:CameraProfile="Embedded"
   crs:CameraProfileDigest="54650A341B5B5CCAE8442D0B43A92BCE"
   crs:LensProfileSetup="LensDefaults"
   crs:UprightVersion="151388160"
   crs:UprightCenterMode="0"
   crs:UprightCenterNormX="0.5"
   crs:UprightCenterNormY="0.5"
   crs:UprightFocalMode="0"
   crs:UprightFocalLength35mm="35"
   crs:UprightPreview="False"
   crs:UprightTransformCount="6"
   crs:UprightFourSegmentsCount="0"
   crs:HasSettings="True"
   crs:AlreadyApplied="False">
   <crs:ToneCurve>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurve>
   <crs:ToneCurveRed>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurveRed>
   <crs:ToneCurveGreen>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurveGreen>
   <crs:ToneCurveBlue>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurveBlue>
   <crs:ToneCurvePV2012>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurvePV2012>
   <crs:ToneCurvePV2012Red>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurvePV2012Red>
   <crs:ToneCurvePV2012Green>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurvePV2012Green>
   <crs:ToneCurvePV2012Blue>
    <rdf:Seq>
     <rdf:li>0, 0</rdf:li>
     <rdf:li>255, 255</rdf:li>
    </rdf:Seq>
   </crs:ToneCurvePV2012Blue>
   <crs:PaintBasedCorrections>
    <rdf:Seq>
     <rdf:li>
      <rdf:Description
       crs:What="Correction"
       crs:CorrectionAmount="1.000000"
       crs:CorrectionActive="true"
       crs:LocalExposure="0.000000"
       crs:LocalSaturation="0.000000"
       crs:LocalContrast="0.000000"
       crs:LocalClarity="0.000000"
       crs:LocalSharpness="0.000000"
       crs:LocalBrightness="0.000000"
       crs:LocalToningHue="0.000000"
       crs:LocalToningSaturation="0.000000"
       crs:LocalExposure2012="0.500000"
       crs:LocalContrast2012="0.000000"
       crs:LocalHighlights2012="0.000000"
       crs:LocalShadows2012="-0.042071"
       crs:LocalWhites2012="1.000000"
       crs:LocalBlacks2012="1.000000"
       crs:LocalClarity2012="0.000000"
       crs:LocalDehaze="0.000000"
       crs:LocalLuminanceNoise="0.000000"
       crs:LocalMoire="0.000000"
       crs:LocalDefringe="0.000000"
       crs:LocalTemperature="0.000000"
       crs:LocalTint="0.000000">
      <crs:CorrectionMasks>
       <rdf:Seq>
        <rdf:li>
         <rdf:Description
          crs:What="Mask/Paint"
          crs:MaskValue="1.000000"
          crs:Radius="0.013802"
          crs:Flow="0.500000"
          crs:CenterWeight="0.000000">
         <crs:Dabs>
          <rdf:Seq>
           <rdf:li>d 0.944824 0.911002</rdf:li>
           <rdf:li>d 0.940887 0.909293</rdf:li>
           <rdf:li>d 0.936950 0.907583</rdf:li>
]]

local xmpCameraRawEnd = [[
           <rdf:li>d 0.496560 0.297892</rdf:li>
          </rdf:Seq>
         </crs:Dabs>
         </rdf:Description>
        </rdf:li>
       </rdf:Seq>
      </crs:CorrectionMasks>
      <crs:CorrectionRangeMask
       crs:Version="+2"
       crs:Type="0"
       crs:ColorAmount="0.500000"
       crs:LumMin="0.000000"
       crs:LumMax="1.000000"
       crs:LumFeather="0.500000"/>
      </rdf:Description>
     </rdf:li>
    </rdf:Seq>
   </crs:PaintBasedCorrections>
   <crs:Look
    crs:Name=""/>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
]]

local function getCameraRaw( useBigXmpDevelop )

	local xmpCameraRaw = xmpCameraRawStart
	if useBigXmpDevelop then
		local x = 0.900
		local y = 0.800

		for i = 1, 400 do
			x = x - .001
			y = y - .001
			local stroke =  string.format("           <rdf:li>d %f %f</rdf:li>\r\n", x, y )
			xmpCameraRaw = xmpCameraRaw .. stroke
		end
	end

	xmpCameraRaw = xmpCameraRaw .. xmpCameraRawEnd

	return xmpCameraRaw

end

WFCatAssetTestUtils.getCameraRaw = getCameraRaw

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

-- Sha256 for result of getCameraRaw( true )
local xmpDevelopSha256 = '0054ef228b1eaf3322c8caa31df6123e5851bfb3c851f81e0a09c270d51877dc'

local function getXmpDevelopSettings()
	return {
		processingModel = "lightroom",
		xmpCameraRaw = { sha256 = xmpDevelopSha256 }
	}
end

WFCatAssetTestUtils.xmpDevelopSha256 = xmpDevelopSha256
WFCatAssetTestUtils.getXmpDevelopSettings = getXmpDevelopSettings

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

function WFCatAssetTestUtils.addXmpDevelopToDocument( rxTest, catalog, assetId, xmpDevelopFilePath, userUpdated )

	local revisionId

	local xmpDevelopString = getCameraRaw( true )
	local xmpDevelopData = KSData.make( xmpDevelopString )
	local xmpDevelopFileSize = #xmpDevelopString
	KSData.saveToFile( xmpDevelopData, xmpDevelopFilePath )
	rxTest:expectEqual( 'file', WFFileUtils.exists( xmpDevelopFilePath ) )
	
	rxTest:finally( function()
		WFFileUtils.delete( xmpDevelopFilePath )
	end )
	
	local newXmpDevelopFilePath = xmpDevelopFilePath
	if WFPlatformInfo.isAndroid then
		-- Android doesn't store full file path here, so remove the beginning if we have a full file path
		local basePath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
		newXmpDevelopFilePath = string.replace( xmpDevelopFilePath, basePath .. '/', '' )
	end
	
	local developSettings = getXmpDevelopSettings()
	
	local contentIdOrSha256 = WFPlatformInfo.isiOS and 'contentId' or 'sha256'
	rxTest:expectSingleValue( catalog:docStore():applyOverlay {
			_id = assetId,
			userUpdated = userUpdated,
			develop = developSettings,
			_localOnly = {
				files = {
					xmp_develop = {
						path = newXmpDevelopFilePath,
						fileSize = xmpDevelopFileSize,
						[contentIdOrSha256] = xmpDevelopSha256,
					},
				},
			}
		}
		:map( function( revId )
			revisionId = revId
			return revId
		end )
	, ':ignore:' )

	rxTest:expectNotEqual( nil, revisionId )

	return revisionId

end

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

function WFCatAssetTestUtils.addBasicOzMetadataUpdate( baseAssetDoc, catalogId, assetId, revisionId )
	
	local hrefPrefaceForCreate = '/v2/catalogs/' .. catalogId .. '/assets/' .. assetId .. '/revisions/{revision_id}/'
		
	-- add the oz metadata overlay so we know where to upload it to
	local overlay = { 
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master_create' ] = {
					href = hrefPrefaceForCreate .. 'master',
					templated = true,
				},
				[ '/rels/proxy_create' ] = {
					href = hrefPrefaceForCreate .. 'proxies/{proxy_id}',
					templated = true,
				},
				[ '/rels/xmp_develop_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp/develop',
					templated = true,
				},
				[ '/rels/xmp_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp',
					templated = true,
				},
			},
			revision_ids = {
				revisionId,
			},
		}
	}
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, overlay )
	
	return baseAssetDoc
end

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

function WFCatAssetTestUtils.addBasicOzMetadataUpdateForDirectUpload( baseAssetDoc, catalogId, assetId, revisionId )
	
	local hrefPrefaceForCreate = string.format( '/v2/catalogs/%s/assets/%s/revisions/{revision_id}/', catalogId, assetId ) 
		
	-- add the oz metadata overlay so we know where to upload it to
	local overlay = { 
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master_create' ] = {
					href = hrefPrefaceForCreate .. 'master',
					templated = true,
				},
				[ '/rels/proxy_create' ] = {
					href = hrefPrefaceForCreate .. 'proxies/{proxy_id}',
					templated = true,
				},
				[ '/rels/xmp_develop_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp/develop',
					templated = true,
				},
				[ '/rels/xmp_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp',
					templated = true,
				},
				[ '/rels/main_create/parts' ] = {
					href = hrefPrefaceForCreate .. 'main/parts?access=put&upload_status={upload_status}&file_size={file_size}&part_size={part_size}&content_type={content_type}',
					templated = true,
				},
			},
			revision_ids = {
				revisionId,
			},
		}
	}
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, overlay )
	
	return baseAssetDoc
	
end

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

function WFCatAssetTestUtils.addBasicOzMetadataUpdateForIACAsset( baseAssetDoc, spaceId, assetId, revisionId )
	
	local hrefPrefaceForCreate = '/v2/spaces/' .. spaceId .. '/assets/' .. assetId .. '/revisions/{revision_id}/'
		
	-- add the oz metadata overlay so we know where to upload it to
	local overlay = { 
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master_create' ] = {
					href = hrefPrefaceForCreate .. 'master',
					templated = true,
				},
				[ '/rels/proxy_create' ] = {
					href = hrefPrefaceForCreate .. 'proxies/{proxy_id}',
					templated = true,
				},
				[ '/rels/xmp_develop_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp/develop',
					templated = true,
				},
				[ '/rels/xmp_create' ] = {
					href = hrefPrefaceForCreate .. 'xmp',
					templated = true,
				},
			},
			revision_ids = {
				revisionId,
			},
		}
	}
	
	baseAssetDoc = WFTableMerge.applyOverlay( baseAssetDoc, overlay )
	
	return baseAssetDoc
	
end

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

local function contentTypeFromFileName( fileName )

	if fileName then
		local extension = fileName:match( "%.([^%.]+)$" )
		
		if extension then
			extension = extension:lower()
			return WFOzContentTypes.contentTypeForExtention( extension ) or WFOzContentTypes.kRawContentType
		end
	end
	
	return "image/jpeg"

end

WFCatAssetTestUtils.contentTypeFromFileName = contentTypeFromFileName

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

local function getTestDataOriginalFilePath( tempTestFolder, fileName )

	local testDataPath = child( KSPathUtils.getStandardFilePath( 'testdata' ), "import-samples" )
	local testDataFolder1 = child( testDataPath, "folder-1" )
	local testFilePath = child( testDataFolder1, fileName )

	local originalFilePath = child( tempTestFolder, fileName )
	WFFileUtils.copy( testFilePath, originalFilePath )

	return originalFilePath

end

WFCatAssetTestUtils.getTestDataOriginalFilePath = getTestDataOriginalFilePath

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

local function pollBinaryUploadCount( uploader, bucket )
	return Observable.timer( .1, .1, WFRxScheduler.sharedScheduler() )
		:map( function()
			return uploader:DEBUG_uploadCount( bucket )
		end )
end

WFCatAssetTestUtils.pollBinaryUploadCount = pollBinaryUploadCount

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

function WFCatAssetTestUtils.createBaseAssetDocument( catalogId, assetId, revisionId, sha256, originalFilePath, importedBy )
	
	local originalFileSize
	local fileName
	local contentType = 'image/jpeg'
	local subtype = 'image'
	
	if originalFilePath then
	
		originalFileSize = WFFileUtils.fileAttributes( originalFilePath ).fileSize
		fileName = KSPathUtils.leafName( originalFilePath )
		contentType = contentTypeFromFileName( fileName )
		
		subtype = string.find( contentType, "video") and "video" or "image"
	
	end
	
	local originalHeight = 3840
	local originalWidth = 5760
	
	if fileName == "IMG_7763.JPG" then
	
		originalHeight = 2448
		originalWidth = 3264
	
	elseif fileName == 'IMG_7772.mp4' then
	
		originalHeight = 270
		originalWidth = 480
	
	elseif fileName == 'IMG_9876.CR2' then
		
		originalHeight = 3456
		originalWidth = 5184
	
	elseif fileName == 'IMG_6025.MOV' then
	
		originalHeight = 1920
		originalWidth = 1080
	
	end
	
	
	return {
		_id = assetId or KSIdGenerator.uuidInLowercaseHex(),
		_newRevId = revisionId or KSIdGenerator.uuidInLowercaseHex(),
		type = 'asset',
		subtype = subtype,
		captureDate = "2015-06-08T08:19:59Z",
		userCreated = "2018-04-15T18:18:18Z",
		userUpdated = "2018-04-15T18:18:18Z",

		importSource = {
			originalHeight = originalHeight,
			originalWidth = originalWidth,
			importedOnDevice = "Macbook Pro",
			importTimestamp = "2017-04-15T11:03:40Z",
			contentType = contentType,
			sha256 = sha256,
			fileSize = originalFileSize,
			importedBy = importedBy or "129d51066a7a3a5711fa62810b758fc8",
			fileName = fileName,
		},
	}

end

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

return WFCatAssetTestUtils
