#include <iostream>
#include <map>
#include <functional>
#include <memory>

using namespace std;
using namespace std::placeholders;

#define REGISTER_CONNECTOR(map, connector, id) \
{ \
	ConnectorFunc connectorFunc = {}; \
	connectorFunc.openFunc = std::bind(&decltype(connector)::open, connector); \
	connectorFunc.recvFunc = std::bind(&decltype(connector)::recv, connector, _1, _2); \
	connectorFunc.sendFunc = std::bind(&decltype(connector)::send, connector, _1, _2); \
	connectorFunc.closeFunc = std::bind(&decltype(connector)::close, connector); \
	map.insert(std::make_pair(id, connectorFunc)); \
}

#define GET_CONNECTOR_FUNC(map, id, connectorFunc)  \
{ \
	auto iter = funcMap.find(type); \
	if(iter != funcMap.end()) \
	{ \
		connectorFunc = iter->second; \
	} \
}

typedef void (OPEN_FUNC)();
typedef size_t (RECV_FUNC)(void*, size_t);
typedef void (SEND_FUNC)(void*, size_t);
typedef void (CLOSE_FUNC)();

struct ConnectorFunc
{
	std::function<OPEN_FUNC> openFunc;
	std::function<RECV_FUNC> recvFunc;
	std::function<SEND_FUNC> sendFunc;
	std::function<CLOSE_FUNC> closeFunc;
};

typedef enum { TCP, SERIAL, CONNECT_COUNT} CONNECT_TYPE;

// Define interface
class IConnector
{
public:
	virtual void open() = 0;
	virtual size_t recv(void* buffer, size_t size) = 0;
	virtual void send(void* buffer, size_t size) = 0;
	virtual void close() = 0;
};

// Define CSerial
class CSerial	/*: public IConnector*/ // From 3rd?
{
public:
	void open();
	size_t recv(void* buffer, size_t size);
	void send(void* buffer, size_t size);
	void close();
};

// Define CTcp
class CTcp	   /*: public IConnector*/ // From 3rd?
{
public:
	void open();
	size_t recv(void* buffer, size_t size);
	void send(void* buffer, size_t size);
	void close();
};

// Define CConnector
class CConnector: public IConnector
{
public:
	CConnector();
	void open();
	size_t recv(void* buffer, size_t size);
	void send(void* buffer, size_t size);
	void close();
	void setType(CONNECT_TYPE type);

private:
	CONNECT_TYPE mType;
	CTcp mTcp;
	CSerial mSerial;
	std::map<CONNECT_TYPE, ConnectorFunc> funcMap;
	ConnectorFunc connectorFunc;
};

// Implement CSerial
void CSerial::open()
{
	cout <<	"CSerial::" <<  __func__ << endl;
}

size_t CSerial::recv(void* buffer, size_t size)
{
	cout << "CSerial::" << __func__ << endl;
	return 0;
}

void CSerial::send(void* buffer, size_t size)
{
	cout << "CSerial::" << __func__ << endl;
}

void CSerial::close()
{
	cout << "CSerial::" << __func__ << endl;
}

// Implement CTcp
void CTcp::open()
{
	cout << "CTcp::" <<  __func__ << endl;
}

size_t CTcp::recv(void* buffer, size_t size)
{
	cout << "CTcp::" << __func__ << endl;
	return 0;
}

void CTcp::send(void* buffer, size_t size)
{
	cout << "CTcp::" << __func__ << endl;
}

void CTcp::close()
{
	cout << "CTcp::" << __func__ << endl;
}

// Implement CConnector
CConnector::CConnector(): mType(CONNECT_TYPE::TCP), mTcp(), mSerial(), connectorFunc()
{
	REGISTER_CONNECTOR(funcMap, mTcp, CONNECT_TYPE::TCP);
	REGISTER_CONNECTOR(funcMap, mSerial, CONNECT_TYPE::SERIAL);
}

void CConnector::open()
{
	if(connectorFunc.openFunc != NULL)
		connectorFunc.openFunc();
}

size_t CConnector::recv(void* buffer, size_t size)
{
	if(connectorFunc.recvFunc != NULL)
		return connectorFunc.recvFunc(buffer, size);
	return -1;
}

void CConnector::send(void* buffer, size_t size)
{
	if(connectorFunc.sendFunc != NULL)
		connectorFunc.sendFunc(buffer, size);
}

void CConnector::close()
{
	if(connectorFunc.closeFunc != NULL)
		connectorFunc.closeFunc();
}

void CConnector::setType(CONNECT_TYPE type)
{
	mType = type;
	GET_CONNECTOR_FUNC(funcMap, type, connectorFunc);
}

void testTCP()
{
	CConnector* conector = new CConnector();
	conector->setType(CONNECT_TYPE::TCP);
	conector->open();
	conector->recv(NULL, 0);
	conector->send(NULL, 0);
	conector->close();
	delete conector;
	cout << endl;
}

void testSerial()
{
	CConnector* conector = new CConnector();
	conector->setType(CONNECT_TYPE::SERIAL);
	conector->open();
	conector->recv(NULL, 0);
	conector->send(NULL, 0);
	conector->close();
	delete conector;
	cout << endl;
}

int main(int argc, char* argv[])
{
	testTCP();
	testSerial();

	return 0;
}