# 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 gzip
import http.client
import logging
import os
import socket
import time
import xmlrpc.client

from freenasUI.common.system import get_sw_version

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


class ServerProxy(xmlrpc.client.ServerProxy):

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


class HTTPTimeout(http.client.HTTPConnection):

    def __init__(self, *args, **kwargs):
        self._conntimeout = kwargs.pop('conntimeout', None)
        self._timeout = kwargs.get('timeout', None)
        http.client.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
        http.client.HTTPConnection.connect(self)
        if self._conntimeout:
            self.timeout = self._timeout
            self.sock.settimeout(
                float(self._timeout) if self._timeout else None
            )

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


class TimeoutTransport(xmlrpc.client.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)
        xmlrpc.client.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, host, handler, request_body, debug):
        connection = self.make_connection(host)
        headers = self._extra_headers[:]
        if debug:
            connection.set_debuglevel(1)
        if self.accept_gzip_encoding and gzip:
            connection.putrequest("POST", handler, skip_accept_encoding=True)
            headers.append(("Accept-Encoding", "gzip"))
        else:
            connection.putrequest("POST", handler)
        connection.putheader('Version', VERSION)
        headers.append(("Content-Type", "text/xml"))
        headers.append(("User-Agent", self.user_agent))
        self.send_headers(connection, headers)
        self.send_content(connection, request_body)
        return connection


def node_label_field(node, field_a, field_b):

    if node == 'A':
        field_a.label = "%s (This Controller)" % field_a.label
        field_b.label = "%s (TrueNAS Controller 2)" % field_b.label
    elif node == 'B':
        field_a.label = "%s (TrueNAS Controller 1)" % field_a.label
        field_b.label = "%s (This Controller)" % field_b.label
