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

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

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

local WFLogger = require 'WFLogger'

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

local logger = WFLogger 'WFCatAssetInfoListUtils'

local WFCatAssetInfoListUtils = {}

local WFPerformanceUtils = require "WFPerformanceUtils"

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

local unpackedAssetRequiredFields = "docId, width, height, subtype, orientation, captureDate, duration, fileName, rating"

local packedAssetInfoEqual

--==============================================================================
-- @mark Transit Raw Query Asset Info
--==============================================================================

-- The raw query result is transited in this case (array of arrays)

function WFCatAssetInfoListUtils.getRequiredFields()

	return unpackedAssetRequiredFields

end

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

WFCatAssetInfoListUtils.getPackedAssetInfoDetails = function( currentAssetInfoFromQuery, additionalDataForAsset )

	currentAssetInfoFromQuery[ 10 ] = additionalDataForAsset
	return currentAssetInfoFromQuery

end

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

WFCatAssetInfoListUtils.getPackedAssetInfoDetailsForArray = function( queryResult )

	return queryResult

end

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

function WFCatAssetInfoListUtils.processPackedAssetInfo( packedAssetInfo )

	-- It is too risky at the moment to leave the assetInfo in packed form
	-- for the grid to use as other things like share and export
	-- refer to assetInfos. So unpack everything during processPackedAssetInfo
	-- to reduce risk.

	if packedAssetInfo[ 1 ] then

		local assetInfo = {
			id = packedAssetInfo[ 1 ],
			width = packedAssetInfo[ 2 ],
			height = packedAssetInfo[ 3 ],
			subtype = packedAssetInfo[ 4 ],
			orientation = packedAssetInfo[ 5 ],
			captureDate = packedAssetInfo[ 6 ],
			duration = packedAssetInfo[ 7 ],
			fileName = packedAssetInfo[ 8 ],
			aspectRatio = packedAssetInfo[ 2 ] / packedAssetInfo[ 3 ],
			rating = packedAssetInfo[ 9 ],
			additionalAssetInfo = packedAssetInfo[ 10 ],
		}

		return assetInfo.id, assetInfo.subtype, assetInfo

	end

	return packedAssetInfo.id, packedAssetInfo.subtype, packedAssetInfo

end

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

function WFCatAssetInfoListUtils.unpackAssetIdAndSubtype( packedAssetInfo )

	if packedAssetInfo[ 1 ] then
		return packedAssetInfo[ 1 ], packedAssetInfo[ 4 ]
	end

	return packedAssetInfo.id, packedAssetInfo.subtype

end

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

function WFCatAssetInfoListUtils.unpackAssetInfoDetailsToTable( packedAssetInfo )

	-- For now table was already unpacked in processPackedAssetInfo.
	return packedAssetInfo

end

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

packedAssetInfoEqual = function( a, b )

	return a and b
		and a[ 1 ] == b[ 1 ]
		and	a[ 2 ] == b[ 2 ]
		and	a[ 3 ] == b[ 3 ]
		and	a[ 4 ] == b[ 4 ]
		and	a[ 5 ] == b[ 5 ]
		and	a[ 6 ] == b[ 6 ]
		and	a[ 7 ] == b[ 7 ]
		and	a[ 8 ] == b[ 8 ]
		and	a[ 9 ] == b[ 9 ]
		and	a[ 10 ] == b[ 10 ]

end

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

local function copyPackedTable( t )

	local copy = {}

	for i, v in ipairs( t ) do
		copy[ i ] = {
			v[ 1 ], v[ 2 ], v[ 3 ], v[ 4 ], v[ 5 ], v[ 6 ], v[ 7 ], v[ 8 ], v[ 9 ], v[ 10 ]
		}
	end

	return copy

end

--==============================================================================
-- @mark merge and split routines for sending asset info as diffs
--==============================================================================

local prevMergePackedAssetInfos = {}
-- The client only uses prevMergePackedAssetInfos, however when running
-- unit tests we need to have the prevPackedAssetInfos in separate lists as the
-- test runs both the client and daemon side of things.

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

