/*
 * Copyright (c) 2010-2016 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#pragma once

#include <mutex>

#include <boost/numeric/conversion/cast.hpp>

#include <Swiften/Base/ByteArray.h>
#include <Swiften/EventLoop/EventLoop.h>
#include <Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h>
#include <Swiften/LinkLocal/DNSSD/DNSSDRegisterQuery.h>

namespace Swift {
    class BonjourQuerier;

    class BonjourRegisterQuery : public DNSSDRegisterQuery, public BonjourQuery {
        public:
            BonjourRegisterQuery(const std::string& name, int port, const ByteArray& txtRecord, std::shared_ptr<BonjourQuerier> querier, EventLoop* eventLoop) : BonjourQuery(querier, eventLoop) {
                DNSServiceErrorType result = DNSServiceRegister(
                        &sdRef, 0, 0, name.c_str(), "_presence._tcp", nullptr, nullptr, boost::numeric_cast<unsigned short>(port),
                        boost::numeric_cast<unsigned short>(txtRecord.size()), vecptr(txtRecord),
                        &BonjourRegisterQuery::handleServiceRegisteredStatic, this);
                if (result != kDNSServiceErr_NoError) {
                    sdRef = nullptr;
                }
            }

            void registerService() {
                if (sdRef) {
                    run();
                }
                else {
                    eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
                }
            }

            void unregisterService() {
                finish();
            }

            void updateServiceInfo(const ByteArray& txtRecord) {
                std::lock_guard<std::mutex> lock(sdRefMutex);
                DNSServiceUpdateRecord(sdRef, nullptr, 0, boost::numeric_cast<unsigned short>(txtRecord.size()), vecptr(txtRecord), 0);
            }

        private:
            static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) {
                static_cast<BonjourRegisterQuery*>(context)->handleServiceRegistered(errorCode, name, regtype, domain);
            }

            void handleServiceRegistered(DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain) {
                if (errorCode != kDNSServiceErr_NoError) {
                    eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this());
                }
                else {
                    eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, domain, regtype, 0))), shared_from_this());
                }
            }
    };
}