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

WFCatDevelopSettingsTest.lua

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

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

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

local Rx = require 'Rx'

	-- WF.oz_client.managed_catalog
local WFCatDevelopSettings = require 'WFCatDevelopSettings'
local WFCatMockCatalog = require 'WFCatMockCatalog'

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

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSData = require "KSData"
local KSDate = require 'KSDate'
local KSTableUtils = require 'KSTableUtils'
local WFFileUtils = require 'WFFileUtils'

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

local WFCatDevelopSettingsTest = {}

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

function WFCatDevelopSettingsTest.rxTestWaitForPendingDevelopSettingsSaveForAssetIds( rxTest )

	local assetIds = {
		'a',
		'b',
		'c'
	}

	-- No Assets Pending

	local observer =
			rxTest:observe( WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( assetIds ) )

	observer:expectDataWithinTime( 1, true )
	observer:expectCompleted()

	-- 1 Pending Asset

	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveInProgress( 1, 'a' )

	observer =
			rxTest:observe( WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( assetIds ) )

	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveSucceeded( 1, 'a' )

	observer:expectDataWithinTime( 1, true )
	observer:expectCompleted()

	-- All Assets Pending

	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveInProgress( 1, 'a' )
	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveInProgress( 2, 'b' )
	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveInProgress( 3, 'c' )

	observer =
			rxTest:observe( WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( assetIds ) )

	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveSucceeded( 1, 'a' )
	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveSucceeded( 2, 'b' )
	WFCatDevelopSettings._DEBUG_pendingDevelopSettingsSaveSucceeded( 3, 'c' )

	observer:expectDataWithinTime( 1, true )
	observer:expectCompleted()

end

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

