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

ADOBE CONFIDENTIAL
------------------
Copyright 2014 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 KSPromises = require 'KSPromises'
local LrmAssetQueries = require 'LrmAssetQueries'

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

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local WFCapabilities = require 'WFCapabilities'

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

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

	-- WF.oz_client.catalog
local OzCatalogUrls = require 'OzCatalogUrls'
local OzCatHelperFns = require 'OzCatHelperFns'

	-- WF.oz_client.ims
local WFImsAppIdentity = require "WFImsAppIdentity"

	-- WF.web.http_client
local WFRxHttpClient = require 'WFRxHttpClient'

	-- WF.web.utils
local WFURI = require 'WFURI'

local logger = WFLogger "LrmWFOzProfileUtils"
--	:setLevel 'debug'

local asKSPromise = require 'WFRxPromiseBridge'

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

local LrmWFOzProfileUtils = {}

local kLensPresetDirectory = nil
local kCameraPresetDirectory = nil

local cAdobeStandardDcp = "Adobe Standard.dcp"
local cAdobeStandard = "Adobe Standard"

local cCameraSearch = " Camera "
local cCamera = "Camera"
		
--------------------------------------------------------------------------------

local function getLensPresetDirectory()

	if not kLensPresetDirectory then
		kLensPresetDirectory = assert( WFCapabilities.getCapability( 'WFOzAppConfig.lensPresetDirectory' ) )
	end
	
	return kLensPresetDirectory
	
end

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

local function getCameraPresetDirectory()

	if not kCameraPresetDirectory then
		kCameraPresetDirectory = assert( WFCapabilities.getCapability( 'WFOzAppConfig.cameraPresetDirectory' ) )
	end
	
	return kCameraPresetDirectory
	
end

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

local function getLocalProfileDirectory( profileType, filename )

	local cVersion = "1.0"
	local directoryPath
	
	if profileType == 'camera' then
	
		directoryPath = getCameraPresetDirectory()

		if string.find( filename, cAdobeStandardDcp .. "$" ) then
			directoryPath = KSPathUtils.child( directoryPath, cAdobeStandard )
		else
			directoryPath = KSPathUtils.child( directoryPath, cCamera )
			
			local sentinalPos = filename:find( cCameraSearch )

			if sentinalPos and sentinalPos > 0 then
				directoryPath = KSPathUtils.child( directoryPath, filename:sub( 1, sentinalPos ) )
			end
		end
		
	elseif profileType == 'lens' then
	
		directoryPath = getLensPresetDirectory()
		
		directoryPath = KSPathUtils.child( directoryPath, cVersion )
		directoryPath = KSPathUtils.child( directoryPath, "MakerName" )
		
	else
		-- error out
	end
	
	return directoryPath
	
end
 
--------------------------------------------------------------------------------

local function noncachedProfilesForAssetP( catalog, assetId )

	local sql = LrmAssetQueries.unsyncedCameraProfiles

	return catalog:indexStore():queryP( sql, assetId )
	:mapDataP( function( status, data )
	
		local profiles = {}
		
		for i=1, #data do
			local profile = {
				type = data[ i ][ 2 ],
				filename = data[ i ][ 3 ],
				masterUrl = data[ i ][ 4 ],
				uniqueId = data[ i ][ 5 ],
			}
			table.insert( profiles, profile )
		end
		
		return KSPromises.resolvedPromiseP( 'data', profiles )
	end )
end

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

