# Based on resolver_ares.py from gevent
# Copyright (c) 2011 Denis Bilenko. See LICENSE for details.

from __future__ import absolute_import
import os
from _socket import gaierror
from gevent.hub import Waiter, get_hub
from minemeld.packages.gdns._ares import channel


class Dig(object):

    ares_class = channel

    # from arpa/nameser.h
    NS_C_INVALID = 0  # Cookie
    NS_C_IN = 1  # Internet
    NS_C_2 = 2  # unallocated/unsupported
    NS_C_CHAOS = 3  # MIT Chaos-net
    NS_C_HS = 4  # MIT Hesiod
    NS_C_NONE = 254  # prereq. sections in update requests
    NS_C_ANY = 255  # Wildcard match
    NS_C_MAX = 65536

    NS_T_INVALID = 0  # Cookie
    NS_T_A = 1  # Host address
    NS_T_NS = 2  # Authoritative server
    NS_T_MD = 3  # Mail destination
    NS_T_MF = 4  # Mail forwarder
    NS_T_CNAME = 5  # Canonical name
    NS_T_SOA = 6  # Start of authority zone
    NS_T_MB = 7  # Mailbox domain name
    NS_T_MG = 8  # Mail group member
    NS_T_MR = 9  # Mail rename name
    NS_T_NULL = 10  # Null resource record
    NS_T_WKS = 11  # Well known service
    NS_T_PTR = 12  # Domain name pointer
    NS_T_HINFO = 13  # Host information
    NS_T_MINFO = 14  # Mailbox information
    NS_T_MX = 15  # Mail routing information
    NS_T_TXT = 16  # Text strings
    NS_T_RP = 17  # Responsible person
    NS_T_AFSDB = 18  # AFS cell database
    NS_T_X25 = 19  # X_25 calling address
    NS_T_ISDN = 20  # ISDN calling address
    NS_T_RT = 21  # Router
    NS_T_NSAP = 22  # NSAP address
    NS_T_NSAP_PTR = 23  # Reverse NSAP lookup (deprecated)
    NS_T_SIG = 24  # Security signature
    NS_T_KEY = 25  # Security key
    NS_T_PX = 26  # X.400 mail mapping
    NS_T_GPOS = 27  # Geographical position (withdrawn)
    NS_T_AAAA = 28  # Ip6 Address
    NS_T_LOC = 29  # Location Information
    NS_T_NXT = 30  # Next domain (security)
    NS_T_EID = 31  # Endpoint identifier
    NS_T_NIMLOC = 32  # Nimrod Locator
    NS_T_SRV = 33  # Server Selection
    NS_T_ATMA = 34  # ATM Address
    NS_T_NAPTR = 35  # Naming Authority PoinTeR
    NS_T_KX = 36  # Key Exchange
    NS_T_CERT = 37  # Certification record
    NS_T_A6 = 38  # IPv6 address (deprecated, use NS_T_AAAA)
    NS_T_DNAME = 39  # Non-terminal DNAME (for IPv6)
    NS_T_SINK = 40  # Kitchen sink (experimentatl)
    NS_T_OPT = 41  # EDNS0 option (meta-RR)
    NS_T_APL = 42  # Address prefix list (RFC3123)
    NS_T_TKEY = 249  # Transaction key
    NS_T_TSIG = 250  # Transaction signature
    NS_T_IXFR = 251  # Incremental zone transfer
    NS_T_AXFR = 252  # Transfer zone of authority
    NS_T_MAILB = 253  # Transfer mailbox records
    NS_T_MAILA = 254  # Transfer mail agent records
    NS_T_ANY = 255  # Wildcard match
    NS_T_ZXFR = 256  # BIND-specific, nonstandard
    NS_T_MAX = 65536

    def __init__(self, hub=None, **kwargs):
        if hub is None:
            hub = get_hub()
        self.hub = hub

        self.ares = self.ares_class(hub.loop, **kwargs)
        self.pid = os.getpid()
        self.params = kwargs
        self.fork_watcher = hub.loop.fork(ref=False)
        self.fork_watcher.start(self._on_fork)

    def __repr__(self):
        return (
            '<minemeld.packages.gdns.dig.Dig at 0x%x ares=%r>' %
            (id(self), self.ares)
        )

    def _on_fork(self):
        pid = os.getpid()
        if pid != self.pid:
            self.hub.loop.run_callback(self.ares.destroy)
            self.ares = self.ares_class(self.hub.loop, **self.params)
            self.pid = pid

    def close(self):
        if self.ares is not None:
            self.hub.loop.run_callback(self.ares.destroy)
            self.ares = None
        self.fork_watcher.stop()

    def query(self, name, dnsclass, type_):
        if isinstance(name, unicode):
            name = name.encode('ascii')
        elif not isinstance(name, str):
            raise TypeError('Expected string, not %s' % type(name).__name__)

        while True:
            ares = self.ares
            try:
                waiter = Waiter(self.hub)
                ares.query(waiter, name, dnsclass, type_)
                result = waiter.get()

                return result

            except gaierror:
                if ares is self.ares:
                    raise

    def parse_txt_reply(self, reply):
        return self.ares.parse_txt_reply(reply)