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

#include <algorithm>
#include <iostream>
#include <sys/types.h>
#include <tuple>
#include <unistd.h>

#include <libcamera/ipa/ipa_interface.h>
#include <libcamera/ipa/ipu3_ipa_interface.h>
#include <libcamera/ipa/ipu3_ipa_serializer.h>
#include <libcamera/logging.h>

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

#include "libcamera/internal/camera_sensor.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"

using namespace libcamera;

LOG_DEFINE_CATEGORY(IPAProxyIPU3Worker)
using namespace ipa;
using namespace ipu3;


class IPAProxyIPU3Worker
{
public:
	IPAProxyIPU3Worker()
		: ipa_(nullptr),
		  controlSerializer_(ControlSerializer::Role::Worker),
		  exit_(false) {}

	~IPAProxyIPU3Worker() {}

	void readyRead()
	{
		IPCUnixSocket::Payload _message;
		int _retRecv = socket_.receive(&_message);
		if (_retRecv) {
			LOG(IPAProxyIPU3Worker, Error)
				<< "Receive message failed: " << _retRecv;
			return;
		}

		IPCMessage _ipcMessage(_message);

		_IPU3Cmd _cmd = static_cast<_IPU3Cmd>(_ipcMessage.header().cmd);

		switch (_cmd) {
		case _IPU3Cmd::Exit: {
			exit_ = true;
			break;
		}


		case _IPU3Cmd::Init: {
		                
                	[[maybe_unused]] const size_t settingsBufSize = readPOD<uint32_t>(_ipcMessage.data(), 0);
                	[[maybe_unused]] const size_t sensorInfoBufSize = readPOD<uint32_t>(_ipcMessage.data(), 4);
                	[[maybe_unused]] const size_t sensorControlsBufSize = readPOD<uint32_t>(_ipcMessage.data(), 8);

                	const size_t settingsStart = 12;
                	const size_t sensorInfoStart = settingsStart + settingsBufSize;
                	const size_t sensorControlsStart = sensorInfoStart + sensorInfoBufSize;


                	IPASettings settings = IPADataSerializer<IPASettings>::deserialize(
                        	_ipcMessage.data().cbegin() + settingsStart,
                        	_ipcMessage.data().cbegin() + settingsStart + settingsBufSize);

                	IPACameraSensorInfo sensorInfo = IPADataSerializer<IPACameraSensorInfo>::deserialize(
                        	_ipcMessage.data().cbegin() + sensorInfoStart,
                        	_ipcMessage.data().cbegin() + sensorInfoStart + sensorInfoBufSize);

                	ControlInfoMap sensorControls = IPADataSerializer<ControlInfoMap>::deserialize(
                        	_ipcMessage.data().cbegin() + sensorControlsStart,
                        	_ipcMessage.data().cend(),
                        	&controlSerializer_);


			ControlInfoMap ipaControls;

			int32_t _callRet =ipa_->init(settings, sensorInfo, sensorControls, &ipaControls);

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
			std::vector<uint8_t> _callRetBuf;
			std::tie(_callRetBuf, std::ignore) =
				IPADataSerializer<int32_t>::serialize(_callRet);
			_response.data().insert(_response.data().end(), _callRetBuf.cbegin(), _callRetBuf.cend());
		                
                	std::vector<uint8_t> ipaControlsBuf;
                	std::tie(ipaControlsBuf, std::ignore) =
                		IPADataSerializer<ControlInfoMap>::serialize(ipaControls, &controlSerializer_);
                	_response.data().insert(_response.data().end(), ipaControlsBuf.begin(), ipaControlsBuf.end());
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to init() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to init()";
			break;
		}

		case _IPU3Cmd::Start: {
		                




			int32_t _callRet =ipa_->start();

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
			std::vector<uint8_t> _callRetBuf;
			std::tie(_callRetBuf, std::ignore) =
				IPADataSerializer<int32_t>::serialize(_callRet);
			_response.data().insert(_response.data().end(), _callRetBuf.cbegin(), _callRetBuf.cend());
		                
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to start() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to start()";
			break;
		}

		case _IPU3Cmd::Stop: {
		                



ipa_->stop();

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
		                
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to stop() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to stop()";
			break;
		}

		case _IPU3Cmd::Configure: {
			controlSerializer_.reset();
		                

                	const size_t configInfoStart = 0;


                	IPAConfigInfo configInfo = IPADataSerializer<IPAConfigInfo>::deserialize(
                        	_ipcMessage.data().cbegin() + configInfoStart,
                        	_ipcMessage.data().cend(),
                        	&controlSerializer_);


			ControlInfoMap ipaControls;

			int32_t _callRet =ipa_->configure(configInfo, &ipaControls);

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
			std::vector<uint8_t> _callRetBuf;
			std::tie(_callRetBuf, std::ignore) =
				IPADataSerializer<int32_t>::serialize(_callRet);
			_response.data().insert(_response.data().end(), _callRetBuf.cbegin(), _callRetBuf.cend());
		                
                	std::vector<uint8_t> ipaControlsBuf;
                	std::tie(ipaControlsBuf, std::ignore) =
                		IPADataSerializer<ControlInfoMap>::serialize(ipaControls, &controlSerializer_);
                	_response.data().insert(_response.data().end(), ipaControlsBuf.begin(), ipaControlsBuf.end());
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to configure() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to configure()";
			break;
		}

		case _IPU3Cmd::MapBuffers: {
		                

                	const size_t buffersStart = 0;

                	const size_t buffersFdStart = 0;

                	std::vector<libcamera::IPABuffer> buffers = IPADataSerializer<std::vector<libcamera::IPABuffer>>::deserialize(
                        	_ipcMessage.data().cbegin() + buffersStart,
                        	_ipcMessage.data().cend(),
                        	_ipcMessage.fds().cbegin() + buffersFdStart,
                        	_ipcMessage.fds().cend());

ipa_->mapBuffers(buffers);

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
		                
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to mapBuffers() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to mapBuffers()";
			break;
		}

		case _IPU3Cmd::UnmapBuffers: {
		                

                	const size_t idsStart = 0;


                	std::vector<uint32_t> ids = IPADataSerializer<std::vector<uint32_t>>::deserialize(
                        	_ipcMessage.data().cbegin() + idsStart,
                        	_ipcMessage.data().cend());

ipa_->unmapBuffers(ids);

			IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
			IPCMessage _response(header);
		                
			int _ret = socket_.send(_response.payload());
			if (_ret < 0) {
				LOG(IPAProxyIPU3Worker, Error)
					<< "Reply to unmapBuffers() failed: " << _ret;
			}
			LOG(IPAProxyIPU3Worker, Debug) << "Done replying to unmapBuffers()";
			break;
		}

		case _IPU3Cmd::QueueRequest: {
		                
                	[[maybe_unused]] const size_t frameBufSize = readPOD<uint32_t>(_ipcMessage.data(), 0);
                	[[maybe_unused]] const size_t controlsBufSize = readPOD<uint32_t>(_ipcMessage.data(), 4);

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


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

                	ControlList controls = IPADataSerializer<ControlList>::deserialize(
                        	_ipcMessage.data().cbegin() + controlsStart,
                        	_ipcMessage.data().cend(),
                        	&controlSerializer_);

ipa_->queueRequest(frame, controls);

			break;
		}

		case _IPU3Cmd::FillParamsBuffer: {
		                
                	[[maybe_unused]] const size_t frameBufSize = readPOD<uint32_t>(_ipcMessage.data(), 0);
                	[[maybe_unused]] const size_t bufferIdBufSize = readPOD<uint32_t>(_ipcMessage.data(), 4);

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


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

                	uint32_t bufferId = IPADataSerializer<uint32_t>::deserialize(
                        	_ipcMessage.data().cbegin() + bufferIdStart,
                        	_ipcMessage.data().cend());

ipa_->fillParamsBuffer(frame, bufferId);

			break;
		}

		case _IPU3Cmd::ProcessStatsBuffer: {
		                
                	[[maybe_unused]] const size_t frameBufSize = readPOD<uint32_t>(_ipcMessage.data(), 0);
                	[[maybe_unused]] const size_t frameTimestampBufSize = readPOD<uint32_t>(_ipcMessage.data(), 4);
                	[[maybe_unused]] const size_t bufferIdBufSize = readPOD<uint32_t>(_ipcMessage.data(), 8);
                	[[maybe_unused]] const size_t sensorControlsBufSize = readPOD<uint32_t>(_ipcMessage.data(), 12);

                	const size_t frameStart = 16;
                	const size_t frameTimestampStart = frameStart + frameBufSize;
                	const size_t bufferIdStart = frameTimestampStart + frameTimestampBufSize;
                	const size_t sensorControlsStart = bufferIdStart + bufferIdBufSize;


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

                	int64_t frameTimestamp = IPADataSerializer<int64_t>::deserialize(
                        	_ipcMessage.data().cbegin() + frameTimestampStart,
                        	_ipcMessage.data().cbegin() + frameTimestampStart + frameTimestampBufSize);

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

                	ControlList sensorControls = IPADataSerializer<ControlList>::deserialize(
                        	_ipcMessage.data().cbegin() + sensorControlsStart,
                        	_ipcMessage.data().cend(),
                        	&controlSerializer_);

ipa_->processStatsBuffer(frame, frameTimestamp, bufferId, sensorControls);

			break;
		}

		default:
			LOG(IPAProxyIPU3Worker, Error) << "Unknown command " << _ipcMessage.header().cmd;
		}
	}