function WFCatDevelopSettingsTest.rxTestGetCorrectSettingsOverlay( rxTest )

	local catalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local imageAssetId = KSIdGenerator.uuidInLowercaseHex()
	local videoAssetId = KSIdGenerator.uuidInLowercaseHex()

	local croppedWidth = 1920
	local croppedHeight = 1080

	local now = KSDate.timeInIso8601Format()

	local developSettingsOverlayStream, assetInfo, imageInfo, videoInfo, isVideo

	-- Base Case
	imageInfo = {
		assetId = imageAssetId,
		tiffOrientation = 2,
		temporal = nil,
		croppedWidth = croppedWidth,
		croppedHeight = croppedHeight,
		needsUpdateAISettings = true,
		settings = '<xmp:camera stuff .. />',
		devSettingsDate = now,
		fromDefaults = false,
	}

	videoInfo = {
		assetId = videoAssetId,
		tiffOrientation = 2,
		temporal = {
			inPoint = 1000000,
			duration = 2000000
		},
		-- not fully supported yet, revisit once Oz deploys
		croppedWidth = croppedWidth,
		croppedHeight = croppedHeight,
		--
		settings = {
			adjustments = {
				exposure = 1,
				contrast = 2,
				whites = 3,
				blacks = 4
			}
		},
		devSettingsDate = now,
		fromDefaults = false,
	}

	isVideo = false
	assetInfo = imageInfo
	developSettingsOverlayStream = WFCatDevelopSettings.getSettingsOverlayStream( isVideo, catalog, assetInfo.assetId, assetInfo.tiffOrientation,
																					assetInfo.temporal, assetInfo.croppedWidth, assetInfo.croppedHeight,
																					assetInfo.settings, assetInfo.devSettingsDate, assetInfo.fromDefaults,
																					nil, assetInfo.needsUpdateAISettings )
	rxTest:waitFor(developSettingsOverlayStream:map(
		function( docValue )
			rxTest:expectEqual(assetInfo.assetId, docValue._id)
			rxTest:expectEqual(croppedWidth, docValue.develop.croppedWidth)
			rxTest:expectEqual(false, docValue.develop.fromDefaults)
			rxTest:expectEqual("lightroom", docValue.develop.processingModel)
			rxTest:expectEqual(2, docValue.develop.userOrientation)
			rxTest:expectEqual(now, docValue.develop.userUpdated)
			rxTest:expectEqual(	{
				_replace = true,
				sha256 = "13ada4e31740f4a6ef2f1dc05d959a0e078cb82106d2e1e555bb95cb1e3c346f",
			}, docValue.develop.xmpCameraRaw)
                        -- Test that the device field has a name plus sha256 identifier
			rxTest:expectEqual(true, not not string.match(docValue.develop.device, "^(.*)({[A-Fa-f0-9]+})"))
			rxTest:expectEqual( false, docValue.develop.crsHDREditMode )
			rxTest:expectEqual( true, docValue.develop.needsUpdateAISettings )
		end )
	)

	rxTest:expectSingleValue( developSettingsOverlayStream, ':ignore:' )

	assetInfo.needsUpdateAISettings = false
	developSettingsOverlayStream = WFCatDevelopSettings.getSettingsOverlayStream( isVideo, catalog, assetInfo.assetId, assetInfo.tiffOrientation,
																					assetInfo.temporal, assetInfo.croppedWidth, assetInfo.croppedHeight,
																					assetInfo.settings, assetInfo.devSettingsDate, assetInfo.fromDefaults,
																					nil, assetInfo.needsUpdateAISettings )
	rxTest:waitFor(developSettingsOverlayStream:map(
		function( docValue )
			rxTest:expectEqual(assetInfo.assetId, docValue._id)
			rxTest:expectEqual(croppedWidth, docValue.develop.croppedWidth)
			rxTest:expectEqual(false, docValue.develop.fromDefaults)
			rxTest:expectEqual("lightroom", docValue.develop.processingModel)
			rxTest:expectEqual(2, docValue.develop.userOrientation)
			rxTest:expectEqual(now, docValue.develop.userUpdated)
			rxTest:expectEqual(	{
				_replace = true,
				sha256 = "13ada4e31740f4a6ef2f1dc05d959a0e078cb82106d2e1e555bb95cb1e3c346f",
			}, docValue.develop.xmpCameraRaw)
                        -- Test that the device field has a name plus sha256 identifier
			rxTest:expectEqual(true, not not string.match(docValue.develop.device, "^(.*)({[A-Fa-f0-9]+})"))
			rxTest:expectEqual( false, docValue.develop.crsHDREditMode )
			rxTest:expectEqual( {_delete = true}, docValue.develop.needsUpdateAISettings )
		end )
	)

	rxTest:expectSingleValue( developSettingsOverlayStream, ':ignore:' )

	isVideo = true
	assetInfo = videoInfo
	developSettingsOverlayStream = WFCatDevelopSettings.getSettingsOverlayStream( isVideo, catalog, assetInfo.assetId, assetInfo.tiffOrientation,
																					assetInfo.temporal, assetInfo.croppedWidth, assetInfo.croppedHeight,
																					assetInfo.settings, assetInfo.devSettingsDate, assetInfo.fromDefaults )

	developSettingsOverlayStream =

			developSettingsOverlayStream:map( function( docValue )

				rxTest:expectEqual( true, not not string.match( docValue.develop.temporal.device, "^(.*)({[A-Fa-f0-9]+})" ) )
				rxTest:expectEqual( true, not not string.match( docValue.develop.adjustments.device, "^(.*)({[A-Fa-f0-9]+})" ) )

				KSTableUtils.setNested( docValue, '<device>', 'develop', 'temporal', 'device' )
				KSTableUtils.setNested( docValue, '<device>', 'develop', 'adjustments', 'device' )

				return docValue

			end )

	rxTest:expectSingleValue( developSettingsOverlayStream,
		{
			_id = assetInfo.assetId,
			develop = {
				adjustments = {
					_replace = true,
					device = '<device>',
					blacks = 4,
					contrast = 2,
					exposure = 1,
					whites = 3,
					userUpdated = now,
				},
				-- SPAM [btran 2022-02-11]
				-- Oz has deployed croppedHeight and croppedWidth for video.
				croppedHeight = croppedHeight,
				croppedWidth = croppedWidth,

				processVersion = 1,
				temporal = {
					_replace = true,
					device = '<device>',
					inPoint = 1000000,
					duration = 2000000,
					userUpdated = now,
				},
				version = 1,
			},

		}
	)

