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

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

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

local Rx = require 'Rx'

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

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

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'
local WFFileUtils = require 'WFFileUtils'
local WFDeviceInfo = require 'WFDeviceInfo'

	-- WF.core.lua
local KSData = require 'KSData'

local LrmCatalogTestHelper = require "LrmCatalogTestHelper"

local WFOzRenditionServerUtils = require 'WFOzRenditionServerUtils'

local LrmOzDevelopSession = require "LrmOzDevelopSession"

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

-- SPAM [ashj 2020-04-27]: LrmOzDevelopSessionTest uses RenditionServer
-- functions which are used only in Android as of now.
if not WFPlatformInfo.isAndroid then return {} end

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

local LrmOzDevelopSessionTest = {}

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

local TEST_TIMEOUT = 5

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

local function generateAsset( rxTest, catalog )
	local assetId = KSIdGenerator.uuidInLowercaseHex()

	local overlay = {
		type = 'asset',
		subtype = 'image',
		_id = assetId,
		importSource = {
			originalWidth = 100,
			originalHeight = 100,
		},
		_serverProvidedMetadata = {
			links = {
				[ '/rels/master' ] = assetId,
			}
		}
	}
	
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( overlay ),
		':ignore:' )
	
	return assetId

end

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

local function updateDevelopSection( rxTest, catalog, assetId, developSection )
	local overlay = {
		_id = assetId,
		type = 'asset',
		subtype = 'image',
		
		develop = developSection
	}
	
	local revId
	rxTest:expectSingleValue( catalog:docStore():applyOverlay( overlay )
		:tap( function( newRevId )
			revId = newRevId
		end ),
		':ignore:' )

	return revId
end

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

local function getMD5( assetId, ksdata )
	local newMD5 = ksdata:digest( 'MD5' )
	return WFOzRenditionServerUtils.getFullMD5HashedKey( newMD5, assetId )
end

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

local function stripEpoch( t )
	if t then
		t.epoch = nil
	end
end

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

local function createCatalogService( rxTest )

	local catalog, _, catalogId = LrmCatalogTestHelper.makeCatalogService( rxTest )
	
	rxTest:finally( function()
		catalog:close()
	end )
	
	return catalog, catalogId
end

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

function LrmOzDevelopSessionTest.rxTestUpdateRenditionsForDevSettings( rxTest )

	rxTest:setTimeLimit( TEST_TIMEOUT )
	
	local catalog = createCatalogService( rxTest )
	
	local photoId = generateAsset( rxTest, catalog )
	local developSession = LrmOzDevelopSession( catalog, photoId )
	local thumbData = KSData.make( "thumbKSData" )
	local previewData = KSData.make( "prevKSData" )
	local croppedWidth = 100
	local croppedHeight = 200
	
	-- Develop settings doesn't match
	
	rxTest:expectSingleValue( 
		developSession:updateRenditionsForDevSettings({
			thumbnail = thumbData,
			preview = previewData,
			xmpCameraRaw = "<xmp></xmp>",
			userOrientation = 1,
			croppedWidth = croppedWidth,
			croppedHeight = croppedHeight,
		}),
		false )
		
	
	-- Positive case
		
	local newRevId = updateDevelopSection( rxTest, catalog, photoId, {
		userUpdated = "2020-03-09T08:01:16.540Z",
		croppedWidth = croppedWidth,
		croppedHeight = croppedHeight,
		processingModel = "lightroom",
		userOrientation = 1,
		xmpCameraRaw = "<xmp/>"
	})
	
	rxTest:expectSingleValue( 
		developSession:updateRenditionsForDevSettings({
			thumbnail = KSData.make( "thumbKSData" ),
			preview = KSData.make( "prevKSData" ),
			xmpCameraRaw = "<xmp/>",
			userOrientation = 1,
			croppedWidth = croppedWidth,
			croppedHeight = croppedHeight,
		}),
		':ignore:' )
		
	
	local expectedTable = {
		thumbnail2x = {
			revId = newRevId,
			size = KSData.length( thumbData ),
			referenceWidth = croppedWidth,
			referenceHeight = croppedHeight,
			md5 = getMD5( photoId, thumbData )
		},
		preview = {
			revId = newRevId,
			size = KSData.length( previewData ),
			referenceWidth = croppedWidth,
			referenceHeight = croppedHeight,
			md5 = getMD5( photoId, previewData )
		},
	}
	
	local result = {}
	
	rxTest:expectSingleValue( 
		catalog:docStore():docValue( photoId )
			:tap( function( docValue )
				-- Take only relevant fields
				
				local thumbTable = docValue._localOnly[ 'thumbnail2x' ]
				stripEpoch( thumbTable )
				
				local previewTable = docValue._localOnly[ 'preview' ]
				stripEpoch( previewTable )
				
				result = {
					thumbnail2x = thumbTable,
					preview = previewTable,
				}
			end ),
		':ignore:' )
	
	rxTest:expectEqual( expectedTable, result )
	
