# -*- coding: utf-8 -*-

import binascii
import random
import string

__author__ = 'Giacomo Tanganelli'


def str_append_hash(*args):
    """ Convert each argument to a lower case string, appended, then hash """
    ret_hash = ""
    for i in args:
        if isinstance(i, (str, int)):
            ret_hash += str(i).lower()
        elif isinstance(i, bytes):
            ret_hash += binascii.hexlify(i).decode("utf-8")

    return hash(ret_hash)


def check_nocachekey(option):
    """
    checks if an option is a NoCacheKey option or Etag

    :param option:
    :return:
    """
    return ((option.number & 0x1E) == 0x1C) | (option.number == 4)


def check_code(code):
    """
    checks if the response code is one of the valid ones defined in the rfc

    :param code:
    :return:
    """
    if (65 <= code <= 69) or (128 <= code <= 134) or (code == 140) or (code == 141) or (code == 143) or (
            160 <= code <= 165):
        return

    else:
        raise InvalidResponseCode

"""
exception used to signal an invalid response code
"""


class InvalidResponseCode:
    def __init__(self, code):
        self.inv_code = code


def is_uri_option(number):
    """
    checks if the option is part of uri-path, uri-host, uri-port, uri-query

    :param number:
    :return:
    """
    if number == 3 | number == 7 | number == 11 | number == 15:
        return True
    return False


def generate_random_token(size):
    return bytes([random.randint(0, 255) for _ in range(size)])


def parse_blockwise(value):
    """
    Parse Blockwise option.

    :param value: option value
    :return: num, m, size
    """

    length = byte_len(value)
    if length == 1:
        num = value & 0xF0
        num >>= 4
        m = value & 0x08
        m >>= 3
        size = value & 0x07
    elif length == 2:
        num = value & 0xFFF0
        num >>= 4
        m = value & 0x0008
        m >>= 3
        size = value & 0x0007
    else:
        num = value & 0xFFFFF0
        num >>= 4
        m = value & 0x000008
        m >>= 3
        size = value & 0x000007
    return num, int(m), pow(2, (size + 4))


def byte_len(int_type):
    """
    Get the number of byte needed to encode the int passed.

    :param int_type: the int to be converted
    :return: the number of bits needed to encode the int passed.
    """
    length = 0
    while int_type:
        int_type >>= 1
        length += 1
    if length > 0:
        if length % 8 != 0:
            length = int(length / 8) + 1
        else:
            length = int(length / 8)
    return length


def parse_uri(uri):
    t = uri.split("://")
    tmp = t[1]
    t = tmp.split("/", 1)
    tmp = t[0]
    path = t[1]
    if tmp.startswith("["):
        t = tmp.split("]")
        host = t[0][1:]
        port = int(t[1][1:])
    else:
        t = tmp.split(":", 1)
        try:
            host = t[0]
            port = int(t[1])
        except IndexError:
            host = tmp
            port = 5683

    return str(host), port, path


def create_logging():  # pragma: no cover
    with open("logging.conf", "w") as f:
        f.writelines("[loggers]\n")
        f.writelines("keys=root\n\n")
        f.writelines("[handlers]\n")
        f.writelines("keys=consoleHandler\n\n")
        f.writelines("[formatters]\n")
        f.writelines("keys=simpleFormatter\n\n")
        f.writelines("[logger_root]\n")
        f.writelines("level=DEBUG\n")
        f.writelines("handlers=consoleHandler\n\n")
        f.writelines("[handler_consoleHandler]\n")
        f.writelines("class=StreamHandler\n")
        f.writelines("level=DEBUG\n")
        f.writelines("formatter=simpleFormatter\n")
        f.writelines("args=(sys.stdout,)\n\n")
        f.writelines("[formatter_simpleFormatter]\n")
        f.writelines("format=%(asctime)s - %(threadName)-10s - %(name)s - %(levelname)s - %(message)s\n")
        f.writelines("datefmt=")


class Tree(object):
    def __init__(self):
        self.tree = {}

    def dump(self):
        """
        Get all the paths registered in the server.

        :return: registered resources.
        """
        return sorted(list(self.tree.keys()))

    def with_prefix(self, path):
        ret = []
        for key in list(self.tree.keys()):
            if path.startswith(key):
                ret.append(key)

        if len(ret) > 0:
            return ret
        raise KeyError

    def with_prefix_resource(self, path):
        ret = []
        for key, value in self.tree.items():
            if path.startswith(key):
                ret.append(value)

        if len(ret) > 0:
            return ret
        raise KeyError

    def __getitem__(self, item):
        return self.tree[item]

    def __setitem__(self, key, value):
        self.tree[key] = value

    def __delitem__(self, key):
        del self.tree[key]

    def __contains__(self, item):
        return item in self.tree