function WFCatAssetInfoListUtils.mergePackedAssetInfoTable( packedAssetInfos)

	local requestNumber = packedAssetInfos.requestNumber
	if requestNumber == nil then
		-- Currently trash doesn't split/merge so have a path where we jsut return what came in
		return packedAssetInfos
	end

	local keepTopTo = packedAssetInfos.keepTopTo
	local keepBottomFrom = packedAssetInfos.keepBottomFrom
	local queryUpdateCount = packedAssetInfos.queryUpdateCount

	logger:infof( "mergePackedAssetInfoTable %s:%s keepTopTo = %s keepBottomFrom %s", requestNumber, queryUpdateCount, keepTopTo, keepBottomFrom )

	local mergedPackedAssetInfos = {}

	if keepTopTo then

		if prevMergePackedAssetInfos.requestNumber and requestNumber ~= prevMergePackedAssetInfos.requestNumber then
			logger:errorf( "mergePackedAssetInfoTable requestNumber wrong request %s keepTopTo = %s keepBottomFrom %s, sending %s rows", requestNumber, keepTopTo, keepBottomFrom, #packedAssetInfos )
			error( "invalid requestNumber" )
		end

		if queryUpdateCount ~= prevMergePackedAssetInfos.queryUpdateCount + 1 then
			logger:errorf( "mergePackedAssetInfoTable queryUpdateCount wrong %s ~= %s request %s keepTopTo = %s keepBottomFrom %s, sending %s rows", queryUpdateCount, prevMergePackedAssetInfos.queryUpdateCount + 1, requestNumber, keepTopTo, keepBottomFrom, #packedAssetInfos )
			error( "invalid queryUpdateCount" )
		end

		for i = 1, packedAssetInfos.keepTopTo do
			mergedPackedAssetInfos[ i ] = prevMergePackedAssetInfos[ i ]
		end

		for i = 1, #packedAssetInfos do
			mergedPackedAssetInfos[ #mergedPackedAssetInfos + 1 ] = packedAssetInfos[ i ]
		end

		if packedAssetInfos.keepBottomFrom then
			for i = packedAssetInfos.keepBottomFrom, #prevMergePackedAssetInfos do
				mergedPackedAssetInfos[ #mergedPackedAssetInfos + 1 ] = prevMergePackedAssetInfos[ i ]
			end
		end

		-- Only need to make a new copy of prevResults if it changed.
		if #packedAssetInfos > 0 or packedAssetInfos.keepTopTo ~= #prevMergePackedAssetInfos then
			prevMergePackedAssetInfos = copyPackedTable( mergedPackedAssetInfos )
			prevMergePackedAssetInfos.requestNumber = requestNumber
		end

		-- Always update the updateCount even if we don't need to copy a new table
		prevMergePackedAssetInfos.queryUpdateCount = queryUpdateCount or 1

	else

		-- We have a full query result, this happens on the first query for a requestNumber.
		mergedPackedAssetInfos = copyPackedTable( packedAssetInfos )
		prevMergePackedAssetInfos = packedAssetInfos
		packedAssetInfos.queryUpdateCount = queryUpdateCount or 1

	end

	return mergedPackedAssetInfos

end

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

local prevSplitPackedAssetInfos = {}
-- The daemon only uses prevSplitPackedAssetInfos, however when running
-- unit tests we need to have the prevPackedAssetInfos in separate lists as the
-- test runs both the client and daemon side of things.

local prevQueryUpdateCount
-- prevQueryUpdateCount is used by split to keep track of the updateCount

function WFCatAssetInfoListUtils.splitPackedAssetInfoTable( packedAssetInfos, requestNumber, optDontSuppressUnchangedAssetList )

	local function copyPackedTableWithRequestNumber( packedAssetInfos )

		local copiedTable = copyPackedTable( packedAssetInfos )
		copiedTable.requestNumber = packedAssetInfos.requestNumber
		return copiedTable

	end

	if prevSplitPackedAssetInfos == nil or requestNumber ~= prevSplitPackedAssetInfos.requestNumber then

		-- Currently trash doesn't split/merge so have a path where we jsut return what came in

		if requestNumber then
			packedAssetInfos.requestNumber = requestNumber
			packedAssetInfos.queryUpdateCount = 1
			prevQueryUpdateCount = 1
			prevSplitPackedAssetInfos = copyPackedTableWithRequestNumber( packedAssetInfos )
		end

		logger:infof( "splitPackedAssetInfoTable %s:%s sending all %s rows", requestNumber, packedAssetInfos.queryUpdateCount, #packedAssetInfos )

		return packedAssetInfos, true

	end

	local queryUpdateCount = prevQueryUpdateCount or 1
	queryUpdateCount = queryUpdateCount + 1
	prevQueryUpdateCount = queryUpdateCount

	packedAssetInfos.requestNumber = requestNumber
	packedAssetInfos.queryUpdateCount = queryUpdateCount

	local keepTopTo
	local keepBottomFrom

	-- Find the first changed asset info
	for i = 1, #packedAssetInfos do
		if not packedAssetInfoEqual( packedAssetInfos[ i ], prevSplitPackedAssetInfos[ i ] ) then
			keepTopTo = i - 1
			break
		end
	end

	if keepTopTo then

		-- Something in the list is different, start from the bottom
		-- to find the first changed asset from the bottom.
		local copyTo
		local prevPackedInfosIndex = #prevSplitPackedAssetInfos
		for i = #packedAssetInfos, keepTopTo, -1 do
			if not packedAssetInfoEqual( packedAssetInfos[ i ], prevSplitPackedAssetInfos[ prevPackedInfosIndex ] ) then
				if prevPackedInfosIndex < #prevSplitPackedAssetInfos then
					keepBottomFrom = prevPackedInfosIndex + 1
				end
				copyTo = i
				break
			end
			prevPackedInfosIndex = prevPackedInfosIndex - 1
		end

		local changedPackedAssetInfos = {}

		-- Copy the chunk of assets that fall between the first and last change.
		for i = keepTopTo + 1, copyTo do
			changedPackedAssetInfos[ #changedPackedAssetInfos + 1 ] = packedAssetInfos[ i ]
		end

		-- Set up the metadata for merge to use to verify the array
		-- is the correct sequence number and what should be kept
		-- from the previous info array.
		changedPackedAssetInfos.requestNumber = requestNumber
		changedPackedAssetInfos.queryUpdateCount = queryUpdateCount
		changedPackedAssetInfos.keepTopTo = keepTopTo
		changedPackedAssetInfos.keepBottomFrom = keepBottomFrom

		-- Save off the results from this query to prevResults for the next query to compare against.
		prevSplitPackedAssetInfos = copyPackedTableWithRequestNumber( packedAssetInfos )

		logger:infof( "splitPackedAssetInfoTable %s:%s keepTopTo = %s keepBottomFrom %s, sending %s rows", requestNumber, queryUpdateCount, keepTopTo, keepBottomFrom, #changedPackedAssetInfos )

		return changedPackedAssetInfos, true

	else

		local somethingChanged = #packedAssetInfos ~= #prevSplitPackedAssetInfos

		logger:infof( "splitPackedAssetInfoTable %s:%s sending nothing all %s rows matched, somethingChanged=%s", requestNumber, queryUpdateCount, #packedAssetInfos, somethingChanged )

		if somethingChanged or optDontSuppressUnchangedAssetList then

			local changedPackedAssetInfos = {}
			changedPackedAssetInfos.requestNumber = requestNumber
			changedPackedAssetInfos.queryUpdateCount = queryUpdateCount
			changedPackedAssetInfos.keepTopTo = #packedAssetInfos
			-- don't set keepBottomFrom as everything kept is at the top of the list.

			prevSplitPackedAssetInfos = copyPackedTableWithRequestNumber( packedAssetInfos )

			-- list was truncated just send an empty list, with fields
			return changedPackedAssetInfos, true

		else

			-- nothing changed, caller should suppress the results via filter.
			prevQueryUpdateCount = queryUpdateCount - 1
			return nil, false

		end

	end

end

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

WFPerformanceUtils.wrapInLogLongExecutionInDiagnosticLogWithStaticDescription(
		"WFCatAssetInfoListUtils", .1, "WFCatAssetInfoListUtils.mergePackedAssetInfoTable", WFCatAssetInfoListUtils.mergePackedAssetInfoTable )

WFCatAssetInfoListUtils.splitPackedAssetInfoTable =
	WFPerformanceUtils.wrapInLogLongExecutionInDiagnosticLogWithStaticDescription(
		"WFCatAssetInfoListUtils", .1, "WFCatAssetInfoListUtils.splitPackedAssetInfoTable", WFCatAssetInfoListUtils.splitPackedAssetInfoTable )

copyPackedTable =
	WFPerformanceUtils.wrapInLogLongExecutionInDiagnosticLogWithStaticDescription(
		"WFCatAssetInfoListUtils", .1, "copyPackedTable", copyPackedTable )

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

return WFCatAssetInfoListUtils
