/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2020, Google Inc.
 *
 * raspberrypi_ipa_proxy.cpp - Image Processing Algorithm proxy for raspberrypi
 *
 * This file is auto-generated. Do not edit.
 */

#include <libcamera/ipa/raspberrypi_ipa_proxy.h>

#include <memory>
#include <string>
#include <vector>

#include <libcamera/ipa/ipa_module_info.h>
#include <libcamera/ipa/raspberrypi_ipa_interface.h>
#include <libcamera/ipa/raspberrypi_ipa_serializer.h>

#include <libcamera/base/log.h>
#include <libcamera/base/thread.h>

#include "libcamera/internal/control_serializer.h"
#include "libcamera/internal/ipa_data_serializer.h"
#include "libcamera/internal/ipa_module.h"
#include "libcamera/internal/ipa_proxy.h"
#include "libcamera/internal/ipc_pipe.h"
#include "libcamera/internal/ipc_pipe_unixsocket.h"
#include "libcamera/internal/ipc_unixsocket.h"
#include "libcamera/internal/process.h"

namespace libcamera {

LOG_DECLARE_CATEGORY(IPAProxy)

namespace ipa {

namespace RPi {


IPAProxyRPi::IPAProxyRPi(IPAModule *ipam, bool isolate)
	: IPAProxy(ipam), isolate_(isolate),
	  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)
{
	LOG(IPAProxy, Debug)
		<< "initializing raspberrypi proxy: loading IPA from "
		<< ipam->path();

	if (isolate_) {
		const std::string proxyWorkerPath = resolvePath("raspberrypi_ipa_proxy");
		if (proxyWorkerPath.empty()) {
			LOG(IPAProxy, Error)
				<< "Failed to get proxy worker path";
			return;
		}

		ipc_ = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),
							   proxyWorkerPath.c_str());
		if (!ipc_->isConnected()) {
			LOG(IPAProxy, Error) << "Failed to create IPCPipe";
			return;
		}

		ipc_->recv.connect(this, &IPAProxyRPi::recvMessage);

		valid_ = true;
		return;
	}

	if (!ipam->load())
		return;

	IPAInterface *ipai = ipam->createInterface();
	if (!ipai) {
		LOG(IPAProxy, Error)
			<< "Failed to create IPA context for " << ipam->path();
		return;
	}

	ipa_ = std::unique_ptr<IPARPiInterface>(static_cast<IPARPiInterface *>(ipai));
	proxy_.setIPA(ipa_.get());


	ipa_->statsMetadataComplete.connect(this, &IPAProxyRPi::statsMetadataCompleteThread);
	ipa_->runIsp.connect(this, &IPAProxyRPi::runIspThread);
	ipa_->embeddedComplete.connect(this, &IPAProxyRPi::embeddedCompleteThread);
	ipa_->setIspControls.connect(this, &IPAProxyRPi::setIspControlsThread);
	ipa_->setDelayedControls.connect(this, &IPAProxyRPi::setDelayedControlsThread);

	valid_ = true;
}

IPAProxyRPi::~IPAProxyRPi()
{
	if (isolate_) {
		IPCMessage::Header header =
			{ static_cast<uint32_t>(_RPiCmd::Exit), seq_++ };
		IPCMessage msg(header);
		ipc_->sendAsync(msg);
	}
}


void IPAProxyRPi::recvMessage(const IPCMessage &data)
{
	size_t dataSize = data.data().size();
	_RPiEventCmd _cmd = static_cast<_RPiEventCmd>(data.header().cmd);

	switch (_cmd) {
	case _RPiEventCmd::StatsMetadataComplete: {
		statsMetadataCompleteIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _RPiEventCmd::RunIsp: {
		runIspIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _RPiEventCmd::EmbeddedComplete: {
		embeddedCompleteIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _RPiEventCmd::SetIspControls: {
		setIspControlsIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _RPiEventCmd::SetDelayedControls: {
		setDelayedControlsIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	default:
		LOG(IPAProxy, Error) << "Unknown command " << static_cast<uint32_t>(_cmd);
	}
}


int32_t IPAProxyRPi::init(
	const IPASettings &settings,
	IPAInitResult *result)
{
	if (isolate_)
		return initIPC(settings, result);
	else
		return initThread(settings, result);
}

int32_t IPAProxyRPi::initThread(
	const IPASettings &settings,
	IPAInitResult *result)
{
	int32_t _ret = ipa_->init(settings, result);

	proxy_.moveToThread(&thread_);

	return _ret;
}

int32_t IPAProxyRPi::initIPC(
	const IPASettings &settings,
	IPAInitResult *result)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::Init), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;


	std::vector<uint8_t> settingsBuf;
	std::tie(settingsBuf, std::ignore) =
		IPADataSerializer<IPASettings>::serialize(settings);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), settingsBuf.begin(), settingsBuf.end());


	int _ret = ipc_->sendSync(_ipcInputBuf, &_ipcOutputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call init";
		return static_cast<int32_t>(_ret);
	}

	int32_t _retValue = IPADataSerializer<int32_t>::deserialize(_ipcOutputBuf.data(), 0);



	const size_t resultStart = 4;


	if (result) {
                *result = IPADataSerializer<IPAInitResult>::deserialize(
                	_ipcOutputBuf.data().cbegin() + resultStart,
                	_ipcOutputBuf.data().cend(),
                	&controlSerializer_);
	}


	return _retValue;

}


void IPAProxyRPi::start(
	const ControlList &controls,
	StartConfig *startConfig)
{
	if (isolate_)
		startIPC(controls, startConfig);
	else
		startThread(controls, startConfig);
}

void IPAProxyRPi::startThread(
	const ControlList &controls,
	StartConfig *startConfig)
{
	state_ = ProxyRunning;
	thread_.start();

	proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking, controls, startConfig);
}

