
#include "Client.h"

// Writes XML Header
bool Client::writeHeader()
{
	return writeln("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
};

// Writes TimedTriple
bool Client::writeTimedTriple(const string time, const string object,
							  const string attribute, const string value)
{
	return writeln("<TimedTriple time=\"" + time + "\" object=\"" + object +
		"\" attribute=\"" + attribute + "\" value=\"" + value + "\"/>");
};

// Writes TimedTriple
bool Client::writeTimedTriple(const string object, const string attribute,
	const string value)
{
	// eat time
	timeval tim;
#ifdef _WIN32 // there is no gettimeofday in the MSVC
	long int tc = GetTickCount();
	tim.tv_sec = tc / 1000;
	tim.tv_usec = (tc % 1000) * 1000;
#else
	gettimeofday(&tim, null);
#endif
	double dtim = tim.tv_sec*100 + tim.tv_usec/10000;

	// blow time
	char t[50];
	sprintf(t, "%.0f", dtim);
	string s(t);

	// sleep
	return writeTimedTriple(s, object, attribute, value);
};

// Writes TimedTriple Query
bool Client::writeQuery(const string object, const string attribute,
	const string value)
{
	bool ret = true;
	ret = ret && writeln("<Queries>");
	ret = ret && writeln("<Triple object=\"" + object + "\" attribute=\"" + attribute + "\" value=\"" + value + "\"/>");
	ret = ret && writeln("</Queries>");

	return ret;
}

// Writes TimedTriple Query
bool Client::writeQuery(const int start, const int end, const string object,
	const string attribute, const string value)
{
	// convert datum
	char s[50];
	sprintf(s, ":%d", start);
	char e[50];
	sprintf(e, ":%d", end);

	bool ret = true;
	ret = ret && writeln("<Queries>");
	ret = ret && writeln("<Triple object=\"" + object + "\" attribute=\"" + attribute + "\" value=\"" + value + "\" start=\"" + s + "\" end=\"" + e + "\"/>");
	ret = ret && writeln("</Queries>");

	return ret;
}

// Reads whole reply and returns it in a string
SSXMLParser Client::readReply()
{
	SSXMLParser red = SSXMLParser(rcv());

	int from = 0;
	while (red.finds("<uptodate/>", from, red.input.length()) == -1)
	{
		red.input += rcv();
		red.toLower();
	}

#ifdef _DEBUG
	cerr << "Parser: Recieved: " << red.input << endl;
#endif

	SSXMLParser head = SSXMLParser(red.getTag());
	if (head.findName().compare("?xml") != 0)
	{
		error("Can't read Hub server XML header.");
		SSXMLParser("");
	}

	head = SSXMLParser(red.getTag());
	if (head.findName().compare("updates") != 0)
	{
		error("Can't read Hub server updates header.");
		SSXMLParser("");
	}

	return red;
}

//
// Writes a line using send
// @return sucess
bool Client::writeln(const string msg)
{
	return write(msg + "\n");
}

//
// Writes a text using send 
// @return sucess
bool Client::write(const string msg)
{
	// connect if not
	if (!vld())
	{
		if (!conn())
		{
			return false;
		}
	}
	return snd(msg);
}


//
// Connect
// @return sucess
//
bool Client::conn()
{
#ifdef _WIN32
	// Prepare socket
	if (WSAStartup(wVersionRequested, &data) != 0)
	{
		error("Can't init socket");
		return false;
	}

	// prepare char* from port number
	char p[50];
	sprintf(p, ":%d", port);

	// create socket (IP, reliable, TCP)
	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
	{
		error("Can't create socket");
		return false;
	}

/*
	// get information of the remote computer
	if ((host = gethostbyname(hostName.c_str())) == NULL)
	{
		error(("Invalid adress: " + hostName).c_str());
		return false;
	}
*/
/*
	// Setup the hints address info structure
	// which is passed to the getaddrinfo() function
	struct addrinfo aiHints;
	memset(&aiHints, 0, sizeof(aiHints));
	aiHints.ai_family = AF_INET;
	aiHints.ai_socktype = SOCK_STREAM;
	aiHints.ai_protocol = IPPROTO_TCP;


	// Call getaddrinfo(). If the call succeeds,
	// the aiList variable will hold a linked list
	// of addrinfo structures containing response
	// information about the host
	struct addrinfo *aiList = NULL;

	if ((getaddrinfo(hostName.c_str(), p, &aiHints, &aiList)) != 0) {
		error("Can't get address info");
		return false;
	}

	// aiList->ai_addr
*/

	// fill the sockaddr_in structure
	// protocol family (IP)
	remote.sin_family = AF_INET;
	// remote IP adress
	remote.sin_addr.s_addr = inet_addr(hostName.c_str());

	// remote port number
	remote.sin_port = htons(port);

	// connecting...
	if (connect(sock, (sockaddr*)&remote, sizeof(remote)) == SOCKET_ERROR)
	{
		error(("Can't connect remote host: " + hostName + p).c_str());
		return false;
	}

#else

	// get information of the remote computer
	if ((host = gethostbyname(hostName.c_str())) == null) {
		error(("Invalid adress: " + hostName).c_str());
		return false;
	}

	// create socket (IP, reliable, TCP)
	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
		error("Can't create socket");
		return false;
	}

	// fill the sockaddr_in structure
	// protocol family (IP)
	remote.sin_family = AF_INET;
	// remote IP adress
	memcpy(&(remote.sin_addr), host->h_addr, host->h_length);
	// remote port number
	remote.sin_port = htons(port);

	// connecting...
	if (connect(sock, (sockaddr*)&remote, (unsigned int)sizeof(remote)) == -1) {
		char p[50];
		sprintf(p, ":%d", port);
		error(("Can't connect remote host: " + hostName + p).c_str());
		return false;
	}
#endif

#ifdef _DEBUG
	cerr << "Client: Connected to: " << hostName << ":" << port << endl;
#endif
	// connected = sucess
	return true;
}

// Send
// @return sucess
bool Client::snd(const string msg)
{
	// sending message
	if (send(sock, msg.c_str(), msg.size(), 0) == -1)
	{
		error("Can't send message: " + msg);
		return false;
	}

#ifdef _DEBUG
//	cerr << "Client: Sent: " << msg << endl;
#endif

	// sucess
	return true;
}

// Recieve all from the socket
// @return message (empty if no sucess)
string Client::rcv()
{
	// return str
	string str("");

	// buffer
	char buf [BUFSIZE+1];
	memset (buf, 0, BUFSIZE+1);

	// recieve all from the socket
	if (recv(sock, buf, BUFSIZE, 0) > 0)
	{
		str.append(buf);     // append recieved
//		memset (buf, 0, BUFSIZE+1);
#ifdef _DEBUG
//		cerr << "Client: Recieved: " << buf << endl;
#endif
	}
	return str;
}

//
//  prints the error to cerr
//
void Client::error(string message)
{
	cerr << "Client: Error: " << message << endl;
}

// Close connection
void Client::cls()
{
#ifdef _WIN32
	closesocket(sock);
	WSACleanup();
#else
	close(sock);
#endif
	sock = -1;
}

