
local LrLogger = import 'LrLogger'
local LrPathUtils = import 'LrPathUtils'
--local LrTasks = import 'LrTasks'
--local LrFunctionContext = import 'LrFunctionContext'
local LrFileUtils = import "LrFileUtils"


local LrRemoteCommunication = import 'LrRemoteCommunication'

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

local LeicaTetheredTetherTask = {}

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

local logger = LrLogger 'LeicaTetheredTetherTask'
	-- not enabled by default

--local ddt = import 'LrTableUtils'.debugDumpTable
local launchAndConnectToImageCaptureTetherServer

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

function LeicaTetheredTetherTask.init( propertyTable, pluginPath, completionCallback )
	-- Attempt to find and launch the tether application
	
	logger:trace( "plugin path: ", pluginPath, " callback: ", completionCallback )
	
	LeicaTetheredTetherTask._connectedToPluginApp = false
	LeicaTetheredTetherTask._sentListDevicesMessage = false
	LeicaTetheredTetherTask._propertyTable = propertyTable
	
	launchAndConnectToImageCaptureTetherServer( pluginPath, completionCallback )
end

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

function launchAndConnectToImageCaptureTetherServer( pluginPath, completionCallback )
	-- Attempt a connection
	
	local taskPath
	if MAC_ENV then
	
		-- First try our debug location in case the app is there.
		local parent = LrPathUtils.parent( pluginPath )
		local testPath = LrPathUtils.child( parent, "leica-tethered.app/Contents/MacOS/leica-tethered" )
		if LrFileUtils.exists( testPath ) then
			taskPath = testPath
		end
	
		if not taskPath then
			taskPath = LrPathUtils.child( pluginPath, "Contents/PlugIns/leica-tethered.app/Contents/MacOS/leica-tethered" )
		end
		
		logger:trace( "launching plugin app with path: ", taskPath )
	else
		taskPath = pluginPath .. "/leica-tethered.exe"
		
		logger:trace( "launching plugin app with path: ", taskPath )
	end

	LrRemoteCommunication.spawnTaskAndConnect( "tether_imageCapture", taskPath,
		function( serverRef )
			logger:trace( "launchAndConnectToImageCaptureTetherServer - serverRef: ", serverRef )
			LeicaTetheredTetherTask._serverConnection = serverRef
			if completionCallback then completionCallback( serverRef ~= nil ) end
		end )
	
end

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

function LeicaTetheredTetherTask.shutdown( propertyTable )
	-- Send a message to the server asking it to shut down
	if LeicaTetheredTetherTask._serverConnection then
		logger:trace( "LeicaTetheredTetherTask.shutdown" )
		LrRemoteCommunication.closeNamedConnection( LeicaTetheredTetherTask._serverConnection, "tether_imageCapture" )
	end
end

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

function LeicaTetheredTetherTask.setCallback( propertyTable, callback )
	LeicaTetheredTetherTask._hostCallback = callback
end

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

local function getCaptureInfoCallback( captureId, messageType, result )
	local device = LeicaTetheredTetherTask._connectedDevice
	local capture = device.captures[ captureId ]
	
	logger:trace( 'getCaptureInfoCallback: Received capture info, now starting file download.' )
	
	if messageType == "ok" then
		capture.captureInfo = result
	elseif messageType == "error" then
		capture.captureInfoErr = result
	end
	
	LeicaTetheredTetherTask._hostCallback( "fileInfoAvailable", 
						{ captureId = captureId, 
							fileType = LrPathUtils.extension( result.szFileName ),
							fileLength = result.size, } )

	LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "startDownload", { capture_id = captureId }, 
				function( messageType, result ) if messageType == "error" then capture.errorCode = result end end )
end

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