void IPAProxyRPi::startIPC(
	const ControlList &controls,
	StartConfig *startConfig)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::Start), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;


	std::vector<uint8_t> controlsBuf;
	std::tie(controlsBuf, std::ignore) =
		IPADataSerializer<ControlList>::serialize(controls, &controlSerializer_);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), controlsBuf.begin(), controlsBuf.end());


	int _ret = ipc_->sendSync(_ipcInputBuf, &_ipcOutputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call start";
		return;
	}



	const size_t startConfigStart = 0;


	if (startConfig) {
                *startConfig = IPADataSerializer<StartConfig>::deserialize(
                	_ipcOutputBuf.data().cbegin() + startConfigStart,
                	_ipcOutputBuf.data().cend(),
                	&controlSerializer_);
	}

}


void IPAProxyRPi::stop()
{
	if (isolate_)
		stopIPC();
	else
		stopThread();
}

void IPAProxyRPi::stopThread()
{
	ASSERT(state_ != ProxyStopping);
	if (state_ != ProxyRunning)
		return;

	state_ = ProxyStopping;

	proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking);

	thread_.exit();
	thread_.wait();

	Thread::current()->dispatchMessages(Message::Type::InvokeMessage);

	state_ = ProxyStopped;
}

void IPAProxyRPi::stopIPC()
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::Stop), seq_++ };
	IPCMessage _ipcInputBuf(_header);




	int _ret = ipc_->sendSync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call stop";
		return;
	}
}


int32_t IPAProxyRPi::configure(
	const IPACameraSensorInfo &sensorInfo,
	const std::map<uint32_t, libcamera::IPAStream> &streamConfig,
	const std::map<uint32_t, libcamera::ControlInfoMap> &entityControls,
	const IPAConfig &ipaConfig,
	ControlList *controls,
	IPAConfigResult *result)
{
	if (isolate_)
		return configureIPC(sensorInfo, streamConfig, entityControls, ipaConfig, controls, result);
	else
		return configureThread(sensorInfo, streamConfig, entityControls, ipaConfig, controls, result);
}

int32_t IPAProxyRPi::configureThread(
	const IPACameraSensorInfo &sensorInfo,
	const std::map<uint32_t, libcamera::IPAStream> &streamConfig,
	const std::map<uint32_t, libcamera::ControlInfoMap> &entityControls,
	const IPAConfig &ipaConfig,
	ControlList *controls,
	IPAConfigResult *result)
{
	return ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, controls, result);

}