local function ensureProfileExistP( catalog, profiles, index, isAdobeStandardProfile )

	local function getCustomDirectoryPathP(status, data, profile)
		return catalog:getCatalogIdP()
		:mapDataP( function( _ , catalogId )

			logger:debugf('Status:%s, catalogId:%s, profile.masterUrl:%s', status, catalogId, profile.masterUrl)

			if profile.userProfile or string.match(profile.masterUrl, catalogId) then

				local userCameraProfilesFolder = 'Profiles/Settings/UserCameraProfiles'
				local directoryPath = KSPathUtils.getStandardFilePath( 'carouselDocuments' )
				directoryPath = KSPathUtils.child( KSPathUtils.child(directoryPath, catalogId), userCameraProfilesFolder)

				logger:debugf('Custom user path is being returned, dirPath:%s', directoryPath)

				return KSPromises.resolvedPromiseP( status, data, directoryPath )
			end

			logger:debugf('returning common dcp Storage:%s, profile.userProfile:%s', status, profile.userProfile)

			return KSPromises.resolvedPromiseP( status, data )

		end )
	end
	if index <= #profiles then
	
		local profile = profiles[index]
		local profileType = profile.type
		local filename = profile.filename
		
		local imageBaseUrl = WFCapabilities.getCapability( "OzCatalogUrls.downloadImagesBaseUrl" )
	
		if not imageBaseUrl then
			imageBaseUrl = OzCatalogUrls.baseServerUrl
		end

		local directoryPath = getLocalProfileDirectory( profileType, filename )
		local filePath = KSPathUtils.child( directoryPath, filename )
		local skipDownload = false

		if WFFileUtils.exists (filePath) and isAdobeStandardProfile then

			skipDownload = true

		end

		return catalog
		:ozUserP()
		:mapDataP_new( function( user )
			if not skipDownload then -- download only when file is not present locally or if it is custom profile
				if user:userId() == "00000000000000000000000000000000" then
					local freemiumUrl = imageBaseUrl .. "/v2/profiles?subtype=" ..
										profileType .. "_profile&file_name=" .. WFURI.escapeWithPlusAndMinus( filename )

					local imsClientId = WFImsAppIdentity.getClientId() -- we don't care about secret
					local options = {
						requestHeaders = {
							{ header = 'X-Api-Key', value = imsClientId }
						}
					}

					return WFRxHttpClient.get( freemiumUrl, options )
					:call( asKSPromise )
				else
					local ozUrl = OzCatHelperFns.checkForRelativeUrlAndAppendBase( profile.masterUrl, imageBaseUrl )

					return user:get( ozUrl )
					:call( asKSPromise )
				end
			end

			return KSPromises.resolvedPromiseP( 'data', true )

		end )
		
		:mapDataP( function( status, data )

			if WFPlatformInfo.isAndroid then

				return getCustomDirectoryPathP(status, data, profile)

			end

			return status, data

		end )

		:mapDataP( function( status, data, directoryPath )

			if WFPlatformInfo.isAndroid then -- Store only when file is downloaded

				if not skipDownload then

					logger:debugf('recieved status:%s, directoryPath:%s', status, directoryPath)

					if not directoryPath then

						directoryPath = getLocalProfileDirectory( profileType, filename )

					end

					logger:debugf('received directoryPath:%s, fileName:%s', directoryPath, filename)

					local filePath = KSPathUtils.child( directoryPath, filename )

					local WFCrypto = require 'WFCrypto'

					local expectedSHA256 = nil

					if WFFileUtils.exists (filePath) then

						expectedSHA256 = WFCrypto.digestFile( filePath, 'SHA256' )

					end

					local recievedDigest = WFCrypto.SHA256.digest( tostring(data) )

					if expectedSHA256 == nil or expectedSHA256 ~= recievedDigest then

						WFFileUtils.mkdir( directoryPath )

						local success
						success, filePath = pcall( KSData.saveToUniquelyNamedFile, data, filePath )

						logger:debugf('received newFilename:%s', filePath)

						if not success then
							return KSPromises.resolvedPromiseP( 'error', false )
						end

					end

					profile.filename = filePath -- updating the filename if it has changed as part of saveToUniquelyNamedFile

				end

			else

				local directoryPath = getLocalProfileDirectory( profileType, filename )

				local filePath = KSPathUtils.child( directoryPath, filename )

				WFFileUtils.mkdir( directoryPath )

				logger:debugf( "save profile at %s for %s", filePath, profileType )

				KSData.saveToFile( data, filePath )

			end

			return KSPromises.resolvedPromiseP( 'data', profile )
			
		end )
		:mapDataP( function( status, profile )
		
			if status == 'data' then
			
				local profileId = KSIdGenerator.uuidInLowercaseHex()
				
				local innerResultP = catalog:docStore():applyOverlayToDocumentP {
						docType = 'profile',
						docIds = profileId,
						overlay = {
							_localOnly = {
								subtype = profile.type,
								fileName = profile.filename,
								uniqueId = profile.uniqueId,
							}
						},
					}
				return innerResultP
				
			end
			
			return KSPromises.resolvedPromiseP( 'data', true )
						
		end )
		:mapP( function( _, data )

			if WFPlatformInfo.isAndroid and index == #profiles then
				return KSPromises.resolvedPromiseP( _, data )
			end

			return ensureProfileExistP( catalog, profiles, index + 1, isAdobeStandardProfile)
		end )
	else
		return KSPromises.resolvedPromiseP( 'data', true )
	end
	
end

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

-- Android Specific
function LrmWFOzProfileUtils.ensureCustomCameraProfilesExistLocallyP( catalog, binaryInfo )

	local updateAcrProfileCache = false

	local profilesP
	if #binaryInfo >= 1 then

		updateAcrProfileCache = true
		profilesP = ensureProfileExistP( catalog, binaryInfo, 1, false )

	else

		profilesP = KSPromises.resolvedPromiseP( 'data', true )

	end

	return profilesP
	:mapP( function( status, data )

		local result = {
			status = status,
			updateAcrProfileCache = updateAcrProfileCache
		}

		return KSPromises.resolvedPromiseP( status, result  )

	end )
end

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

function LrmWFOzProfileUtils.ensureProfilesExistLocallyP( catalog, assetId )

	local updateAcrProfileCache = false
	
	return noncachedProfilesForAssetP( catalog, assetId )
		:mapDataP( function( _, profiles )
		
			if #profiles >= 1 then
			
				updateAcrProfileCache = true
				return ensureProfileExistP( catalog, profiles, 1, true )
				
			else
				return KSPromises.resolvedPromiseP( 'data', true )
			end
			
		end )
		:mapP( function( status, data )
		
			local result = {
				status = status,
				updateAcrProfileCache = updateAcrProfileCache
			}
			
			return KSPromises.resolvedPromiseP( 'data', result  )
			
		end )
	
end

--------------------------------------------------------------------------------
-- Android Specific
function LrmWFOzProfileUtils.getCustomCameraProfilesP(catalog, profileDigest, cameraModelName)

	-- First convert them to upper case characters
	local KSStringUtils = require 'KSStringUtils'
	cameraModelName = cameraModelName and KSStringUtils.upper( cameraModelName ) or cameraModelName
	profileDigest = profileDigest and KSStringUtils.upper( profileDigest ) or profileDigest

	return catalog:indexStore()
	:queryP ( [[ SELECT filename, url from CustomCameraProfiles WHERE digest = ? AND modelName = ? ]]
		, profileDigest, cameraModelName)
	:mapP(function(_, data)

		logger:debugf('_dcp, query response for custom dcp sync:%s', data)

		if data and data[1] and data [1][1] and data[1][2] then
			return 'data', data[1][1], data[1][2]
		end

		return 'data' -- rest all fields will become nil
	end)
end

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

return LrmWFOzProfileUtils
