# Copyright (c) 2015 iXsystems, Inc.
# All rights reserved.
# This file is a part of TrueNAS
# and may not be copied and/or distributed
# without the express permission of iXsystems.

import ctypes
import httplib
import logging
import os
import socket
import time
import xmlrpclib

from freenasUI.common.system import get_sw_version

PAIRING_FILE = '/tmp/.pairing'
VERSION = get_sw_version()
log = logging.getLogger('truenas.utils')


class ServerProxy(xmlrpclib.ServerProxy):

    def __del__(self):
        try:
            self.__close()
        except:
            pass


class HTTPTimeout(httplib.HTTPConnection):

    def __init__(self, *args, **kwargs):
        self._conntimeout = kwargs.pop('conntimeout', None)
        self._timeout = kwargs.get('timeout', None)
        httplib.HTTPConnection.__init__(self, *args, **kwargs)
        self._create_connection = self._create_conn
        self._fd = None

    def _create_conn(self, address, timeout, source_address):
        libc = ctypes.cdll.LoadLibrary('libc.so.7')
        port = ctypes.c_int(0)
        pport = ctypes.pointer(port)
        # 860 is the iSCSI port and blocked by the failover script
        self._fd = libc.rresvport(pport)
        retries = 5
        while True:
            if retries == 0:
                break
            if self._fd < 0:
                time.sleep(0.1)
                self._fd = libc.rresvport(pport)
                retries -= 1
                continue
            if pport.contents.value == 860:
                oldfd = self._fd
                self._fd = libc.rresvport(pport)
                os.close(oldfd)
                retries -= 1
                continue
            else:
                break
        sock = socket.fromfd(self._fd, socket.AF_INET, socket.SOCK_STREAM)
        if timeout:
            sock.settimeout(timeout)
        sock.connect(address)
        return sock

    def connect(self):
        if self._conntimeout:
            self.timeout = self._conntimeout
        httplib.HTTPConnection.connect(self)
        if self._conntimeout:
            self.timeout = self._timeout
            self.sock.settimeout(
                float(self._timeout) if self._timeout else None
            )

    def close(self):
        httplib.HTTPConnection.close(self)
        if self._fd:
            try:
                os.close(self._fd)
            except OSError:
                pass
        self._fd = None


class TimeoutTransport(xmlrpclib.Transport):
    """
    Custom XML-RPC transport class for HTTP connections, allowing a timeout in
    the base connection.
    """

    def __init__(self, *args, **kwargs):
        self._timeout = kwargs.pop('timeout', None)
        self._conntimeout = kwargs.pop('conntimeout', None)
        xmlrpclib.Transport.__init__(self, *args, **kwargs)

    def make_connection(self, host):
        # return an existing connection if possible.  This allows
        # HTTP/1.1 keep-alive.
        if self._connection and host == self._connection[0]:
            return self._connection[1]

        # create a HTTP connection object from a host descriptor
        chost, self._extra_headers, x509 = self.get_host_info(host)
        # store the host argument along with the connection object
        self._connection = host, HTTPTimeout(
            chost,
            conntimeout=self._conntimeout,
            timeout=self._timeout,
        )
        return self._connection[1]

    def send_request(self, connection, handler, request_body):
        xmlrpclib.Transport.send_request(
            self, connection, handler, request_body
        )
        connection.putheader('Version', VERSION)


def node_label_field(node, field_a, field_b):

    if node == 'A':
        field_a.label = u"%s (This Node)" % field_a.label
        field_b.label = u"%s (Node B)" % field_b.label
    elif node == 'B':
        field_a.label = u"%s (Node A)" % field_a.label
        field_b.label = u"%s (This Node)" % field_b.label


def failover_disabled_reasons():
    from django.db.models import Q
    from freenasUI.failover.models import Failover
    from freenasUI.middleware.notifier import notifier
    from freenasUI.network.models import Interfaces
    from freenasUI.storage.models import Volume
    _n = notifier()
    reasons = []
    if not Volume.objects.all().exists():
        reasons.append('NO_VOLUME')
    if not Interfaces.objects.exclude(Q(int_vip=None) | Q(int_vip='')).exists():
        reasons.append('NO_VIP')
    try:
        assert _n.failover_rpc().ping() == 'pong'
    except:
        reasons.append('NO_PONG')
    if Failover.objects.filter(disabled=True).exists():
        reasons.append('NO_FAILOVER')
    return reasons
