int main() {
	::SetConsoleTitle("ClientHandler");

	Show::attach(&std::cout);
	Show::start();
	Show::title("\n  ClientHandler Server started");

	BlockingQueue<HttpMessage> msgQ;
	SocketSystem ss;
	SocketListener sl(8080, Socket::IP6);
	ClientHandler cp(msgQ);
	sl.start(cp);

	try
	{
		while (true)
		{
			HttpMessage msg = msgQ.deQ();
			std::cout << "\n\n  clienthandler " + msg.findValue("toAddr") + " recvd message contents:\n  "
				+ msg.bodyString() + "\n  from client " + msg.findValue("fromAddr") + "\n";
			if (msg.bodyString() == "closeServer") {
				::Sleep(100);
			}
		}
	}
	catch (std::exception& exc)
	{
		Show::write("\n  Exeception caught: ");
		std::string exMsg = "\n  " + std::string(exc.what()) + "\n\n";
		Show::write(exMsg);
	}

	std::cout << "\n\n";
	return 0;
}
// -------------< establish client handler and start receiving messages >--------------
void Receiver::start() {
	ClientHandler ch(rcvrQ);
	size_t port = stoi(localUrl.substr(localUrl.find(":") + 1, 4));
	SocketSystem ss;
	SocketListener sl(port, Socket::IP6);
	sl.start(ch);
	while (true)
		{
			HttpMessage msg = rcvrQ.deQ();
			std::cout << "\n\n  " + msg.findValue("toAddr") + " recvd message contents:\n  "
				+ msg.bodyString() + "\n  from " + msg.findValue("fromAddr");
			if (msg.bodyString() == "closeServer") break;
		}
}
// ------------------------------------ Starts Receiver for the this component ---------------------------
void Server::startReceiverThread()
{
	BlockingQueue<HttpMessage> msgQ;
	try
	{
		SocketSystem ss;
		SocketListener sl(getServerPort(), Socket::IP6);
		Receiver cp(msgQ,"Server");
		sl.start(cp);
		/*
		* Since this is a server the loop below never terminates.
		* We could easily change that by sending a distinguished
		* message for shutdown.
		*/
		while (true)
		{			
			HttpMessage msg = msgQ.deQ();
			std::string cPort = msg.findValue("From-Port");
			if (msg.attributes()[0].first == "GET") {												// Checks if its an file extraction request
				std::string filename = msg.findValue("file");				
				Show::write("\n\n Client @: "+cPort+ " => requested file :: " + filename);
				sendServerFiles(msg);
			}
			else
			Show::write("\n\n Message from client @:"+cPort +"  =>  " + msg.bodyString());
		}
	}
	catch (std::exception& exc)
	{
		Show::write("\n  Exeception caught: ");
		std::string exMsg = "\n  " + std::string(exc.what()) + "\n\n";
		Show::write(exMsg);
	}
}
//----< receiver functionality is defined by this function >---------
void ClientHandler::operator()(Socket socket) {
	std::function<void()> threadProc = [&]() {
		while (true) {
			HttpMessage msg = readMessage(socket); // read a message from socket
			if (msg.attributes().size() == 0 || msg.bodyString() == "quit") break;
			if (msg.findValue("type") == "returnList") {
				sendMessage(msg);
				continue;
			}
			else if (msg.findValue("type") == "returnFiles") {
				Sender sndr(msg.findValue("fromAddr"));
				sndr.start();
				std::vector<std::string> files = splitString(msg.bodyString());
				for (std::string file : files) {
					std::string fileSpec = searchFile(file);
					FileSystem::FileInfo fi(fileSpec);
					FileSystem::File fileIn(fileSpec);
					fileIn.open(FileSystem::File::in, FileSystem::File::binary);
					if (!fileIn.isGood()) {
						std::cout << "\n  could not open file " << file;
						continue;
					}
					size_t fileSize = fi.size();
					std::string sizeString = Converter<size_t>::toString(fileSize);
					HttpMessage message = sndr.makeMessage(4, "", msg.findValue("toAddr"));
					message.addAttribute(HttpMessage::Attribute("file", fileSpec));
					message.addAttribute(HttpMessage::Attribute("content-length", sizeString));
					sndr.postMessage(message);
				}
				sndr.postMessage(sndr.makeMessage(0, "closeServer", msg.findValue("toAddr")));
				sndr.postMessage(sndr.makeMessage(0, "quit", msg.findValue("toAddr")));
				sndr.wait();
				continue;
			}
			else if (msg.attributes()[0].second == "error") {
				sendMessage(msg);
				continue;
			}
			msgQ_.enQ(msg);
		}
	};
	std::thread receiveThread(threadProc);
	receiveThread.join();
}
// ------------------< prepare and send message to recepient >-------------
void ClientHandler::sendMessage(HttpMessage msg) {
	HttpMessage message;
	Sender sndr(msg.findValue("fromAddr"));
	sndr.start();
	message = sndr.makeMessage(0, msg.bodyString(), msg.findValue("toAddr"));
	sndr.postMessage(message);
	sndr.postMessage(sndr.makeMessage(0, "closeServer", msg.findValue("toAddr")));
	sndr.postMessage(sndr.makeMessage(0, "quit", msg.findValue("toAddr")));
	sndr.wait();
}
// -----------------< get the requested files, with or without dependencies >------------
HttpMessage ClientHandler::getFiles(HttpMessage msg, std::string type) {
	HttpMessage message;
	if (!FileSystem::Directory::exists("../TestFileServer/" + msg.bodyString()))
		return errorMessage(msg, "\n  no package " + msg.bodyString());
	std::string path = "../TestFileServer/" + msg.bodyString() + "/";
	std::vector<std::string> files = FileSystem::Directory::getFiles(path, "*.ver*");
	if (files.size() == 0) return errorMessage(msg, "\n  so files in package " + msg.bodyString());
	std::vector<std::string> sendFiles;
	for (int i = files.size() - 1; i >= 0; --i) { // figure out the latest version of the files
		std::string ext = files[i].substr(files[i].find_last_of(".") + 1);
		if (ext == "xml") continue;
		std::string name = files[i].substr(0, files[i].find_last_of("."));
		if (find(sendFiles, name) == sendFiles.size())
			sendFiles.push_back(files[i]); // add only unique file names to be sent
	}
	std::vector<std::shared_ptr<AbstractXmlElement>> deps;
	if (type == "FileWithDeps") { // figure out dependencies of files to be sent
		for (int i = 0; i < sendFiles.size(); ++i) {
			std::string file = sendFiles[i];
			if (!FileSystem::File::exists("../TestFileServer/" + msg.bodyString() + "/" + file)) continue;
			std::string xmlname = "../TestFileServer/" + msg.bodyString() + "/" + file + ".xml";
			XmlDocument doc(xmlname, XmlDocument::file);
			deps = doc.element("deps").descendents().select();
			for (auto dep : deps)
				if (dep->tag() == "") { // prevent circular dependencies to be stuck in a loop
					std::string depName = dep->value().substr(0, dep->value().find("\n"));
					if (find(sendFiles, depName.substr(0, depName.find_last_of("."))) == sendFiles.size()) sendFiles.push_back(depName);
				}
		}
	}
	std::string body;
	for (size_t i = 0; i < sendFiles.size() - 1; ++i) body += sendFiles[i] + ",";
	body += sendFiles[sendFiles.size() - 1];
	message.addAttribute(HttpMessage::attribute("fromAddr", msg.findValue("toAddr")));
	message.addAttribute(HttpMessage::attribute("toAddr", msg.findValue("fromAddr")));
	message.addAttribute(HttpMessage::attribute("type", "returnFiles"));
	message.addBody(body);
	return message;
}
int main()
{
  Utils::Title("Testing HttpMessage class", '=');

  HttpMessage msg;
  msg.addAttribute(HttpMessage::attribute("Command", "GetFiles"));
  msg.addAttribute(HttpMessage::attribute("ToAddr", "127.0.0.1:8080"));
  msg.addAttribute(HttpMessage::attribute("FromAddr", "127.0.0.1:8081"));
  msg.addAttribute(HttpMessage::attribute("Mode", "OneWay"));
  msg.addAttribute(HttpMessage::attribute("content_length", "10"));

  msg.addBody(std::vector<HttpMessage::byte> { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });

  std::cout << "\n" << Utils::addHeaderAndFooterLines(msg.toString());
  
  Utils::title("testing headerString(), bodyString(), and toString()");
  std::cout << "\nheader string:";
  std::cout << "\n" << msg.headerString();
  std::cout << "\nbody string:";
  std::cout << "\n" << msg.bodyString();
  std::cout << "\n\nmessage string:";
  std::cout << "\n" << StringHelper::addHeaderAndFooterLines(msg.toString());

  Utils::title("Testing removeAttribute");
  putline();
  msg.removeAttribute("content_length");
  std::cout << msg.toString();
  putline();

  Utils::title("Testing addBody(const std::string&)");
  std::string msgBody = "<msg>this is a message</msg>";
  msg.addAttribute(HttpMessage::Attribute("content_length", Converter<size_t>::toString(msgBody.size())));
  msg.addBody(msgBody);
  std::cout << "\n" << StringHelper::addHeaderAndFooterLines(msg.toString());

  Utils::title("Testing parseAttribute(const std::string&)");
  std::string test2 = "name:value";
  std::cout << "\n  input = \"" << test2 << "\"";
  Attribute attrib2 = HttpMessage::parseAttribute(test2);
  std::cout << "\n  result is: " << HttpMessage::attribString(attrib2);

  test2 = " name : value ";
  std::cout << "\n  input = \"" << test2 << "\"";
  attrib2 = HttpMessage::parseAttribute(test2);
  std::cout << "\n  result is: " << HttpMessage::attribString(attrib2);

  Utils::title("Testing Message parsing");
  MockSocket sock(msg);
  HttpMessage msg2;
  while (true)
  {
    std::string line = sock.recvString();
    if (line.size() == 0)
      break;
    Attribute attrib = HttpMessage::parseAttribute(line);
    msg2.addAttribute(attrib);
  }
  Value val = msg2.findValue("content_length");
  if (val.size() > 0)
  {
    size_t numBytes = Converter<size_t>::toValue(val);
    byte* pBuffer = new byte[numBytes];
    sock.recv(numBytes, pBuffer);
    msg2.addBody(numBytes, pBuffer);
  }
  std::cout << "\n" << Utils::addHeaderAndFooterLines(msg2.toString());
  std::cout << "\n\n";
}