	int init(std::unique_ptr<IPAModule> &ipam, UniqueFD socketfd)
	{
		if (socket_.bind(std::move(socketfd)) < 0) {
			LOG(IPAProxyIPU3Worker, Error)
				<< "IPC socket binding failed";
			return EXIT_FAILURE;
		}
		socket_.readyRead.connect(this, &IPAProxyIPU3Worker::readyRead);

		ipa_ = dynamic_cast<IPAIPU3Interface *>(ipam->createInterface());
		if (!ipa_) {
			LOG(IPAProxyIPU3Worker, Error)
				<< "Failed to create IPA interface instance";
			return EXIT_FAILURE;
		}

		ipa_->setSensorControls.connect(this, &IPAProxyIPU3Worker::setSensorControls);
		ipa_->paramsBufferReady.connect(this, &IPAProxyIPU3Worker::paramsBufferReady);
		ipa_->metadataReady.connect(this, &IPAProxyIPU3Worker::metadataReady);
		return 0;
	}

	void run()
	{
		EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
		while (!exit_)
			dispatcher->processEvents();
	}

	void cleanup()
	{
		delete ipa_;
		socket_.close();
	}

private:


        void setSensorControls(
        	const uint32_t frame,
        	const ControlList &sensorControls,
        	const ControlList &lensControls)
	{
		IPCMessage::Header header = {
			static_cast<uint32_t>(_IPU3EventCmd::SetSensorControls),
			0
		};
		IPCMessage _message(header);

		
	std::vector<uint8_t> frameBuf;
	std::tie(frameBuf, std::ignore) =
		IPADataSerializer<uint32_t>::serialize(frame);
	std::vector<uint8_t> sensorControlsBuf;
	std::tie(sensorControlsBuf, std::ignore) =
		IPADataSerializer<ControlList>::serialize(sensorControls, &controlSerializer_);
	std::vector<uint8_t> lensControlsBuf;
	std::tie(lensControlsBuf, std::ignore) =
		IPADataSerializer<ControlList>::serialize(lensControls, &controlSerializer_);
	appendPOD<uint32_t>(_message.data(), frameBuf.size());
	appendPOD<uint32_t>(_message.data(), sensorControlsBuf.size());
	appendPOD<uint32_t>(_message.data(), lensControlsBuf.size());
	_message.data().insert(_message.data().end(), frameBuf.begin(), frameBuf.end());
	_message.data().insert(_message.data().end(), sensorControlsBuf.begin(), sensorControlsBuf.end());
	_message.data().insert(_message.data().end(), lensControlsBuf.begin(), lensControlsBuf.end());

		int _ret = socket_.send(_message.payload());
		if (_ret < 0)
			LOG(IPAProxyIPU3Worker, Error)
				<< "Sending event setSensorControls() failed: " << _ret;

		LOG(IPAProxyIPU3Worker, Debug) << "setSensorControls done";
	}

