#pragma once

#include "NativeSockets.h"

#include <winsock2.h>

#include <atomic>
#include <functional>
#include <string>
#include <thread>

class OscServer
{
public:
	struct Callbacks
	{
		std::function<bool(const std::string&, const std::string&, const std::string&, std::string&)> updateParameter;
	};

	OscServer();
	~OscServer();

	bool Start(const std::string& bindAddress, unsigned short port, const Callbacks& callbacks, std::string& error);
	void Stop();

	unsigned short GetPort() const { return mPort; }

private:
	friend struct OscServerTestAccess;

	struct OscMessage
	{
		std::string address;
		std::string valueJson;
	};

	void ServerLoop();
	bool DecodeMessage(const char* data, int byteCount, OscMessage& message, std::string& error) const;
	bool DispatchMessage(const OscMessage& message, std::string& error) const;
	static bool TryParseBindAddress(const std::string& bindAddress, in_addr& address, std::string& error);
	static bool DecodeArgument(const char* data, int byteCount, int& offset, char valueType, std::string& valueJson);
	static bool ReadPaddedString(const char* data, int byteCount, int& offset, std::string& value);
	static bool ReadInt32(const char* data, int byteCount, int& offset, int& value);
	static bool ReadFloat32(const char* data, int byteCount, int& offset, double& value);
	static bool ReadFloat64(const char* data, int byteCount, int& offset, double& value);
	static std::string BuildJsonString(const std::string& value);

	Callbacks mCallbacks;
	UniqueSocket mSocket;
	unsigned short mPort;
	std::thread mThread;
	std::atomic<bool> mRunning;
};