end

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

function WFCatDevelopSettingsTest.rxTestExternalizeAllXmpDevelop( rxTest )

	-- for now this is only applicable when atomic develop settings feature is enabled

	WFCatDevelopSettings._TEST_getCapabilitiesCache():setCapabilitiesForTests( {
		[ 'WFCatBinaryUploader.atomicDevelopSettings' ] = true,
	} )

	rxTest:finally( function()
		WFCatDevelopSettings._TEST_getCapabilitiesCache():setCapabilitiesForTests( {
			[ 'WFCatBinaryUploader.atomicDevelopSettings' ] = false,
		} )
	end )

	local catalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	-- test getSettingsOverlayStream

	local isVideo = false
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	local tiffOrientation = 2
	local temporal = nil
	local croppedWidth = 1920
	local croppedHeight = 1080
	local settings = '<xmp:camera stuff .. />'
	local devSettingsDate = KSDate.timeInIso8601Format()
	local fromDefaults = false

	local sha256 = KSData.make( settings ):digest( 'SHA256' )

	local developSettingsOverlayStream =
		WFCatDevelopSettings.getSettingsOverlayStream( isVideo, catalog, assetId, tiffOrientation, temporal, croppedWidth, croppedHeight, settings, devSettingsDate, fromDefaults )

	rxTest:waitFor( developSettingsOverlayStream:map(
		function( docValue )
			rxTest:expectEqual( assetId, docValue._id )
			rxTest:expectEqual( croppedWidth, docValue.develop.croppedWidth )
			rxTest:expectEqual( false, docValue.develop.fromDefaults )
			rxTest:expectEqual( "lightroom", docValue.develop.processingModel )
			rxTest:expectEqual( 2, docValue.develop.userOrientation )
			rxTest:expectEqual( sha256, docValue.develop.xmpCameraRaw.sha256 )
			rxTest:expectEqual( devSettingsDate, docValue.develop.userUpdated )
			rxTest:expectEqual( false, docValue.develop.crsHDREditMode )

			local localXmpTable = KSTableUtils.derefOpt( docValue, '_localOnly', 'files', 'xmp_develop' )
			rxTest:expectEqual( 'table', type( localXmpTable ) )
			rxTest:expectEqual( #settings, localXmpTable.fileSize )
			rxTest:expectEqual( sha256, localXmpTable.sha256 )
			rxTest:expectEqual( 'file', WFFileUtils.exists( localXmpTable.path ) )
		end )
	)

	rxTest:expectSingleValue( developSettingsOverlayStream, ':ignore:' )

	-- test externalizeDevelopSettings

	settings = 'this is a dummy xmp camera raw string'
	sha256 = KSData.make( settings ):digest( 'SHA256' )

	local spaceId = KSIdGenerator.uuidInLowercaseHex()

	local overlay = {
		type = 'asset',
		subtype = 'image',
		_id = assetId,
		_spaceId = spaceId,
		develop = {
			xmpCameraRaw = settings
		}
	}
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( overlay ), ':ignore:' )

	rxTest:expectSingleValue( WFCatDevelopSettings.externalizeDevelopSettings( catalog, assetId ), true )

	local newDocValue
	rxTest:expectSingleValue(
		catalog:docStore():docValue( assetId )
			:map( function( docValue )
				newDocValue = docValue
				return nil
			end ),
		nil )

	local newXmpCameraRawSection = KSTableUtils.derefOpt( newDocValue, 'develop', 'xmpCameraRaw' )
	rxTest:expectEqual( 'table', type( newXmpCameraRawSection ) )
	rxTest:expectEqual( sha256, newXmpCameraRawSection.sha256 )

	local localXmpTable = KSTableUtils.derefOpt( newDocValue, '_localOnly', 'files', 'xmp_develop' )
	rxTest:expectEqual( 'table', type( localXmpTable ) )
	rxTest:expectEqual( newXmpCameraRawSection.sha256, localXmpTable.sha256 )
	rxTest:expectEqual( 'file', WFFileUtils.exists( localXmpTable.path ) )