        void paramsBufferReady(
        	const uint32_t frame)
	{
		IPCMessage::Header header = {
			static_cast<uint32_t>(_IPU3EventCmd::ParamsBufferReady),
			0
		};
		IPCMessage _message(header);

		
	std::vector<uint8_t> frameBuf;
	std::tie(frameBuf, std::ignore) =
		IPADataSerializer<uint32_t>::serialize(frame);
	_message.data().insert(_message.data().end(), frameBuf.begin(), frameBuf.end());

		int _ret = socket_.send(_message.payload());
		if (_ret < 0)
			LOG(IPAProxyIPU3Worker, Error)
				<< "Sending event paramsBufferReady() failed: " << _ret;

		LOG(IPAProxyIPU3Worker, Debug) << "paramsBufferReady done";
	}

        void metadataReady(
        	const uint32_t frame,
        	const ControlList &metadata)
	{
		IPCMessage::Header header = {
			static_cast<uint32_t>(_IPU3EventCmd::MetadataReady),
			0
		};
		IPCMessage _message(header);

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

		int _ret = socket_.send(_message.payload());
		if (_ret < 0)
			LOG(IPAProxyIPU3Worker, Error)
				<< "Sending event metadataReady() failed: " << _ret;

		LOG(IPAProxyIPU3Worker, Debug) << "metadataReady done";
	}