function LeicaTetheredTetherTask.tetherCallback( propertyTable, messageType, result )
	logger:trace( "LeicaTetheredTetherTask.tetherCallback - message: ", messageType )

	if messageType == "objectWasAdded" then
		if LeicaTetheredTetherTask._hostCallback then
			local device = LeicaTetheredTetherTask._connectedDevice
			if not device then
				logger:error( "LeicaTetheredTetherTask.tetherCallback: Could not locate device." )
				return
			end
			local captureId = result.capture_id
			if not device.captures then
				device.captures = {}
			end
			if not device.captures[ captureId ] then
				device.captures[ captureId ] = {}
			end
			
			logger:tracef( "objectWasAdded - device %s, device.captures = %s, device.captures[%s] = %s", device, device.captures, captureId, device.captures[captureId] )
			
			local capture = device.captures[ captureId ]
			
			-- Immediately request info about the capture

			LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "getCaptureInfo", { capture_id = captureId }, 
						function( messageType, result ) getCaptureInfoCallback( captureId, messageType, result ) end )
			
			capture.requestedInfo = true
			
			logger:trace( "objectWasAdded - captureId = ", captureId )
			
			LeicaTetheredTetherTask._hostCallback( "objectWasAdded", result )
		end
	elseif messageType == "dataIsAvailable" then
	
	elseif messageType == "thumbnailWasDownloaded" then
		if LeicaTetheredTetherTask._hostCallback then
			local device = LeicaTetheredTetherTask._connectedDevice
			if not device then
				logger:error( "LeicaTetheredTetherTask.tetherCallback: Could not locate device." )
				return
			end
			
			if result.thumbnail_data then
					
				local exifToLROrientation = {
					"AB", "BA", "CD", "DC", "CB", "DA", "AD", "BC",
				}
				
				local orientation = result.image_orientation and exifToLROrientation[ result.image_orientation ]
				
				LeicaTetheredTetherTask._hostCallback( "thumbnailAvailable", { captureId = result.capture_id, thumbnailData = result.thumbnail_data, orientation = orientation } )
			end
					
		end

	elseif messageType == "fileDownloadComplete" then
		if LeicaTetheredTetherTask._hostCallback then
			logger:trace( "LeicaTetheredTetherTask.tetherCallback - fileDownloadComplete: ", result.file_path )
			local device = LeicaTetheredTetherTask._connectedDevice
			
			if not device then
				logger:error( "LeicaTetheredTetherTask.tetherCallback: Could not locate device." )
				return
			end
			local captureId = result.capture_id
			local capture = device.captures[ captureId ]
			
			if not capture then
				logger:error( "LeicaTetheredTetherTask.tetherCallback: could not find capture id: ", captureId )
				return
			end
			
			LeicaTetheredTetherTask._hostCallback( "fileDownloadComplete", 
							{ captureId = captureId, 
								filePath = result.file_path, 
								fileType = LrPathUtils.extension( result.file_path ),
								status = "done" } )
		end
	elseif messageType == "deviceAdded" then
		logger:trace( "LeicaTetheredTetherTask.tetherCallback - deviceAdded: ", result.device_id )
		if LeicaTetheredTetherTask._propertyTable.availableDevices then
			local forceUpdate = false
			for i, device in ipairs( LeicaTetheredTetherTask._propertyTable.availableDevices ) do
				if device.device_id == result.device_id then
					forceUpdate = true
				end
			end
			if not forceUpdate then
				table.insert( LeicaTetheredTetherTask._propertyTable.availableDevices, result )
			end
		--	LrTableUtils.debugDumpTable( LeicaTetheredTetherTask._propertyTable.availableDevices )
			logger:trace( "- deviceAdded forceUpdate: ", forceUpdate )
			LeicaTetheredTetherTask._hostCallback( "availableDevicesChanged", forceUpdate )
			
		end
	elseif messageType == "deviceRemoved" then
		logger:trace( "LeicaTetheredTetherTask.tetherCallback - deviceRemoved: ", result.device_id )
		local deviceId = result.device_id
		if deviceId and LeicaTetheredTetherTask._propertyTable.availableDevices then
			for i, v in ipairs( LeicaTetheredTetherTask._propertyTable.availableDevices ) do
				if v.device_id == deviceId then
					table.remove( LeicaTetheredTetherTask._propertyTable.availableDevices, i )
					LeicaTetheredTetherTask._hostCallback( "availableDevicesChanged" )
					break
				end
			end
		end
	elseif messageType == "cameraSettingsChanged" then
		LeicaTetheredTetherTask._hostCallback( "cameraSettingsChanged", result )
	else
		LeicaTetheredTetherTask._hostCallback( messageType, result )
	end
end

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


function LeicaTetheredTetherTask.pollForMessages( propertyTable )
--	logger:trace( 'LeicaTetheredTetherTask.pollForMessages()' )
	pcall( function()
		local message, params = LrRemoteCommunication.pollForMessage( LeicaTetheredTetherTask._serverConnection )
		
		if message then
			logger:trace( 'LeicaTetheredTetherTask.pollForMessages() - received message: ', message )
			
			LeicaTetheredTetherTask.tetherCallback( propertyTable, message, params )
		end
	end )
end

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

function LeicaTetheredTetherTask.startDeviceSearch( propertyTable )
	
	logger:trace( "LeicaTetheredTetherTask.startDeviceSearch" )
	
	if LeicaTetheredTetherTask._connectedToPluginApp then return end
	
	if LeicaTetheredTetherTask._serverConnection then
		LeicaTetheredTetherTask._connectedToPluginApp = true
		LeicaTetheredTetherTask._sentListDevicesMessage = false

		logger:trace( 'LeicaTetheredTetherTask.startDeviceSearch - connection established.' )
								
		LeicaTetheredTetherTask.queryDevices( propertyTable )
	end
end

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

