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

ADOBE CONFIDENTIAL
------------------
Copyright 2019 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"

local OzFilePaths = require "OzFilePaths"
local KSData = require "KSData"
local KSTableUtils = require "KSTableUtils"
local KSPathUtils = require "KSPathUtils"

local WFCrypto = require "WFCrypto"

local WFRxHttpServerUtils = require "WFRxHttpServerUtils"

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

local derefOpt = assert( KSTableUtils.derefOpt )


local typeToPrefix = {
	preview = 'pv_',
	thumbnail2x= 'th_',
}

local contentTypeMap = {
	jpg = 'image/jpeg',
	jpeg = 'image/jpeg',
	png = 'image/png',
	tiff = 'image/tiff',
	gif = 'image/gif',
	mpeg = 'video/mpeg',
	mpg = 'video/mpeg',
	mp4 = 'video/mp4',
	qt = 'video/quicktime',
	mov = 'video/quicktime',
	avi = 'video/x-msvideo',
	ogg = 'video/ogg',
	ogv = 'video/ogg'
}

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

local function verifyPeer( info, catalog )

	if not info.clientCert then
		return false, { statusCode = 401, data = 'no client cert' }
	end

	local catalogHash = KSData.make( catalog:ozCatalogSpec():catalogId() ):digest( 'SHA224' )

	local splitSubject = WFCrypto.splitSubject( info.clientCert.subject )

	local requiredSubject = {
		C = 'US',
		ST = 'California',
		L = 'San Jose',
		O = 'Adobe Inc.',
		OU = 'Digital Imaging',
		CN = catalogHash
	}

	if not KSTableUtils.deepEquals( splitSubject, requiredSubject ) then
		return false, { statusCode = 401, data = 'go away' }
	end

	return true
end

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

local handlers = {
	{ '^/hello$', { 'GET' }, function( path, info, catalog )

		local verified, response = verifyPeer( info, catalog )

		if not verified then
			return Rx.Observable.just( response )
		end

		return Rx.Observable.just { contentType = 'text/plain', data = 'moin' }
	end },
	{ '^/assets/(%x+)/(%x+)/(%x+)/(%w+)\.(%w+)$', { 'GET' }, function( path, info, catalog )

		local verified, response = verifyPeer( info, catalog )

		if not verified then
			return Rx.Observable.just( response )
		end

		if #info.matches ~= 5 then
			return Rx.Observable.just{ statusCode = 400, data = 'Bad Request' }
		end

		for i = 1, 2 do
			if #info.matches[ i ] ~= 32 then
				return Rx.Observable.just{ statusCode = 400, data = 'Bad Request' }
			end
		end

		if #info.matches[ 3 ] ~= 32 and #info.matches[ 3 ] ~= 64 then
			return Rx.Observable.just{ statusCode = 400, data = 'Bad Request' }
		end

		local catalogId, assetId, contentId, renditionName, ext = unpack( info.matches )

		if catalogId ~= catalog:ozCatalogSpec():catalogId() then
			return Rx.Observable.just{ statusCode = 400, data = 'Bad Request' }
		end
		
		local contentType = 'application/octet-stream'

		if ext then

			contentType = contentTypeMap[ ext:lower() ]
			if not contentType then
				contentType = 'application/octet-stream'
			end

		end

		local basePath = OzFilePaths.getCatalogBasePath( catalogId )
		local filePath

		filePath = OzFilePaths.getRelativeBlobPath( assetId, catalogId )
		filePath = KSPathUtils.child( basePath, filePath )

		local prefix = typeToPrefix[ renditionName ]

		if prefix then

			filePath = KSPathUtils.child( filePath, prefix .. contentId .. '.' .. ext )

			return Rx.Observable.just { contentType = contentType, filePath = filePath }
		end

		if renditionName ~= 'master' and renditionName ~= 'proxy' then
			return Rx.Observable.just{ statusCode = 400, data = 'Bad Request' }
		end

		-- the 'master' is stored as 'original' in cacheReferences
		if renditionName == 'master' then
			renditionName = 'original'
		end

		-- for proxy and master, we need to query the indexStore

		return catalog:indexStore():queryOnce{
				sql = 'SELECT path from cacheReferences WHERE assetId = ? AND renditionName = ?',
				singleColumnQuery = true,
				assetId,
				renditionName
			}
			:map( function( paths )

				local path = derefOpt( paths, 1 )
				if not path then
					return { statusCode = 404, data = string.format('%s %s not found', renditionName, assetId ) }
				end

				filePath = KSPathUtils.child( basePath, path )

				return { contentType = contentType, filePath = filePath }

			end )

	end },
}

local LriP2PHttpServerHandlers = {}

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

function LriP2PHttpServerHandlers.startRequest( uri, requestParams, ... )

	return { 
		value = WFRxHttpServerUtils.startRequest( handlers, uri, requestParams, ... )
	}

end

return LriP2PHttpServerHandlers