	IPAIPU3Interface *ipa_;
	IPCUnixSocket socket_;

	ControlSerializer controlSerializer_;

	bool exit_;
};

int main(int argc, char **argv)
{
	/* Uncomment this for debugging. */
#if 0
	std::string logPath = "/tmp/libcamera.worker." +
			      std::to_string(getpid()) + ".log";
	logSetFile(logPath.c_str());
#endif

	if (argc < 3) {
		LOG(IPAProxyIPU3Worker, Error)
			<< "Tried to start worker with no args: "
			<< "expected <path to IPA so> <fd to bind unix socket>";
		return EXIT_FAILURE;
	}

	UniqueFD fd(std::stoi(argv[2]));
	LOG(IPAProxyIPU3Worker, Info)
		<< "Starting worker for IPA module " << argv[1]
		<< " with IPC fd = " << fd.get();

	std::unique_ptr<IPAModule> ipam = std::make_unique<IPAModule>(argv[1]);
	if (!ipam->isValid() || !ipam->load()) {
		LOG(IPAProxyIPU3Worker, Error)
			<< "IPAModule " << argv[1] << " isn't valid";
		return EXIT_FAILURE;
	}

	/*
	 * Shutdown of proxy worker can be pre-empted by events like
	 * SIGINT/SIGTERM, even before the pipeline handler can request
	 * shutdown. Hence, assign a new gid to prevent signals on the
	 * application being delivered to the proxy.
	 */
	if (setpgid(0, 0) < 0) {
		int err = errno;
		LOG(IPAProxyIPU3Worker, Warning)
			<< "Failed to set new gid: " << strerror(err);
	}

	IPAProxyIPU3Worker proxyWorker;
	int ret = proxyWorker.init(ipam, std::move(fd));
	if (ret < 0) {
		LOG(IPAProxyIPU3Worker, Error)
			<< "Failed to initialize proxy worker";
		return ret;
	}

	LOG(IPAProxyIPU3Worker, Debug) << "Proxy worker successfully initialized";

	proxyWorker.run();

	proxyWorker.cleanup();

	return 0;
}