#include <iostream>
#include <iostream>
#include <string>
#include <cstring>
#include <avahi-client/client.h>
#include <avahi-client/publish.h>
#include <avahi-common/alternative.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include <avahi-common/timeval.h>
#include "publisher.h"
namespace libpublisher
{
	static AvahiEntryGroup *group;
	static AvahiSimplePoll *poll_;
	static const char *name;
	std::string service_type, message;
	AvahiStringList *dns_message;
	int  port_number;
	void publisher::entry_group_callback(AvahiEntryGroup * _group, AvahiEntryGroupState _state, void * userdata)
	{
		group = _group;
		switch(_state)
		{
			case AVAHI_ENTRY_GROUP_FAILURE:
				std::cerr << "ENTRY GROUP FAILURE: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(_group))) << std::endl;
				avahi_simple_poll_quit(poll_);
			break;
			case AVAHI_ENTRY_GROUP_COLLISION :
			case AVAHI_ENTRY_GROUP_ESTABLISHED :
			case AVAHI_ENTRY_GROUP_UNCOMMITED:
			case AVAHI_ENTRY_GROUP_REGISTERING:
				;
			break;
		}
	}
		void publisher::create_services(AvahiClient * _client)
		{
			if ((!group)&&(!(group = avahi_entry_group_new(_client, entry_group_callback, NULL))))
			{
				std::cout << "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(_client)) << std::endl;
				avahi_simple_poll_quit(poll_);
			}
			if (avahi_entry_group_is_empty(group))
			{
				auto ret = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), name, service_type.c_str() , NULL, NULL, port_number, dns_message);
				if (ret < 0)
				{
					if (ret == AVAHI_ERR_COLLISION){
						create_services(_client);
					}
					std::cerr << "Failed to add _ipp._tcp service:" << avahi_strerror(ret) << std::endl;
					avahi_simple_poll_quit(poll_);
				}
				if ((ret = avahi_entry_group_commit(group)) < 0) {
					std::cerr << "Failed to commit entry group: " << avahi_strerror(ret) << std::endl;
					avahi_simple_poll_quit(poll_);
				}
			}
			return;
		}
		void publisher::client_callback(AvahiClient *_client, AvahiClientState _state, void *userdata)
		{
			switch (_state)
			{
				case AVAHI_CLIENT_S_RUNNING:
					create_services(_client);
				break;
				case AVAHI_CLIENT_FAILURE:
					std::cerr << 	"Client failure: " <<  avahi_strerror(avahi_client_errno(_client)) << std::endl;
					avahi_simple_poll_quit(poll_);
				break;
				case AVAHI_CLIENT_S_COLLISION:
				case AVAHI_CLIENT_S_REGISTERING:
					if (group) avahi_entry_group_reset(group);
				break;
				case AVAHI_CLIENT_CONNECTING:
					;
				break;
			}
		}
		void publisher::treat_collision(AvahiClient * _client)
		{
			char *n;
			n = avahi_alternative_service_name(name);
			avahi_free((void *)name);
			name = n;
			fprintf(stderr, "Service name collision, renaming service to '%s'\n", name);
			avahi_entry_group_reset(group);
			create_services(_client);
			return;
		}
		void publisher::publish_avahi_service(const char *_service_name, const int _port_number, const char *_service_type)
		{
			name = _service_name;
			port_number = _port_number;
			service_type = _service_type;
			poll_ = avahi_simple_poll_new();
			if(poll_){
				int error;
				AvahiClient *client = avahi_client_new(avahi_simple_poll_get(poll_), static_cast<AvahiClientFlags>(0), client_callback, NULL,  &error);
				if(client){
					avahi_simple_poll_loop(poll_);
					avahi_client_free(client);
				} else {
					std::cout << "FAILED TO CREATE AVAHI CLIENT OBJECT: " << avahi_strerror(error) << std::endl;
					avahi_simple_poll_free(poll_);
				}
			} else {
				std::cout << "FAILED TO CREATE AVAHI SIMPLE POLL OBJECT: " << std::endl;
			}
		}
		publisher::publisher(const std::string &service_name, const int &port_number, const std::string &service_type, std::vector<std::string> &message)
		{
			dns_message = avahi_string_list_new("version = V1.13", NULL);
			for(auto i = 0; i < message.size(); i++)
				dns_message = avahi_string_list_add(dns_message,std::string("message"+std::to_string(i)+"="+message.at(i)).c_str());
			publish_avahi_service(service_name.c_str(), port_number, service_type.c_str());
		}
		publisher::~publisher()
		{
			avahi_simple_poll_free(poll_);
		}
}