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

#include <libcamera/ipa/ipu3_ipa_proxy.h>

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

#include <libcamera/ipa/ipa_module_info.h>
#include <libcamera/ipa/ipu3_ipa_interface.h>
#include <libcamera/ipa/ipu3_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 ipu3 {


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

	if (isolate_) {
		const std::string proxyWorkerPath = resolvePath("ipu3_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, &IPAProxyIPU3::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<IPAIPU3Interface>(static_cast<IPAIPU3Interface *>(ipai));
	proxy_.setIPA(ipa_.get());


	ipa_->setSensorControls.connect(this, &IPAProxyIPU3::setSensorControlsThread);
	ipa_->paramsBufferReady.connect(this, &IPAProxyIPU3::paramsBufferReadyThread);
	ipa_->metadataReady.connect(this, &IPAProxyIPU3::metadataReadyThread);

	valid_ = true;
}

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


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

	switch (_cmd) {
	case _IPU3EventCmd::SetSensorControls: {
		setSensorControlsIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _IPU3EventCmd::ParamsBufferReady: {
		paramsBufferReadyIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	case _IPU3EventCmd::MetadataReady: {
		metadataReadyIPC(data.data().cbegin(), dataSize, data.fds());
		break;
	}
	default:
		LOG(IPAProxy, Error) << "Unknown command " << static_cast<uint32_t>(_cmd);
	}
}


int32_t IPAProxyIPU3::init(
	const IPASettings &settings,
	const IPACameraSensorInfo &sensorInfo,
	const ControlInfoMap &sensorControls,
	ControlInfoMap *ipaControls)
{
	if (isolate_)
		return initIPC(settings, sensorInfo, sensorControls, ipaControls);
	else
		return initThread(settings, sensorInfo, sensorControls, ipaControls);
}

int32_t IPAProxyIPU3::initThread(
	const IPASettings &settings,
	const IPACameraSensorInfo &sensorInfo,
	const ControlInfoMap &sensorControls,
	ControlInfoMap *ipaControls)
{
	int32_t _ret = ipa_->init(settings, sensorInfo, sensorControls, ipaControls);

	proxy_.moveToThread(&thread_);

	return _ret;
}

int32_t IPAProxyIPU3::initIPC(
	const IPASettings &settings,
	const IPACameraSensorInfo &sensorInfo,
	const ControlInfoMap &sensorControls,
	ControlInfoMap *ipaControls)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::Init), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;


	std::vector<uint8_t> settingsBuf;
	std::tie(settingsBuf, std::ignore) =
		IPADataSerializer<IPASettings>::serialize(settings);
	std::vector<uint8_t> sensorInfoBuf;
	std::tie(sensorInfoBuf, std::ignore) =
		IPADataSerializer<IPACameraSensorInfo>::serialize(sensorInfo);
	std::vector<uint8_t> sensorControlsBuf;
	std::tie(sensorControlsBuf, std::ignore) =
		IPADataSerializer<ControlInfoMap>::serialize(sensorControls, &controlSerializer_);
	appendPOD<uint32_t>(_ipcInputBuf.data(), settingsBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), sensorInfoBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), sensorControlsBuf.size());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), settingsBuf.begin(), settingsBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), sensorInfoBuf.begin(), sensorInfoBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), sensorControlsBuf.begin(), sensorControlsBuf.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 ipaControlsStart = 4;


	if (ipaControls) {
                *ipaControls = IPADataSerializer<ControlInfoMap>::deserialize(
                	_ipcOutputBuf.data().cbegin() + ipaControlsStart,
                	_ipcOutputBuf.data().cend(),
                	&controlSerializer_);
	}


	return _retValue;

}


int32_t IPAProxyIPU3::start()
{
	if (isolate_)
		return startIPC();
	else
		return startThread();
}

int32_t IPAProxyIPU3::startThread()
{
	state_ = ProxyRunning;
	thread_.start();

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

int32_t IPAProxyIPU3::startIPC()
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::Start), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;




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

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






	return _retValue;

}


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

void IPAProxyIPU3::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 IPAProxyIPU3::stopIPC()
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::Stop), seq_++ };
	IPCMessage _ipcInputBuf(_header);




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