end

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

function LrmOzDevelopSessionTest.rxTestSaveAndClose( rxTest )
	
	local catalog = createCatalogService( rxTest )
	
	local photoId = generateAsset( rxTest, catalog )

	local fakeThumbnail = KSData.make( 'fakeThumbnailData' )
	local fakeHighDpiThumbnail = KSData.make( 'fakeHighDpiThumbnailData' )

	local saveSettingsChanges = function( developSession, changes )
		return Rx.Observable.create(
			function( observer )

				developSession:saveAndCloseP( changes )
				:whenResolvedDo( function( status, result )
					if status == 'data' then
						observer:onNext( true )
						observer:onCompleted()
					else
						observer:onCompleted()
					end
				end )
				:runUntilDone()
			end )
	end

	local developSession = LrmOzDevelopSession( catalog, photoId )

	local changes2 = {
		appliedCameraProfileDigest = "54650a341b5b5ccae8442d0b43a92bce",
		cameraModelName = "JPEG",
		cameraProfileFileName = "JPEG Embedded.dcp",
		cameraProfileLink = "",
		croppedHeight = 960,
		croppedWidth = 1280,
		crsVersion = "12.3",
		fromDefaults = false,
		userOrientation = 8,
		thumbnail = fakeThumbnail,
		highDpiThumbnail = fakeHighDpiThumbnail,
		xmpCameraRaw = "xmpCameraRaw",
	}

	rxTest:expectSingleValue( saveSettingsChanges( developSession, changes2 ), true )

	local docObserver = rxTest:observe( catalog:docStore():ongoingDocValue( photoId )
	:map( function( docValue )
		return KSTableUtils.derefOpt( docValue, 'develop', 'device')
	end ) )

	local deviceId = changes2.deviceId

	if not deviceId then
		deviceId = WFDeviceInfo.uniqueDeviceId()
	end
	local deviceNameWithId = string.format( '%s {%s}', WFDeviceInfo.deviceName(), deviceId )
	docObserver:expectDataWithinTime( 3, deviceNameWithId )
end

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

