#include "common/platform.h"
#include "admin/list_metadataservers_command.h"

#include <iomanip>
#include <iostream>

#include "common/cltoma_communication.h"
#include "common/human_readable_format.h"
#include "common/lizardfs_version.h"
#include "common/matocl_communication.h"
#include "common/server_connection.h"
#include "common/sockets.h"
#include "admin/metadataserver_status_command.h"

std::string ListMetadataserversCommand::name() const {
	return "list-metadataservers";
}

void ListMetadataserversCommand::usage() const {
	std::cerr << name() << " <master ip> <master port>" << std::endl;
	std::cerr << "    Prints status of active metadata servers" << std::endl;
}

LizardFsProbeCommand::SupportedOptions ListMetadataserversCommand::supportedOptions() const {
	return { {kPorcelainMode, kPorcelainModeDescription} };
}

template<class T>
void printInfo(bool porcelain, const std::string& name, T value) {
	if (porcelain) {
		std::cout << value << "\n";
	} else {
		std::cout << "\t" << name << ": " << value << "\n";
	}
}

template<class T1, class T2, class... Args>
void printInfo(bool porcelain,
		const std::string& name1, T1 value1,
		const std::string& name2, T2 value2,
		const Args&... args) {
	if (porcelain) {
		std::cout << value1 << " ";
	} else {
		std::cout << "\t" << name1 << ": " << value1 << "\n";
	}
	printInfo(porcelain, name2, value2, args...);
}

void ListMetadataserversCommand::run(const Options& options) const {
	if (options.arguments().size() != 2) {
		throw WrongUsageException("Expected <master ip> and <master port> for " + name());
	}

	uint32_t ip;
	uint16_t port;
	std::string ipString = options.argument(0);
	std::string portString = options.argument(1);
	tcpresolve(ipString.c_str(), portString.c_str(), &ip, &port, false);
	ServerConnection connection(NetworkAddress(ip, port));
	auto request = cltoma::metadataserversList::build();
	auto response = connection.sendAndReceive(request, LIZ_MATOCL_METADATASERVERS_LIST);

	std::vector<MetadataserverListEntry> shadowsList;
	uint32_t masterVersion;
	matocl::metadataserversList::deserialize(response, masterVersion, shadowsList);

	// A small hack: place the master at the beggining of the metadataservers list
	shadowsList.emplace_back(ip, port, masterVersion);
	std::reverse(shadowsList.begin(), shadowsList.end());
	int server = 1;
	for (const auto& e : shadowsList) {
		MetadataserverStatus s{"unknown", "unknown", 0};
		std::string hostname;
		// If information about MATOCL_SERV_PORT used by shadows isn't available we cannot query it
		// for its hostname, metaversion etc.
		if (e.port != 0) {
			ServerConnection shadowConnection(NetworkAddress(e.ip, e.port));
			s = MetadataserverStatusCommand::getStatus(shadowConnection);

			auto request = cltoma::hostname::build();
			auto response = shadowConnection.sendAndReceive(request, LIZ_MATOCL_HOSTNAME);
			matocl::hostname::deserialize(response, hostname);
		} else {
			hostname = "unknown";
		}

		if (!options.isSet(kPorcelainMode)) {
			std::cout << "Server " << server++ << ":" << std::endl;
		}
		printInfo(options.isSet(kPorcelainMode),
				"IP", ipToString(e.ip),
				"Port", e.port,
				"Hostname", hostname,
				"Personality", s.personality,
				"Status", s.serverStatus,
				"Metadata version", s.metadataVersion,
				"Version", lizardfsVersionToString(e.version));
	}
}