function LeicaTetheredTetherTask.stopDeviceSearch( propertyTable )
	logger:trace( 'LeicaTetheredTetherTask.stopDeviceSearch' )
end

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

function LeicaTetheredTetherTask.queryDevices( propertyTable )
	logger:trace( 'LeicaTetheredTetherTask.queryDevices' )
	
	-- If we're still not connected, wait until later
	if not LeicaTetheredTetherTask._connectedToPluginApp then return end
	logger:trace( "LeicaTetheredTetherTask.queryDevices : 1" )
	
	local function listDevicesCallback( messageType, result )
	
		logger:trace( "listDevicesCallback called with result: %s", messageType )
	
		if messageType == "ok" then
			LeicaTetheredTetherTask._propertyTable.availableDevices = result and result.devices or {}
			LeicaTetheredTetherTask._hostCallback( "availableDevicesChanged" )
		end
	end
	
	if not LeicaTetheredTetherTask._sentListDevicesMessage then
		logger:trace( 'LeicaTetheredTetherTask.queryDevices: sending message to server with callback:', listDevicesCallback )
		LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "listDevices", {}, listDevicesCallback )
		LeicaTetheredTetherTask._sentListDevicesMessage = true
	end
	
	if LeicaTetheredTetherTask._propertyTable.availableDevices then
		return { status = 'ok' }
	else
		return { status = 'notReady' }
	end
end

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

function LeicaTetheredTetherTask.connectDevice( propertyTable, deviceId, callback )
	logger:trace( 'LeicaTetheredTetherTask.connectDevice' )
	
	local function openDeviceCallback( messageType, result )
		if messageType == "ok" then
			LeicaTetheredTetherTask._sessionId = result and result._sessionId
			if callback and result and result.camera_name then
				LeicaTetheredTetherTask._connectedDevice.name = result.camera_name
				callback( result.camera_name )
			end
		end
	end
	
	for i,v in ipairs( LeicaTetheredTetherTask._propertyTable.availableDevices ) do
		if v.device_id == deviceId then
			LeicaTetheredTetherTask._connectedDevice = v
		end
	end

	LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "connectDevice", { device_id = deviceId }, openDeviceCallback )
	
	return { status = 'ok' }
end

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

function LeicaTetheredTetherTask.disconnectDevice( propertyTable, deviceId )
	logger:trace( 'LeicaTetheredTetherTask.disconnectDevice' )
	
	LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "disconnectDevice", { device_id = deviceId }, nil )
end

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

function LeicaTetheredTetherTask.doCapture( propertyTable, deviceId )
	logger:trace( 'LeicaTetheredTetherTask.doCapture' )
	
	local function doCaptureCallback( messageType, result )
		if messageType == "ok" then
		end
	end
	
	LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "doCapture", { device_id = deviceId }, doCaptureCallback )
	
	return { status = 'ok' }
end

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

function LeicaTetheredTetherTask.getCaptureInfo( propertyTable, deviceId, captureId )
	logger:trace( 'LeicaTetheredTetherTask.getCaptureInfo' )

	local device = LeicaTetheredTetherTask._connectedDevice
	local capture = device and device.captures[ captureId ]
	
	if not capture then
		logger:errorf( "LeicaTetheredTetherTask.getCaptureInfo: capture table for captureId %s could not be found.", captureId )
		return {
			status = "error",
			errorMsg = "Could not get capture info for capture id: " .. tostring(captureId),
		}
	end
	
	if capture.captureInfo then
		-- We've already gotten the data
		return {
			status = "ok",
			fileType = LrPathUtils.extension( capture.captureInfo.ifil ),
			fileLength = capture.captureInfo.isiz,
		}
	end
	
	if not capture.requestedInfo then
	end
	
	return {
		status = "notReady",
	}

end

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

function LeicaTetheredTetherTask.startDownload( propertyTable, deviceId, captureId )
	logger:trace( 'LeicaTetheredTetherTask.startDownload' )
	
	local device = LeicaTetheredTetherTask._connectedDevice
	local capture = device and device.captures[ captureId ]
	if capture then
		LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "startDownload", { capture_id = captureId }, 
					function( messageType, result ) if messageType == "error" then capture.errorCode = result end end )
	end
end

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

function LeicaTetheredTetherTask.getCameraSettings( propertyTable, deviceId, callbackFn )
	logger:trace( 'LeicaTetheredTetherTask.getCameraSettings' )
	
	LrRemoteCommunication.sendMessageToServer( LeicaTetheredTetherTask._serverConnection, "getCameraSettings", { device_id = deviceId }, 
			function( messageType, result ) 
				logger:trace( 'LeicaTetheredTetherTask.getCameraSettings: received result - ', messageType, result )
				
				if messageType == "error" then 
					callbackFn( nil, result.error_code )
				else
					callbackFn( result )
				end
			end )
end

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

return LeicaTetheredTetherTask







