/*
 * Copyright 2015 - 2022 gary@drinkingtea.net
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#ifdef OX_USE_STDLIB
#include <cstdio>

#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#else
#include <winsock.h>
#undef interface
#undef min
#undef max
#endif


#include "logconn.hpp"

#include <ox/std/bit.hpp>

namespace ox {

using namespace trace;

void closeSock(auto s) noexcept {
#ifdef _WIN32
	closesocket(s);
#else
	close(s);
#endif
}

LoggerConn::LoggerConn() noexcept: m_netThread([this]{this->msgSend();}) {
}

LoggerConn::~LoggerConn() noexcept {
	m_running = false;
	m_waitCond.notify_one();
	m_netThread.join();
	if (m_socket) {
		closeSock(m_socket);
	}
}

ox::Error LoggerConn::initConn(ox::CRStringView appName) noexcept {
	sockaddr_in addr{};
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	addr.sin_port = htons(5590);
	m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	oxReturnError(OxError(static_cast<ox::ErrorCode>(connect(m_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))));
	return sendInit({.appName = ox::BasicString<128>(appName)});
}

ox::Error LoggerConn::send(const char *buff, std::size_t len) const noexcept {
	std::size_t totalSent = 0;
	while (totalSent < len) {
		//std::fprintf(stdout, "Sending %lu/%lu bytes on socket %d\n", len, totalSent, m_socket);
		const auto sent = ::send(m_socket, buff, len, 0);
		if (sent < 0) {
			std::fprintf(stderr, "Could not send msg\n");
			return OxError(1, "Could not send msg");
		}
		totalSent += static_cast<std::size_t>(sent);
	}
	return {};
}

ox::Error LoggerConn::send(const TraceMsg &msg) noexcept {
	return send(MsgId::TraceEvent, msg);
}

ox::Error LoggerConn::sendInit(const InitTraceMsg &msg) noexcept {
	return send(MsgId::Init, msg);
}

void LoggerConn::msgSend() noexcept {
	while (true) {
		std::unique_lock lk(m_waitMut);
		m_waitCond.wait(lk);
		if (!m_running) {
			break;
		}
		std::lock_guard buffLk(m_buffMut);
		while (true) {
			ox::Array<char, ox::units::KB> tmp;
			const auto read = m_buff.read(tmp.data(), tmp.size());
			if (!read) {
				break;
			}
			//std::printf("LoggerConn: sending %lu bytes\n", read);
			oxIgnoreError(send(tmp.data(), read));
		}
	}
}

}
#endif