end

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

WFCatDevelopSettingsTest.rxTestMakeMostRecentDevelopContentStream = function ( rxTest )

	local developContentStream = Rx.Subject()

	local actualUpdateMsgs = {}

	local mostRecentDevelopContentUpdateDesc 

	local mostRecentDevelopContentUpdateStream = 
			WFCatDevelopSettings._privateUnitTestExports.makeMostRecentDevelopContentStream( 
					developContentStream )

	-- capture description of all updates we see
	require 'WFRxRunUntilDone'(
		mostRecentDevelopContentUpdateStream
			:map( 
				function()

					actualUpdateMsgs[ #actualUpdateMsgs + 1 ] = 
						mostRecentDevelopContentUpdateDesc or "mostRecentDevelopContentUpdateDesc is nil"

				end ) )

		
	local function sendDevelopContentUpdate( updateDesc, developContent )

		mostRecentDevelopContentUpdateDesc = updateDesc

		developContentStream:onNext( developContent )

	end

	sendDevelopContentUpdate( 
		"no changeCounter imageId 1",
		{ 
			id_local = 1,
		} )

	sendDevelopContentUpdate( 
		"changeCounter 1 imageId 1",
		{ 

			id_local = 1,

			changeCounter = 1,

		} )

	sendDevelopContentUpdate( 
		"ignored: changeCounter 1 imageId 1",
		{ 

			id_local = 1,

			changeCounter = 1,

		} )

	sendDevelopContentUpdate( 
		"changeCounter 1 imageId 2",
		{ 

			id_local = 2,

			changeCounter = 1,

		} )

	sendDevelopContentUpdate( 
		"changeCounter 2 imageId 1 settingsXmpStatus complete",
		{ 

			id_local = 1,

			changeCounter = 2,

			settingsXmpStatus = 'complete',

		} )

	sendDevelopContentUpdate( 
		"ignored changeCounter 2 imageId 1 settingsXmpStatus pending",
		{ 

			id_local = 1,

			changeCounter = 2,

			settingsXmpStatus = 'pending',

		} )

	sendDevelopContentUpdate( 
		"ignored changeCounter 2 imageId 1 settingsXmpStatus error",
		{ 

			id_local = 1,

			changeCounter = 2,

			settingsXmpStatus = 'failed',

		} )

	sendDevelopContentUpdate( 
		"changeCounter 3 imageId 1 settingsXmpStatus error",
		{ 

			id_local = 1,

			changeCounter = 3,

			settingsXmpStatus = 'failed',

		} )

	sendDevelopContentUpdate( 
		"ignored changeCounter 3 imageId 1 settingsXmpStatus pending",
		{ 

			id_local = 1,

			changeCounter = 3,

			settingsXmpStatus = 'pending',

		} )

	sendDevelopContentUpdate( 
		"ignored changeCounter 3 imageId 1 settingsXmpStatus complete",
		{ 

			id_local = 1,

			changeCounter = 3,

			settingsXmpStatus = 'complete',

		} )

	sendDevelopContentUpdate( 
		"changeCounter 4 imageId 1 settingsXmpStatus pending",
		{ 

			id_local = 1,

			changeCounter = 4,

			settingsXmpStatus = 'pending',

		} )

	sendDevelopContentUpdate( 
		"changeCounter 4 imageId 1 settingsXmpStatus complete",
		{ 

			id_local = 1,

			changeCounter = 4,

			settingsXmpStatus = 'complete',

		} )

	sendDevelopContentUpdate( 
		"changeCounter 5 imageId 1 settingsXmpStatus pending",
		{ 

			id_local = 1,

			changeCounter = 5,

			settingsXmpStatus = 'pending',

		} )

	sendDevelopContentUpdate( 
		"changeCounter 5 imageId 1 settingsXmpStatus error",
		{ 

			id_local = 1,

			changeCounter = 5,

			settingsXmpStatus = 'failed',

		} )

	rxTest:log( "testing makeMostRecentDevelopContentStream relayed the right updates, if this fails, please see notes in test" )

	-- IMPORTANT NOTE: this test assumes that
	--
	--   local mostRecentDevelopContentUpdateStream =
	--      WFCatDevelopSettings._privateUnitTestExports.makeMostRecentDevelopContentStream(
	--       developContentStream ) 
	--
	-- fires immediately when the developContentStream emits a most recent
	-- value. It relies on that to ensure all sendDevelopContentUpdate calls
	-- have been processed before this equality test.
	local expectedUpdateMsgs = {
		"no changeCounter imageId 1",
		"changeCounter 1 imageId 1",
		"changeCounter 1 imageId 2",
		"changeCounter 2 imageId 1 settingsXmpStatus complete",
		"changeCounter 3 imageId 1 settingsXmpStatus error",
		"changeCounter 4 imageId 1 settingsXmpStatus pending",
		"changeCounter 4 imageId 1 settingsXmpStatus complete",
		"changeCounter 5 imageId 1 settingsXmpStatus pending",
		"changeCounter 5 imageId 1 settingsXmpStatus error",
	}

	rxTest:expectEqual( expectedUpdateMsgs, actualUpdateMsgs )

end

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

-- TODO [jandrew 2025-03-18]: Disabled due to test instability.

function WFCatDevelopSettingsTest.DISABLED_rxTestSetCameraRawSettings( rxTest )

	local catalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local imageAssetId1 = KSIdGenerator.uuidInLowercaseHex()

	local croppedWidth = 1920
	local croppedHeight = 1080
	local needsUpdateAISettings = true
	local auxInfo = {}
	local maybeLensProfileDigest = nil
	local maybeCameraProfileFingerprint = nil -- "CB53843054696C00239B1180057888D3"
	local maybeIsFallbackProfile = false
	local maybeCameraProfileDigest = nil -- "9C057227216BE688434471F22E5E736D"
	local maybeCameraProfileName = nil -- "Adobe Standard"
	local cameraModel = "Canon EOS 20D"
	local tiffOrientation = 1
	local isReset = false
	local isVideo = false
	local temporal = nil
	local xmpSettings = [[
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 7.0-c000 1.000000, 0000/00/00-00:00:00        ">
 <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="17.0"
   crs:ProcessVersion="15.4"
   crs:WhiteBalance="Custom"
   crs:Temperature="2000"
   crs:Tint="-150"
   crs:Exposure2012="0.00"
   crs:Contrast2012="0"
   crs:Highlights2012="0"
   crs:Shadows2012="0"
   crs:Whites2012="0"
   crs:Blacks2012="0"
   crs:Texture="0"
   crs:Clarity2012="0"
   crs:Dehaze="0"
   crs:Vibrance="0"
   crs:Saturation="0"
   crs:ParametricShadows="0"
   crs:ParametricDarks="0"
   crs:ParametricLights="0"
   crs:ParametricHighlights="0"
   crs:ParametricShadowSplit="25"
   crs:ParametricMidtoneSplit="50"
   crs:ParametricHighlightSplit="75"
   crs:Sharpness="40"
   crs:SharpenRadius="+1.0"
   crs:SharpenDetail="25"
   crs:SharpenEdgeMasking="0"
   crs:LuminanceSmoothing="0"
   crs:ColorNoiseReduction="25"
   crs:ColorNoiseReductionDetail="50"
   crs:ColorNoiseReductionSmoothness="50"
   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:ColorGradeMidtoneHue="0"
   crs:ColorGradeMidtoneSat="0"
   crs:ColorGradeShadowLum="0"
   crs:ColorGradeMidtoneLum="0"
   crs:ColorGradeHighlightLum="0"
   crs:ColorGradeBlending="50"
   crs:ColorGradeGlobalHue="0"
   crs:ColorGradeGlobalSat="0"
   crs:ColorGradeGlobalLum="0"
   crs:AutoLateralCA="0"
   crs:LensProfileEnable="0"
   crs:LensManualDistortionAmount="0"
   crs:VignetteAmount="0"
   crs:DefringePurpleAmount="0"
   crs:DefringePurpleHueLo="30"
   crs:DefringePurpleHueHi="70"
   crs:DefringeGreenAmount="0"
   crs:DefringeGreenHueLo="40"
   crs:DefringeGreenHueHi="60"
   crs:PerspectiveUpright="0"
   crs:PerspectiveVertical="0"
   crs:PerspectiveHorizontal="0"
   crs:PerspectiveRotate="0.0"
   crs:PerspectiveAspect="0"
   crs:PerspectiveScale="100"
   crs:PerspectiveX="0.00"
   crs:PerspectiveY="0.00"
   crs:GrainAmount="0"
   crs:PostCropVignetteAmount="0"
   crs:ShadowTint="0"
   crs:RedHue="0"
   crs:RedSaturation="0"
   crs:GreenHue="0"
   crs:GreenSaturation="0"
   crs:BlueHue="0"
   crs:BlueSaturation="0"
   crs:HDREditMode="0"
   crs:OverrideLookVignette="False"
   crs:ToneCurveName2012="Linear"
   crs:CameraProfile="Adobe Standard"
   crs:CameraProfileDigest="9C057227216BE688434471F22E5E736D"
   crs:HasSettings="True"
   crs:AlreadyApplied="False"
   crs:CropTop="0"
   crs:CropLeft="0"
   crs:CropBottom="1"
   crs:CropRight="1"
   crs:CropAngle="0"
   crs:CropConstrainToWarp="0"
   crs:CropConstrainToUnitSquare="1"
   crs:HasCrop="False">
   <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:PointColors>
    <rdf:Seq>
     <rdf:li>-1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000, -1.000000</rdf:li>
    </rdf:Seq>
   </crs:PointColors>
   <crs:Look>
    <rdf:Description
     crs:Name="Adobe Color"
     crs:Amount="1"
     crs:UUID="B952C231111CD8E0ECCF14B86BAA7077"
     crs:SupportsAmount="false"
     crs:SupportsMonochrome="false"
     crs:SupportsOutputReferred="false">
    <crs:Group>
     <rdf:Alt>
      <rdf:li xml:lang="x-default">Profiles</rdf:li>
     </rdf:Alt>
    </crs:Group>
    <crs:Parameters>
     <rdf:Description
      crs:Version="17.0"
      crs:ProcessVersion="15.4"
      crs:ConvertToGrayscale="False"
      crs:CameraProfile="Adobe Standard"
      crs:LookTable="E1095149FDB39D7A057BAB208837E2E1">
     <crs:ToneCurvePV2012>
      <rdf:Seq>
       <rdf:li>0, 0</rdf:li>
       <rdf:li>22, 16</rdf:li>
       <rdf:li>40, 35</rdf:li>
       <rdf:li>127, 127</rdf:li>
       <rdf:li>224, 230</rdf:li>
       <rdf:li>240, 246</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>
     </rdf:Description>
    </crs:Parameters>
    </rdf:Description>
   </crs:Look>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>
	]]

	local wfCatalogStream = Rx.Observable.just( catalog )
	local settingsModel = WFCatDevelopSettings.setCameraRawSettings( wfCatalogStream )

	local observer = rxTest:observe( settingsModel.value )

	settingsModel.events:save( 1, imageAssetId1, isVideo, croppedWidth, croppedHeight,
								xmpSettings, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )

	rxTest:expectSingleValue(
		WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( { imageAssetId1 } ),
		true )

	observer:expectDataWithinTime( 1, { 1, imageAssetId1 } )

	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId1 )
			:pluck( 'develop', 'userUpdated' )
			:map( type ),
		'string' )
		
	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId1 )
			:pluck( 'develop', 'needsUpdateAISettings' ),
		true )
		
	-- remove develop.needsUpdateAISettings
	needsUpdateAISettings = false
	settingsModel.events:save( 2, imageAssetId1, isVideo, croppedWidth, croppedHeight,
								xmpSettings, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )

	rxTest:expectSingleValue(
		WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( { imageAssetId1 } ),
		true )

	observer:expectDataWithinTime( 1, { 2, imageAssetId1 } )
		
	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId1 )
			:pluck( 'develop', 'needsUpdateAISettings' )
			:map( type ),
		'nil' )

	isReset = true
	settingsModel.events:save( 3, imageAssetId1, isVideo, croppedWidth, croppedHeight,
								xmpSettings, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )

	observer:expectDataWithinTime( 1, { 3, imageAssetId1 } )

	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId1 )
			:pluck( 'develop', 'userUpdated' )
			:map( type ),
		'nil' )

	local videoAssetId = KSIdGenerator.uuidInLowercaseHex()
	isVideo = true
	isReset = false

	temporal = {
		duration = 3913272705,
		inPoint = 123393960,
		temporalFraction = 100000000,
	}

	local videoSettingsTable = {
		adjustments = {
			adjustmentsFraction = 1000,
			exposure2012 = 30,
		},
		processVersion = 1,
		version = 1,
	}
	settingsModel.events:save( 4, videoAssetId, isVideo, croppedWidth, croppedHeight,
								videoSettingsTable, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )

	observer:expectDataWithinTime( 1, { 4, videoAssetId } )

	rxTest:expectSingleValue( catalog:docStore():docValue( videoAssetId )
			:map( function( docValue )
				return docValue
			end )
			:pluck( 'develop', 'temporal', 'duration' )
			:map( type ),
		'number' )

	isReset = true
	settingsModel.events:save( 5, videoAssetId, isVideo, croppedWidth, croppedHeight,
								videoSettingsTable, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )

	observer:expectDataWithinTime( 1, { 5, videoAssetId } )

	rxTest:expectSingleValue( catalog:docStore():docValue( videoAssetId )
			:pluck( 'develop', 'temporal', 'duration' )
			:map( type ),
		'nil' )


	-- test with a digest with file that isn't present to generate an error
	local imageAssetId2 = KSIdGenerator.uuidInLowercaseHex()
	isVideo = false
	isReset = true
	auxInfo = { "9C057227216BE688434471F22E5E736D" }
	settingsModel.events:save( 6, imageAssetId2, isVideo, croppedWidth, croppedHeight,
								xmpSettings, auxInfo, needsUpdateAISettings,
								maybeLensProfileDigest,
								maybeCameraProfileFingerprint,
								maybeIsFallbackProfile,
								maybeCameraProfileDigest,
								maybeCameraProfileName,
								cameraModel, tiffOrientation,
								temporal, isReset )


	rxTest:expectSingleValue(
		WFCatDevelopSettings.waitForPendingDevelopSettingsSaveForAssetIds( { imageAssetId2 } ),
		false )

	observer:expectDataWithinTime( 1, { 6, imageAssetId2, "AuxNotFound" } )

	-- Verify the mask is not applied
	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId2 )
			:pluck( 'develop', 'masks' )
			:map( type ),
		'nil' )

	-- Verify the aux table is empty
	rxTest:expectSingleValue( catalog:docStore():docValue( imageAssetId2 )
			:pluck( 'aux' )
			:map( type ),
		'nil' )

end

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

-- WFCatDevelopSettings is only used by LrD

if WFPlatformInfo.isMobile() then
	return {}
else
	return WFRxTestCase.wrapSuite( 'WFCatDevelopSettingsTest', WFCatDevelopSettingsTest )
end
