"""
Copyright (c) 2017 SONATA-NFV and Paderborn University
ALL RIGHTS RESERVED.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Neither the name of the SONATA-NFV, Paderborn University
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.

This work has been performed in the framework of the SONATA project,
funded by the European Commission under Grant number 671517 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.sonata-nfv.eu).
"""
from resources.net import Net
import threading

lock = threading.Lock()

__issued_ips = dict()
__default_subnet_size = 256
__default_subnet_bitmask = 24
__first_ip = Net.ip_2_int('10.0.0.0')
__last_ip = Net.ip_2_int('10.255.255.255')
__current_ip = __first_ip


def get_new_cidr(uuid):
    """
    Calculates a unused cidr for a subnet.

    :param uuid: The UUID of the subnet - Thus it can store which subnet gets which CIDR
    :type uuid: ``str``
    :return: Returns None if all available CIDR are used. Otherwise returns a valid CIDR.
    :rtype: ``str``
    """
    global lock
    lock.acquire()

    global __current_ip
    while __first_ip <= __current_ip < __last_ip and __current_ip in __issued_ips:
        __current_ip += __default_subnet_size

    if __current_ip >= __last_ip or __current_ip < __first_ip or __current_ip in __issued_ips:
        return None

    __issued_ips[__current_ip] = uuid
    lock.release()

    return Net.int_2_ip(__current_ip) + '/' + str(__default_subnet_bitmask)


def free_cidr(cidr, uuid):
    """
    Frees a issued CIDR thus it can be reused.

    :param cidr: The currently used CIDR.
    :type cidr: ``str``
    :param uuid: The UUID of the Subnet, which uses this CIDR.
    :type uuid: ``str``
    :return: Returns False if the CIDR is None or the UUID did not correspond tho the used CIDR. Else it returns True.
    :rtype: ``bool``
    """
    if cidr is None:
        return False

    global __current_ip
    int_ip = Net.cidr_2_int(cidr)

    global lock
    lock.acquire()

    if int_ip in __issued_ips and __issued_ips[int_ip] == uuid:
        del __issued_ips[int_ip]
        if int_ip < __current_ip:
            __current_ip = int_ip
        lock.release()
        return True
    lock.release()
    return False


def is_cidr_issued(cidr):
    """
    Returns True if the CIDR is used.

    :param cidr: The requested CIDR.
    :type cidr: ``str``
    :return: Returns True if the CIDR is used, else False.
    :rtype: ``bool``
    """
    if cidr is None:
        return False

    int_ip = Net.cidr_2_int(cidr)

    if int_ip in __issued_ips:
        return True
    return False


def is_my_cidr(cidr, uuid):
    """
    Checks if the UUID and the used CIDR are related.

    :param cidr: The issued CIDR.
    :type cidr: ``str``
    :param uuid: The Subnet UUID.
    :type uuid: ``str``
    :return: Returns False if the CIDR is None or if the CIDR is not issued. Else returns True.
    :rtype: ``bool``
    """
    if cidr is None:
        return False

    int_ip = Net.cidr_2_int(cidr)

    if not int_ip in __issued_ips:
        return False

    if __issued_ips[int_ip] == uuid:
        return True
    return False


def assign_cidr(cidr, uuid):
    """
    Allows a subnet to request a specific CIDR.

    :param cidr: The requested CIDR.
    :type cidr: ``str``
    :param uuid: The Subnet UUID.
    :type uuid: ``str``
    :return: Returns False if the CIDR is None or if the CIDR is already issued. Returns True if the CIDR could be
     assigned to the UUID.
    """
    if cidr is None:
        return False

    int_ip = Net.cidr_2_int(cidr)

    if int_ip in __issued_ips:
        return False

    global lock
    lock.acquire()
    __issued_ips[int_ip] = uuid
    lock.release()
    return True