int32_t IPAProxyRPi::configureIPC(
	const IPACameraSensorInfo &sensorInfo,
	const std::map<uint32_t, libcamera::IPAStream> &streamConfig,
	const std::map<uint32_t, libcamera::ControlInfoMap> &entityControls,
	const IPAConfig &ipaConfig,
	ControlList *controls,
	IPAConfigResult *result)
{
	controlSerializer_.reset();
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::Configure), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;


	std::vector<uint8_t> sensorInfoBuf;
	std::tie(sensorInfoBuf, std::ignore) =
		IPADataSerializer<IPACameraSensorInfo>::serialize(sensorInfo);
	std::vector<uint8_t> streamConfigBuf;
	std::tie(streamConfigBuf, std::ignore) =
		IPADataSerializer<std::map<uint32_t, libcamera::IPAStream>>::serialize(streamConfig);
	std::vector<uint8_t> entityControlsBuf;
	std::tie(entityControlsBuf, std::ignore) =
		IPADataSerializer<std::map<uint32_t, libcamera::ControlInfoMap>>::serialize(entityControls, &controlSerializer_);
	std::vector<uint8_t> ipaConfigBuf;
	std::vector<SharedFD> ipaConfigFds;
	std::tie(ipaConfigBuf, ipaConfigFds) =
		IPADataSerializer<IPAConfig>::serialize(ipaConfig);
	appendPOD<uint32_t>(_ipcInputBuf.data(), sensorInfoBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), streamConfigBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), entityControlsBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), ipaConfigBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), ipaConfigFds.size());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), sensorInfoBuf.begin(), sensorInfoBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), streamConfigBuf.begin(), streamConfigBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), entityControlsBuf.begin(), entityControlsBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), ipaConfigBuf.begin(), ipaConfigBuf.end());
	_ipcInputBuf.fds().insert(_ipcInputBuf.fds().end(), ipaConfigFds.begin(), ipaConfigFds.end());


	int _ret = ipc_->sendSync(_ipcInputBuf, &_ipcOutputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call configure";
		return static_cast<int32_t>(_ret);
	}

	int32_t _retValue = IPADataSerializer<int32_t>::deserialize(_ipcOutputBuf.data(), 0);


	[[maybe_unused]] const size_t controlsBufSize = readPOD<uint32_t>(_ipcOutputBuf.data(), 4);
	[[maybe_unused]] const size_t resultBufSize = readPOD<uint32_t>(_ipcOutputBuf.data(), 8);

	const size_t controlsStart = 12;
	const size_t resultStart = controlsStart + controlsBufSize;


	if (controls) {
                *controls = IPADataSerializer<ControlList>::deserialize(
                	_ipcOutputBuf.data().cbegin() + controlsStart,
                	_ipcOutputBuf.data().cbegin() + controlsStart + controlsBufSize,
                	&controlSerializer_);
	}

	if (result) {
                *result = IPADataSerializer<IPAConfigResult>::deserialize(
                	_ipcOutputBuf.data().cbegin() + resultStart,
                	_ipcOutputBuf.data().cend(),
                	&controlSerializer_);
	}


	return _retValue;

}


void IPAProxyRPi::mapBuffers(
	const std::vector<libcamera::IPABuffer> &buffers)
{
	if (isolate_)
		mapBuffersIPC(buffers);
	else
		mapBuffersThread(buffers);
}

void IPAProxyRPi::mapBuffersThread(
	const std::vector<libcamera::IPABuffer> &buffers)
{
	ipa_->mapBuffers(buffers);

}

void IPAProxyRPi::mapBuffersIPC(
	const std::vector<libcamera::IPABuffer> &buffers)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::MapBuffers), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> buffersBuf;
	std::vector<SharedFD> buffersFds;
	std::tie(buffersBuf, buffersFds) =
		IPADataSerializer<std::vector<libcamera::IPABuffer>>::serialize(buffers);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), buffersBuf.begin(), buffersBuf.end());
	_ipcInputBuf.fds().insert(_ipcInputBuf.fds().end(), buffersFds.begin(), buffersFds.end());


	int _ret = ipc_->sendSync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call mapBuffers";
		return;
	}
}


void IPAProxyRPi::unmapBuffers(
	const std::vector<uint32_t> &ids)
{
	if (isolate_)
		unmapBuffersIPC(ids);
	else
		unmapBuffersThread(ids);
}

void IPAProxyRPi::unmapBuffersThread(
	const std::vector<uint32_t> &ids)
{
	ipa_->unmapBuffers(ids);

}

void IPAProxyRPi::unmapBuffersIPC(
	const std::vector<uint32_t> &ids)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::UnmapBuffers), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> idsBuf;
	std::tie(idsBuf, std::ignore) =
		IPADataSerializer<std::vector<uint32_t>>::serialize(ids);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), idsBuf.begin(), idsBuf.end());


	int _ret = ipc_->sendSync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call unmapBuffers";
		return;
	}
}


void IPAProxyRPi::signalStatReady(
	const uint32_t bufferId)
{
	if (isolate_)
		signalStatReadyIPC(bufferId);
	else
		signalStatReadyThread(bufferId);
}

void IPAProxyRPi::signalStatReadyThread(
	const uint32_t bufferId)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::signalStatReady, ConnectionTypeQueued,bufferId);
}

void IPAProxyRPi::signalStatReadyIPC(
	const uint32_t bufferId)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::SignalStatReady), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> bufferIdBuf;
	std::tie(bufferIdBuf, std::ignore) =
		IPADataSerializer<uint32_t>::serialize(bufferId);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), bufferIdBuf.begin(), bufferIdBuf.end());


	int _ret = ipc_->sendAsync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call signalStatReady";
		return;
	}
}