function LrmOzDevelopSessionTest.rxTestSaveAndCloseDeleteOldExternalSettings( rxTest )
	rxTest:setTimeLimit( 10 )
	
	local catalog, catalogId = createCatalogService( rxTest )
	
	local OzFilePaths = require 'OzFilePaths'
	
	local photoId = generateAsset( rxTest, catalog )
	
	local fakeThumbnail = KSData.make( 'fakeThumbnailData' )
	local fakeHighDpiThumbnail = KSData.make( 'fakeHighDpiThumbnailData' )
	
	local saveSettingsChanges = function( developSession, changes )
		return Rx.Observable.create(
			function( observer )
			
				developSession:saveAndCloseP( changes )
					:whenResolvedDo( function( status, result )
						if status == 'data' then
							observer:onNext( true )
							observer:onCompleted()
						else
							observer:onCompleted()
						end
					end )
					:runUntilDone()
			end )
		end
		
	local xmpDevelopPathObserver = rxTest:observe( catalog:docStore():ongoingDocValue( photoId )
			:map( function( docValue )
				return KSTableUtils.derefOpt( docValue, '_localOnly', 'files', 'xmp_develop', 'path' )
			end ) )
	
	local developSession = LrmOzDevelopSession( catalog, photoId )
	local externalSettingsXmp = KSData.make( '<xmp/>' )
	local externalSettingsXmpSha256 = externalSettingsXmp:digest( 'SHA256' )
	local externalSettingsRelPath, externalSettingsAbsPath = OzFilePaths.getXmpDevelopPath( catalogId, { _id = photoId }, externalSettingsXmpSha256, true )
	KSData.saveToFile( externalSettingsXmp, externalSettingsAbsPath )
	
	local changes = {
		appliedCameraProfileDigest = "54650a341b5b5ccae8442d0b43a92bce",
		cameraModelName = "JPEG",
		cameraProfileFileName = "JPEG Embedded.dcp",
		cameraProfileLink = "",
		croppedHeight = 960,
		croppedWidth = 1280,
		crsVersion = "12.3",
		fromDefaults = false,
		localXmpDevelopPath = externalSettingsRelPath,
		userOrientation = 8,
		thumbnail = fakeThumbnail,
		highDpiThumbnail = fakeHighDpiThumbnail,
		xmpCameraRaw = {
			sha256 = externalSettingsXmpSha256,
		},
	}
	rxTest:expectSingleValue( saveSettingsChanges( developSession, changes ), true )
	
	xmpDevelopPathObserver:expectDataWithinTime( 3, externalSettingsRelPath )
	
	rxTest:expectEqual( true, not not WFFileUtils.exists( externalSettingsAbsPath ) )
	
	developSession = LrmOzDevelopSession( catalog, photoId )
	
	local externalSettingsXmp2 = KSData.make( '<xmp          />' )
	local externalSettingsXmpSha2562 = externalSettingsXmp2:digest( 'SHA256' )
	local externalSettingsRelPath2, externalSettingsAbsPath2 = OzFilePaths.getXmpDevelopPath( catalogId, { _id = photoId }, externalSettingsXmpSha2562, true )
	KSData.saveToFile( externalSettingsXmp2, externalSettingsAbsPath2 )
	
	rxTest:finally( function()
		WFFileUtils.delete( externalSettingsAbsPath )
		WFFileUtils.delete( externalSettingsAbsPath2 )
	end )

	local changes2 = {
		appliedCameraProfileDigest = "54650a341b5b5ccae8442d0b43a92bce",
		cameraModelName = "JPEG",
		cameraProfileFileName = "JPEG Embedded.dcp",
		cameraProfileLink = "",
		croppedHeight = 960,
		croppedWidth = 1280,
		crsVersion = "12.3",
		fromDefaults = false,
		localXmpDevelopPath = externalSettingsRelPath2,
		userOrientation = 8,
		thumbnail = fakeThumbnail,
		highDpiThumbnail = fakeHighDpiThumbnail,
		xmpCameraRaw = {
			sha256 = externalSettingsXmpSha2562,
		},
	}
	rxTest:expectSingleValue( saveSettingsChanges( developSession, changes2 ), true )
	
	xmpDevelopPathObserver:expectDataWithinTime( 3, externalSettingsRelPath2 )
	
	rxTest:expectEqual( true, not not WFFileUtils.exists( externalSettingsAbsPath2 ) )
	rxTest:expectEqual( false, WFFileUtils.exists( externalSettingsAbsPath ) )
	
	developSession = LrmOzDevelopSession( catalog, photoId )
	
	local changes3 = {
		appliedCameraProfileDigest = "54650a341b5b5ccae8442d0b43a92bce",
		cameraModelName = "JPEG",
		cameraProfileFileName = "JPEG Embedded.dcp",
		cameraProfileLink = "",
		croppedHeight = 960,
		croppedWidth = 1280,
		crsVersion = "12.3",
		fromDefaults = false,
		userOrientation = 8,
		thumbnail = fakeThumbnail,
		highDpiThumbnail = fakeHighDpiThumbnail,
		xmpCameraRaw = '<xmp/>'
	}
	rxTest:expectSingleValue( saveSettingsChanges( developSession, changes3 ), true )
	
	xmpDevelopPathObserver:expectDataWithinTime( 3, nil )
		
	rxTest:expectEqual( false, WFFileUtils.exists( externalSettingsAbsPath ) )
	rxTest:expectEqual( false, WFFileUtils.exists( externalSettingsAbsPath2 ) )
end

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

return WFRxTestCase.wrapSuite( 'LrmOzDevelopSessionTest', LrmOzDevelopSessionTest )
