#!/usr/bin/env python
# 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
#
# Authors:
# - Paul Nilsson, paul.nilsson@cern.ch, 2020

import re
import json
import numbers
import traceback
import threading

from pilot.util.auxiliary import is_python3

import logging
logger = logging.getLogger(__name__)


def camel_to_snake(name):
    """
    Changes CamelCase to snake_case, used by python.

    :param name: name to change
    :return: name in snake_case
    """
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()


def snake_to_camel(snake_str):
    """
    Changes snake_case to firstLowCamelCase, used by server.

    :param snake_str: name to change
    :return: name in camelCase
    """
    components = snake_str.split('_')
    # We capitalize the first letter of each component except the first one
    # with the 'title' method and join them together.
    return components[0] + "".join(x.title() for x in components[1:])


def split(val, separator=",", min_len=0, fill_last=False):
    """
    Splits comma separated values and parses them.

    :param val:         values to split
    :param separator:   comma or whatever
    :param min_len:     minimum needed length of array, array is filled up to this value
    :param fill_last:   Flag stating the array filler, if min_value is greater then extracted array length.
                        If true, array is filled with last value, else, with Nones.
    :return: parsed array
    """
    if val is None:
        return [None for _ in range(min_len)]

    v_arr = val.split(separator)

    for i, v in enumerate(v_arr):
        v_arr[i] = parse_value(v)

    if min_len > len(v_arr):
        filler = None if not fill_last or len(v_arr) < 1 else v_arr[0]
        v_arr.extend([filler for _ in range(min_len - len(v_arr))])

    return v_arr


def get_nulls(val):
    """
    Converts every "NULL" string to python's None.

    :param val: string or whatever
    :return: val or None if val is "NULL"
    """
    return val if val != "NULL" else None


def is_float(val):
    """
    Test floatliness of the string value.

    :param val: string or whatever
    :return: True if the value may be converted to Float
    """
    try:
        float(val)
        return True
    except ValueError:
        return False


def is_int(val):
    """
    Test int of the string value.

    :param val: string or whatever
    :return: True if the value may be converted to int
    """
    try:
        int(val)
        return True
    except ValueError:
        return False


def is_long(s):
    """
    Test value to be convertable to integer.

    :param s: string or whatever
    :return: True if the value may be converted to Long
    """

    try:
        if not isinstance(s, basestring):  # Python 2 # noqa: F821
            try:
                long(s)  # noqa: F821
                return True
            except ValueError:
                return False
    except Exception:
        return False  # Python 3 - this function should not be used on Python 3

    if s and s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()


def parse_value(value):
    """
    Tries to parse value as number or None. If some of this can be done, parsed value is returned. Otherwise returns
    value unparsed.

    :param value:
    :return: mixed
    """

    try:
        if not isinstance(value, basestring):  # Python 2 # noqa: F821
            return value
    except Exception:
        if not isinstance(value, str):  # Python 3
            return value

    if is_python3():  # Python 3
        if is_int(value):  # Python 3
            return int(value)
    else:
        if is_long(value):  # Python 2
            return long(value)  # noqa: F821
    if is_float(value):
        return float(value)

    return get_nulls(value)


def stringify_weird(arg):
    """