"""
Network Manager for Linux
"""

import ctypes
import socket

from .base import BaseManager

# select constants
IFNAMSIZ = 16
SIOCGIFCONF = 0x8912
SIOCGIFFLAGS = 0x8913
SIOCGIFADDR = 0x8915
SIOCGIFHWADDR = 0x8927
IFF_LOOPBACK = 0x8

sa_family_t = ctypes.c_ushort


class sockaddr(ctypes.Structure):
    _fields_ = (('family', sa_family_t), ('data', ctypes.c_byte * 14))


class ifr_map(ctypes.Structure):
    _fields_ = (
        ('mem_start', ctypes.c_ulong),
        ('mem_end', ctypes.c_ulong),
        ('base_addr', ctypes.c_ushort),
        ('irq', ctypes.c_char),
        ('dma', ctypes.c_char),
        ('port', ctypes.c_char),
    )


class ifreq_union(ctypes.Union):
    _fields_ = (
        ('addr', sockaddr),
        ('dest_addr', sockaddr),
        ('broadcast_addr', sockaddr),
        ('netmask', sockaddr),
        ('hardware_addr', sockaddr),
        ('flags', ctypes.c_ushort),
        ('if_index', ctypes.c_int),
        ('metric', ctypes.c_int),
        ('mtu', ctypes.c_int),
        ('map', ifr_map),
        ('slave', ctypes.c_char * IFNAMSIZ),
        ('new_name', ctypes.c_char * IFNAMSIZ),
        ('data', ctypes.c_char_p),
    )


class ifreq(ctypes.Structure):
    _fields_ = (('name', ctypes.c_char * IFNAMSIZ), ('detail', ifreq_union))


p_ifreq = ctypes.POINTER(ifreq)


class ifconf(ctypes.Structure):
    _fields_ = (('length', ctypes.c_int), ('req', p_ifreq))


libc = ctypes.cdll.LoadLibrary('libc.so.6')
ioctl = libc.ioctl


def get_interfaces():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    buf = ctypes.create_string_buffer(1024)
    ifc = ifconf()
    ifc.length = 1024
    ifc.req = ctypes.cast(buf, p_ifreq)
    ioctl(s.fileno(), SIOCGIFCONF, ctypes.byref(ifc))
    n_records = ifc.length / ctypes.sizeof(ifreq)
    for index in range(n_records):
        rec = ifc.req[index]
        yield str(rec.name)


def get_hardware_addresses(if_names):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for name in if_names:
        ifr = ifreq(name)
        res = ioctl(s.fileno(), SIOCGIFFLAGS, ctypes.byref(ifr))
        if res != 0:
            continue
        if ifr.detail.flags & IFF_LOOPBACK:
            continue
        res = ioctl(s.fileno(), SIOCGIFHWADDR, ctypes.byref(ifr))
        if res != 0:
            continue
        yield memoryview(ifr.detail.hardware_addr.data).tobytes()[:6]


def get_ip_addresses(if_names):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for name in if_names:
        ifr = ifreq(name)
        res = ioctl(s.fileno(), SIOCGIFADDR, ctypes.byref(ifr))
        if res != 0:
            continue
        yield memoryview(ifr.detail.addr.data).tobytes()[2:6]


class Manager(BaseManager):
    def get_host_mac_addresses(self):
        return get_hardware_addresses(get_interfaces())

    def get_host_ip_addresses(self):
        return get_ip_addresses(get_interfaces())