void IPAProxyRPi::signalQueueRequest(
	const ControlList &controls)
{
	if (isolate_)
		signalQueueRequestIPC(controls);
	else
		signalQueueRequestThread(controls);
}

void IPAProxyRPi::signalQueueRequestThread(
	const ControlList &controls)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::signalQueueRequest, ConnectionTypeQueued,controls);
}

void IPAProxyRPi::signalQueueRequestIPC(
	const ControlList &controls)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::SignalQueueRequest), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> controlsBuf;
	std::tie(controlsBuf, std::ignore) =
		IPADataSerializer<ControlList>::serialize(controls, &controlSerializer_);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), controlsBuf.begin(), controlsBuf.end());


	int _ret = ipc_->sendAsync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call signalQueueRequest";
		return;
	}
}


void IPAProxyRPi::signalIspPrepare(
	const ISPConfig &data)
{
	if (isolate_)
		signalIspPrepareIPC(data);
	else
		signalIspPrepareThread(data);
}

void IPAProxyRPi::signalIspPrepareThread(
	const ISPConfig &data)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::signalIspPrepare, ConnectionTypeQueued,data);
}

void IPAProxyRPi::signalIspPrepareIPC(
	const ISPConfig &data)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_RPiCmd::SignalIspPrepare), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> dataBuf;
	std::tie(dataBuf, std::ignore) =
		IPADataSerializer<ISPConfig>::serialize(data, &controlSerializer_);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), dataBuf.begin(), dataBuf.end());


	int _ret = ipc_->sendAsync(_ipcInputBuf);
	if (_ret < 0) {
		LOG(IPAProxy, Error) << "Failed to call signalIspPrepare";
		return;
	}
}




void IPAProxyRPi::statsMetadataCompleteThread(
	const uint32_t bufferId,
	const ControlList &controls)
{
	ASSERT(state_ != ProxyStopped);
	statsMetadataComplete.emit(bufferId, controls);
}

void IPAProxyRPi::statsMetadataCompleteIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	uint32_t bufferId;
	ControlList controls;

	[[maybe_unused]] const size_t bufferIdBufSize = readPOD<uint32_t>(data, 0, data + dataSize);
	[[maybe_unused]] const size_t controlsBufSize = readPOD<uint32_t>(data, 4, data + dataSize);

	const size_t bufferIdStart = 8;
	const size_t controlsStart = bufferIdStart + bufferIdBufSize;


	bufferId = IPADataSerializer<uint32_t>::deserialize(
        	data + bufferIdStart,
        	data + bufferIdStart + bufferIdBufSize);

	controls = IPADataSerializer<ControlList>::deserialize(
        	data + controlsStart,
        	data + controlsStart + controlsBufSize,
        	&controlSerializer_);

	statsMetadataComplete.emit(bufferId, controls);
}

void IPAProxyRPi::runIspThread(
	const uint32_t bufferId)
{
	ASSERT(state_ != ProxyStopped);
	runIsp.emit(bufferId);
}

void IPAProxyRPi::runIspIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	uint32_t bufferId;


	const size_t bufferIdStart = 0;


	bufferId = IPADataSerializer<uint32_t>::deserialize(
        	data + bufferIdStart,
        	data + dataSize);

	runIsp.emit(bufferId);
}

void IPAProxyRPi::embeddedCompleteThread(
	const uint32_t bufferId)
{
	ASSERT(state_ != ProxyStopped);
	embeddedComplete.emit(bufferId);
}

void IPAProxyRPi::embeddedCompleteIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	uint32_t bufferId;


	const size_t bufferIdStart = 0;


	bufferId = IPADataSerializer<uint32_t>::deserialize(
        	data + bufferIdStart,
        	data + dataSize);

	embeddedComplete.emit(bufferId);
}

void IPAProxyRPi::setIspControlsThread(
	const ControlList &controls)
{
	ASSERT(state_ != ProxyStopped);
	setIspControls.emit(controls);
}

void IPAProxyRPi::setIspControlsIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	ControlList controls;


	const size_t controlsStart = 0;


	controls = IPADataSerializer<ControlList>::deserialize(
        	data + controlsStart,
        	data + dataSize,
        	&controlSerializer_);

	setIspControls.emit(controls);
}

void IPAProxyRPi::setDelayedControlsThread(
	const ControlList &controls)
{
	ASSERT(state_ != ProxyStopped);
	setDelayedControls.emit(controls);
}

void IPAProxyRPi::setDelayedControlsIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	ControlList controls;


	const size_t controlsStart = 0;


	controls = IPADataSerializer<ControlList>::deserialize(
        	data + controlsStart,
        	data + dataSize,
        	&controlSerializer_);

	setDelayedControls.emit(controls);
}


} /* namespace RPi */

} /* namespace ipa */

} /* namespace libcamera */