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

WFCatRenderSize.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

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

	-- WF.core.utils
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'
local WFFunctionUtils = require 'WFFunctionUtils'

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

local logger = require "WFLogger"( "WFCatRenderSize" )

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

local check = assert( WFArguments.check, "WFArguments.check not defined" )

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

local KSTableUtils_derefOpt = assert( KSTableUtils.derefOpt, 
										"KSTableUtils.derefOpt not defined" )

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

local WFFunctionUtils_reduce = assert( WFFunctionUtils.reduce,
										"WFFunctionUtils.reduce not defined" )

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

local function isNumericArgument( longSide )

	check( 'number' )
	
	return longSide

end

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

local function makeValidatedLongSideArray( longSideArray )

	local validated = WFFunctionUtils.map( isNumericArgument, longSideArray )

	table.sort( validated )

	return validated

end

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

local _defaultLongSide = 1024

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

local function getCroppedDimensions( docStoreValue )

	return tonumber( KSTableUtils_derefOpt( docStoreValue,
											'develop', 'croppedWidth' ) ),
			tonumber( KSTableUtils_derefOpt( docStoreValue,
											'develop', 'croppedHeight' ) )

end

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

local function getMaxCroppedSide( docStoreValue )

	local croppedWidth, croppedHeight = getCroppedDimensions( docStoreValue )

	if croppedWidth and croppedHeight then

		return math.max( croppedWidth, croppedHeight )

	end

	if croppedWidth then
		
		return croppedWidth
		
	end
	
	if croppedHeight then
		
		return croppedHeight
		
	end

	if logger.will_info then

		logger:infof( "No cropped dimensions in: %s", 
						type( docStoreValue ) == 'table' and 
							require "WFTableArchiving".tableToString(
							docStoreValue ) or
							docStoreValue )

	end
	
	return _defaultLongSide

end

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

local function getAspectRatio( docStoreValue )

	local croppedWidth, croppedHeight = getCroppedDimensions( docStoreValue )

	if not croppedWidth or not croppedHeight or croppedWidth <= 0 or
			croppedHeight <= 0 then

		return 1.0

	end

	return croppedWidth / croppedHeight

end

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

local function getMinBigEnoughRenditionSize( bigEnoughRenditionSize,
												renditionSize, neededSize )

	if bigEnoughRenditionSize then

		return bigEnoughRenditionSize

	end

	return neededSize <= renditionSize and renditionSize or nil

end

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

local function isValidRenderSizeArgument( renderSize )

	return renderSize == "full" or
			type( renderSize ) == "number" and renderSize >= 0

end

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

local _maxProxyRenderSize = 2560

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

local function canRenderWithProxy( renderSize )

	-- TODO / FIXME: extract a size from things like '360p' to avoid
	-- cascading errors.  There's sure to be a better way to handle this
	local fixedRenderSize = tonumber(string.gsub(renderSize, '(%d+).*', '%1'), 10)
	if type(fixedRenderSize) ~= 'number' then
		fixedRenderSize = renderSize
	end

	if not isValidRenderSizeArgument( fixedRenderSize ) then

		error( "invalid renderSize argument: " .. tostring(fixedRenderSize) )

	end

	-- FIXME? jandrew 2016.01.27
	-- In the event renderSize == 'full', should we also test that the
	-- original ( master ) image size is <= _maxProxyRenderSize?

	return fixedRenderSize == "full" or fixedRenderSize <= _maxProxyRenderSize

end

--------------------------------------------------------------------------------
-- TODO 
--
-- 2015.11.10 bkruse
--
-- this shouldn't be hardcoded, should be populated by Oz

local _defaultRenderSizes = { 320, 640, 1280, 2048, 8192 }

--------------------------------------------------------------------------------
-- makes a function of signature:
--
-- 	function( maxLongSide, wantedLongSide ) 
-- 	end
--
-- which returns either "full" if a full size render is needed or a normalized
-- longSide that is an element in longSideArray
--
-- Real world use: given the long side of a 1:1 image and a set of of Oz
-- rendition sizes, return the best long side value that will cover an area
-- represented by wantedLongSide
--------------------------------------------------------------------------------

local function makeBoundedRenderSizeFunction( longSideArray )

	check( "table" )

	local validLongSideArray = makeValidatedLongSideArray( longSideArray )

	return function( maxLongSide, wantedLongSide, subtype )

		check( "number", "number|string" )
		
		local maxSizeForSubtype = ( subtype == 'video' or subtype == 'deleted_video' ) and 2048 or "full"
		
		if type( wantedLongSide ) ~= 'number' then

			return wantedLongSide

		end
		
		if wantedLongSide >= tonumber( maxLongSide ) then
		
			return maxSizeForSubtype

		end

		local renditionSize = WFFunctionUtils_reduce( nil,
								getMinBigEnoughRenditionSize,
									validLongSideArray, wantedLongSide )

		if not renditionSize then

			return maxSizeForSubtype

		end

		if renditionSize >= maxLongSide then

			return maxSizeForSubtype

		end

		return renditionSize

	end

end

--------------------------------------------------------------------------------
-- makes a function of signature:
--
-- 	function( wantedLongSide ) 
-- 	end
--
-- which returns a normalized longSide that is an element in longSideArray
--
-- Real world use: given the long side of a 1:1 image and a set of of Oz
-- rendition sizes, return the best long side value that will cover an area
-- represented by wantedLongSide
--------------------------------------------------------------------------------

local function makeNormalizedRenderSizeFunction( longSideArray )

	check( "table" )

	local validLongSideArray = makeValidatedLongSideArray( longSideArray )

	local longSideArrayCount = #validLongSideArray

	local maxLongSide = validLongSideArray[ #validLongSideArray ]

	return function( wantedLongSide )

		if type( wantedLongSide ) ~= "number" then

			logger:warnf( "Invalid long side %s, defaulting to %s",
								wantedLongSide,
								validLongSideArray[ 1 ] )

			return validLongSideArray[ 1 ]

		end

		local i = longSideArrayCount

		while i >= 2 do

			if wantedLongSide > maxLongSide then

				return maxLongSide

			end

			if wantedLongSide <= validLongSideArray[ i ] and
					wantedLongSide > validLongSideArray[ i - 1 ] then

				return validLongSideArray[ i ]

			end

			i = i - 1

		end

		return validLongSideArray[ 1 ]

	end

end

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

return {

	getMaxCroppedSide = getMaxCroppedSide,

	getAspectRatio = getAspectRatio,

	canRenderWithProxy = canRenderWithProxy,

	defaultRenderSizes = _defaultRenderSizes,

	getBoundedRenderSize = makeBoundedRenderSizeFunction( 
								_defaultRenderSizes ),

	getNormalizedRenderSize = makeNormalizedRenderSizeFunction( 
								_defaultRenderSizes ),

	private_UnitTestExports = {

		makeBoundedRenderSizeFunction = makeBoundedRenderSizeFunction,

	}

}