int32_t IPAProxyIPU3::configure(
	const IPAConfigInfo &configInfo,
	ControlInfoMap *ipaControls)
{
	if (isolate_)
		return configureIPC(configInfo, ipaControls);
	else
		return configureThread(configInfo, ipaControls);
}

int32_t IPAProxyIPU3::configureThread(
	const IPAConfigInfo &configInfo,
	ControlInfoMap *ipaControls)
{
	return ipa_->configure(configInfo, ipaControls);

}

int32_t IPAProxyIPU3::configureIPC(
	const IPAConfigInfo &configInfo,
	ControlInfoMap *ipaControls)
{
	controlSerializer_.reset();
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::Configure), seq_++ };
	IPCMessage _ipcInputBuf(_header);
	IPCMessage _ipcOutputBuf;


	std::vector<uint8_t> configInfoBuf;
	std::tie(configInfoBuf, std::ignore) =
		IPADataSerializer<IPAConfigInfo>::serialize(configInfo, &controlSerializer_);
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), configInfoBuf.begin(), configInfoBuf.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);



	const size_t ipaControlsStart = 4;


	if (ipaControls) {
                *ipaControls = IPADataSerializer<ControlInfoMap>::deserialize(
                	_ipcOutputBuf.data().cbegin() + ipaControlsStart,
                	_ipcOutputBuf.data().cend(),
                	&controlSerializer_);
	}


	return _retValue;

}


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

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

}

void IPAProxyIPU3::mapBuffersIPC(
	const std::vector<libcamera::IPABuffer> &buffers)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::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 IPAProxyIPU3::unmapBuffers(
	const std::vector<uint32_t> &ids)
{
	if (isolate_)
		unmapBuffersIPC(ids);
	else
		unmapBuffersThread(ids);
}

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

}

void IPAProxyIPU3::unmapBuffersIPC(
	const std::vector<uint32_t> &ids)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::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 IPAProxyIPU3::queueRequest(
	const uint32_t frame,
	const ControlList &controls)
{
	if (isolate_)
		queueRequestIPC(frame, controls);
	else
		queueRequestThread(frame, controls);
}

void IPAProxyIPU3::queueRequestThread(
	const uint32_t frame,
	const ControlList &controls)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::queueRequest, ConnectionTypeQueued,frame, controls);
}

void IPAProxyIPU3::queueRequestIPC(
	const uint32_t frame,
	const ControlList &controls)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::QueueRequest), seq_++ };
	IPCMessage _ipcInputBuf(_header);


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


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


void IPAProxyIPU3::fillParamsBuffer(
	const uint32_t frame,
	const uint32_t bufferId)
{
	if (isolate_)
		fillParamsBufferIPC(frame, bufferId);
	else
		fillParamsBufferThread(frame, bufferId);
}

void IPAProxyIPU3::fillParamsBufferThread(
	const uint32_t frame,
	const uint32_t bufferId)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::fillParamsBuffer, ConnectionTypeQueued,frame, bufferId);
}

void IPAProxyIPU3::fillParamsBufferIPC(
	const uint32_t frame,
	const uint32_t bufferId)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::FillParamsBuffer), seq_++ };
	IPCMessage _ipcInputBuf(_header);


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


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


void IPAProxyIPU3::processStatsBuffer(
	const uint32_t frame,
	const int64_t frameTimestamp,
	const uint32_t bufferId,
	const ControlList &sensorControls)
{
	if (isolate_)
		processStatsBufferIPC(frame, frameTimestamp, bufferId, sensorControls);
	else
		processStatsBufferThread(frame, frameTimestamp, bufferId, sensorControls);
}

void IPAProxyIPU3::processStatsBufferThread(
	const uint32_t frame,
	const int64_t frameTimestamp,
	const uint32_t bufferId,
	const ControlList &sensorControls)
{
	ASSERT(state_ == ProxyRunning);
	proxy_.invokeMethod(&ThreadProxy::processStatsBuffer, ConnectionTypeQueued,frame, frameTimestamp, bufferId, sensorControls);
}

