#pragma once

#ifndef _SUPPORT_H
#define _SUPPORT_H

#ifdef _SUPPORT_SMALL_BUF
#define BUFSIZE 512
#define BUF_2_MAX 2048
#else
#define BUFSIZE 16384
#define BUF_2_MAX 16384
#endif

#define WIN32_LEAN_AND_MEAN

#include "ArchAbstract.h"

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <thread>
#include <functional>
#include <time.h>
#include "Crypto.h"


namespace Tools {
	char* strappend(char*, char*);
	char* strappend(char*, const char*);
	char* strappend(const char*, char*);
	char* strappend(const char*, const char*);
	void destructivePrint(char* message, ulong_64b size);
	void destructivePrint(char* message);
	void destructivePrintln(char* message, ulong_64b size);
	void destructivePrintln(char* message);
	ulong_64b indexOf(char*, char);
	ulong_64b lastIndexOf(char*, char);
	char* copydata(const char*, ulong_64b);
	char* toHexString(const void* data, ulong_64b size);
	char* toHexString(ulong_64b value);
	bool isDigit(char c);
	bool isNumber(char* c);
	bool isIP(char* c);
}

namespace IO {

	enum CryptoLevel { None, Prefer, Force };

	struct Packet {
		ulong_64b size;
		char* message;
	};

	class NetServer;
	class NetClient {
		friend class NetServer;					// Allow NetServer to access all members of NetClient

	private:
		volatile bool _open;					// Whether or not connection is open
		bool canWrite;							// Whether or not writing to peer is possible
		bool noThread;							// Whether or not reading incoming data should be / is being done in a separate thread
		char rBuf[BUFSIZE];						// Recieve buffer
		CryptoLevel preferEncrypted = CryptoLevel::None;// Whether or not the socket should attempt to request an encrypted channel
		bool encrypted = false;					// Whether or not negotiation determined the use of an encrypted channel
		bool firstMessage = true;				// Whether or not negotiation has yet ocurred
		ulong_64b fm_neg_size;
		bool fm_neg_hasLevel = false;
		bool fm_neg_hasSize = false;
		bool startNegotiate = false;
		std::vector<char>* sparse;
		std::vector<Packet>* outPacketBuf;
		Crypto::RSA::KeyData keys;				// Client's keysets (if using encryption)
		CryptoPP::RSAFunction pK;				// Remote host's public key (if using encryption)

		NetClient(SOCKET, bool, CryptoLevel, bool);// Special setup constructor
		NetClient(SOCKET, bool, Crypto::RSA::KeyData&, CryptoLevel = CryptoLevel::None, bool = false);// Create wrapper for existing socket
		void sharedSetup();						// Setup function for all constructor
		bool _write(char*, ulong_64b);			// Internal write function. Doesn't do any of the fancy auto encryption: just raw write...
		bool writeBufferedPackets();			// Flushes and deletes buffer
		void update();							// Read incoming data and store in buffers
	protected:
		std::thread listener;					// Incoming data listener (optional)
		SOCKET _socket;							// Underlying socket used for communication
		std::vector<Packet>* packets;			// Basically a set containing a backlog of unprocessed data. Will oly be used if event handler doesn't exist
		std::function<void(NetClient*, Packet)> evt;	// New data event handler
		std::function<void()> onDestroy;		// Event handler called when NetClient object is destroyed
	public:
		time_t commTime;						// Latest time a transaction occurred
		std::vector<char*> associatedData;
		NetClient(char* ipAddr, char* port, CryptoLevel = CryptoLevel::None);// Standard constructor for creating connection
		~NetClient();
		bool close();
		void closeWrite();
		bool isEncrypted();
		size_t getBOPCount();					// Should return the amount of buffered packets to be sent to server
		bool write(void* message, ulong_64b size);
		bool write(char* message);
		Packet read();
		void setEventHandler(std::function<void(NetClient*, Packet)>);		// Register a callback that is guaranteed to be called when the socket has at least one unprocessed packet
		void setOnDestroy(std::function<void()>);
		bool isOpen();
		ulong_64b available();
	};

	class NetServer {
		friend class NetClient;
	private:
		CryptoLevel pref;
		Crypto::RSA::KeyData keys;				// Server's keysets (if using encryption)

		std::function<void()> onDestroy;
		volatile bool _open;
		void updateClients();
	protected:
		std::thread clientListener;
		std::vector<std::function<bool(NetClient*)>>* handlers;
		std::vector<NetClient*>* clients;
	public:
		std::function<bool(NetClient*)> timeoutHandler;
		NetServer(char* port, std::function<bool(NetClient*)>, CryptoLevel);
		~NetServer();
		bool isOpen();
		CryptoLevel getCryptoPreference();
		void addHandler(std::function<bool(NetClient*)>);
		void clearHandlers();
		void setOnDestroy(std::function<void()>);
		bool close();
	};
}
#endif