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

#pragma once

#pragma GCC diagnostic ignored "-Wold-style-cast"

#include <string>
#include <Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuery.h>
#include <Swiften/LinkLocal/DNSSD/DNSSDResolveHostnameQuery.h>
#include <Swiften/EventLoop/EventLoop.h>
#include <Swiften/Network/HostAddress.h>
#include <boost/numeric/conversion/cast.hpp>

#include <netinet/in.h>

namespace Swift {
    class BonjourQuerier;

    class BonjourResolveHostnameQuery : public DNSSDResolveHostnameQuery, public BonjourQuery {
        public:
            BonjourResolveHostnameQuery(const std::string& hostname, int interfaceIndex, std::shared_ptr<BonjourQuerier> querier, EventLoop* eventLoop) : BonjourQuery(querier, eventLoop) {
                DNSServiceErrorType result = DNSServiceGetAddrInfo(
                        &sdRef, 0, boost::numeric_cast<unsigned int>(interfaceIndex), kDNSServiceProtocol_IPv4,
                        hostname.c_str(),
                        &BonjourResolveHostnameQuery::handleHostnameResolvedStatic, this);
                if (result != kDNSServiceErr_NoError) {
                    sdRef = nullptr;
                }
            }

            //void DNSSDResolveHostnameQuery::run() {
            void run() {
                if (sdRef) {
                    BonjourQuery::run();
                }
                else {
                    eventLoop->postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>()), shared_from_this());
                }
            }

            void finish() {
                BonjourQuery::finish();
            }

        private:
            static void handleHostnameResolvedStatic(DNSServiceRef, DNSServiceFlags, uint32_t, DNSServiceErrorType errorCode, const char*, const struct sockaddr *address, uint32_t, void *context) {
                static_cast<BonjourResolveHostnameQuery*>(context)->handleHostnameResolved(errorCode, address);
            }

            void handleHostnameResolved(DNSServiceErrorType errorCode, const struct sockaddr *rawAddress) {
                if (errorCode) {
                    eventLoop->postEvent(
                                boost::bind(boost::ref(onHostnameResolved),
                                boost::optional<HostAddress>()),
                            shared_from_this());
                }
                else {
                    assert(rawAddress->sa_family == AF_INET);
                    const sockaddr_in* sa = reinterpret_cast<const sockaddr_in*>(rawAddress);
                    uint32_t address = ntohl(sa->sin_addr.s_addr);
                    eventLoop->postEvent(boost::bind(
                                boost::ref(onHostnameResolved),
                                HostAddress(reinterpret_cast<unsigned char*>(&address), 4)),
                            shared_from_this());
                }
            }
    };
}