void IPAProxyIPU3::processStatsBufferIPC(
	const uint32_t frame,
	const int64_t frameTimestamp,
	const uint32_t bufferId,
	const ControlList &sensorControls)
{
	IPCMessage::Header _header = { static_cast<uint32_t>(_IPU3Cmd::ProcessStatsBuffer), seq_++ };
	IPCMessage _ipcInputBuf(_header);


	std::vector<uint8_t> frameBuf;
	std::tie(frameBuf, std::ignore) =
		IPADataSerializer<uint32_t>::serialize(frame);
	std::vector<uint8_t> frameTimestampBuf;
	std::tie(frameTimestampBuf, std::ignore) =
		IPADataSerializer<int64_t>::serialize(frameTimestamp);
	std::vector<uint8_t> bufferIdBuf;
	std::tie(bufferIdBuf, std::ignore) =
		IPADataSerializer<uint32_t>::serialize(bufferId);
	std::vector<uint8_t> sensorControlsBuf;
	std::tie(sensorControlsBuf, std::ignore) =
		IPADataSerializer<ControlList>::serialize(sensorControls, &controlSerializer_);
	appendPOD<uint32_t>(_ipcInputBuf.data(), frameBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), frameTimestampBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), bufferIdBuf.size());
	appendPOD<uint32_t>(_ipcInputBuf.data(), sensorControlsBuf.size());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), frameBuf.begin(), frameBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), frameTimestampBuf.begin(), frameTimestampBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), bufferIdBuf.begin(), bufferIdBuf.end());
	_ipcInputBuf.data().insert(_ipcInputBuf.data().end(), sensorControlsBuf.begin(), sensorControlsBuf.end());


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




void IPAProxyIPU3::setSensorControlsThread(
	const uint32_t frame,
	const ControlList &sensorControls,
	const ControlList &lensControls)
{
	ASSERT(state_ != ProxyStopped);
	setSensorControls.emit(frame, sensorControls, lensControls);
}

void IPAProxyIPU3::setSensorControlsIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	uint32_t frame;
	ControlList sensorControls;
	ControlList lensControls;

	[[maybe_unused]] const size_t frameBufSize = readPOD<uint32_t>(data, 0, data + dataSize);
	[[maybe_unused]] const size_t sensorControlsBufSize = readPOD<uint32_t>(data, 4, data + dataSize);
	[[maybe_unused]] const size_t lensControlsBufSize = readPOD<uint32_t>(data, 8, data + dataSize);

	const size_t frameStart = 12;
	const size_t sensorControlsStart = frameStart + frameBufSize;
	const size_t lensControlsStart = sensorControlsStart + sensorControlsBufSize;


	frame = IPADataSerializer<uint32_t>::deserialize(
        	data + frameStart,
        	data + frameStart + frameBufSize);

	sensorControls = IPADataSerializer<ControlList>::deserialize(
        	data + sensorControlsStart,
        	data + sensorControlsStart + sensorControlsBufSize,
        	&controlSerializer_);

	lensControls = IPADataSerializer<ControlList>::deserialize(
        	data + lensControlsStart,
        	data + lensControlsStart + lensControlsBufSize,
        	&controlSerializer_);

	setSensorControls.emit(frame, sensorControls, lensControls);
}

void IPAProxyIPU3::paramsBufferReadyThread(
	const uint32_t frame)
{
	ASSERT(state_ != ProxyStopped);
	paramsBufferReady.emit(frame);
}

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


	const size_t frameStart = 0;


	frame = IPADataSerializer<uint32_t>::deserialize(
        	data + frameStart,
        	data + dataSize);

	paramsBufferReady.emit(frame);
}

void IPAProxyIPU3::metadataReadyThread(
	const uint32_t frame,
	const ControlList &metadata)
{
	ASSERT(state_ != ProxyStopped);
	metadataReady.emit(frame, metadata);
}

void IPAProxyIPU3::metadataReadyIPC(
	std::vector<uint8_t>::const_iterator data,
	size_t dataSize,
	[[maybe_unused]] const std::vector<SharedFD> &fds)
{
	uint32_t frame;
	ControlList metadata;

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

	const size_t frameStart = 8;
	const size_t metadataStart = frameStart + frameBufSize;


	frame = IPADataSerializer<uint32_t>::deserialize(
        	data + frameStart,
        	data + frameStart + frameBufSize);

	metadata = IPADataSerializer<ControlList>::deserialize(
        	data + metadataStart,
        	data + metadataStart + metadataBufSize,
        	&controlSerializer_);

	metadataReady.emit(frame, metadata);
}


} /* namespace ipu3 */

} /* namespace ipa */

} /* namespace libcamera */