from __future__ import absolute_import, with_statement

import socket

from ..emulation import patched

exception = patched("dns.exception")
rdatatype = patched("dns.rdatatype")
resolver = patched("dns.resolver")
reversename = patched("dns.reversename")

DNS_CACHE_SIZE = 512


class LRU(object):
    def __init__(self, size):
        self._keyed = {}
        self._head = None
        self.size = size

    def _set_item_head(self, item):
        head = self._head
        self._head = item
        if head:
            item.prev = head.prev or head
            item.prev.next = item
            item.next = head
            head.prev = item
        else:
            item.prev = item.next = None

    def _remove_item(self, item, from_keyed=True):
        if item.prev:
            item.prev.next = item.next
        if item.next:
            item.next.prev = item.prev
        if from_keyed:
            self._keyed.pop(item.key)

    def _purge(self):
        while len(self._keyed) > self.size:
            item = self._head.prev
            self._remove_item(item)

    def __contains__(self, key):
        return key in self._keyed

    def __setitem__(self, key, value):
        old_item = self._keyed.get(key, None)
        if old_item:
            self._remove_item(old_item)

        item = LRUItem(key, value)
        self._set_item_head(item)
        self._keyed[key] = item
        self._purge()

    def __getitem__(self, key):
        item = self._keyed[key]
        self._remove_item(item, from_keyed=False)
        self._set_item_head(item)
        return item.value

    def get(self, key, default=None):
        if key not in self._keyed:
            return default
        return self[key]

    def __delitem__(self, key):
        item = self._keyed.get(item)

        if item is None:
            raise KeyError(key)

        self._remove_item(item)

    def __repr__(self):
        if not self._keyed:
            return "<>"

        return "<%s>" % ", ".join(str(key) for key in self.iterkeys())

    def iterkeys(self):
        return (key for key, value in self.iteritems())

    def itervalues(self):
        return (value for key, value in self.iteritems())

    def iteritems(self):
        memo, item = set(), self._head
        if item is None:
            return

        while item is not None and item not in memo:
            memo.add(item)
            yield (item.key, item.value)
            item = item.next

    def keys(self):
        return list(self.iterkeys())

    def values(self):
        return list(self.itervalues())

    def items(self):
        return list(self.iteritems())


class LRUItem(object):
    __slots__ = ["key", "value", "next", "prev"]

    def __init__(self, key, value):
        self.key = key
        self.value = value


etc_hosts_loaded = False
etc_hosts = {}
cache = LRU(DNS_CACHE_SIZE)
resolver_obj = None

def build_resolver(filename="/etc/resolv.conf"):
    if resolver_obj is None:
        globals()['resolver_obj'] = resolver.Resolver(filename)

def load_etc_hosts(filename="/etc/hosts"):
    with open(filename) as fp:
        for line in fp:
            try:
                if (c for c in line if not c.isspace()).next() == '#':
                    continue
            except StopIteration:
                continue

            parts = line.split()
            if '::' in parts[0]:
                # not handling ipv6 yet
                continue
            for part in parts[1:]:
                etc_hosts.setdefault(part, []).append(parts[0])
    globals()['etc_hosts_loaded'] = True

def is_ipv4(name):
    try:
        socket.inet_pton(socket.AF_INET, name)
    except socket.error:
        return False
    return True

def resolve(name):
    build_resolver()

    if is_ipv4(name):
        return [name]

    if not etc_hosts_loaded:
        load_etc_hosts()

    # if it's in /etc/hosts then use that
    if name in etc_hosts:
        return etc_hosts[name][:]

    # return it out of the cache if it's there
    if name in cache:
        return cache[name][:]

    results = [x.to_text() for x in resolver_obj.query(name)]
    cache[name] = results[:]
    return results

@@ -406,29 +406,24 @@
    def _set_
-item_
 head(self, i
@@ -1128,16 +1128,186 @@
 _keyed%0A%0A
+    def __getitem__(self, key):%0A        item = self._keyed%5Bkey%5D%0A        self._remove_item(item, from_keyed=False)%0A        self._set_head(item)%0A        return item.value%0A%0A
     def 
@@ -1490,37 +1490,32 @@
       self._set_
-item_
 head(item)%0A     
@@ -1568,1226 +1568,8 @@
 ()%0A%0A
-    def __getitem__(self, key):%0A        item = self._keyed%5Bkey%5D%0A        self._remove_item(item, from_keyed=False)%0A        self._set_item_head(item)%0A        return item.value%0A%0A    def get(self, key, default=None):%0A        if key not in self._keyed:%0A            return default%0A        return self%5Bkey%5D%0A%0A    def __delitem__(self, key):%0A        item = self._keyed.get(item)%0A%0A        if item is None:%0A            raise KeyError(key)%0A%0A        self._remove_item(item)%0A%0A    def __repr__(self):%0A        if not self._keyed:%0A            return %22%3C%3E%22%0A%0A        return %22%3C%25s%3E%22 %25 %22, %22.join(str(key) for key in self.iterkeys())%0A%0A    def iterkeys(self):%0A        return (key for key, value in self.iteritems())%0A%0A    def itervalues(self):%0A        return (value for key, value in self.iteritems())%0A%0A    def iteritems(self):%0A        memo, item = set(), self._head%0A        if item is None:%0A            return%0A%0A        while item is not None and item not in memo:%0A            memo.add(item)%0A            yield (item.key, item.value)%0A            item = item.next%0A%0A    def keys(self):%0A        return list(self.iterkeys())%0A%0A    def values(self):%0A        return list(self.itervalues())%0A%0A    def items(self):%0A        return list(self.iteritems())%0A%0A%0A
 clas

trim this LRU class down to just exactly what we need