# -*- coding: utf8 -*-
import struct


# (List<Double>, Uint32, Uint32) -> List<Double>
def slice_list(xs, start, step):
    return list(xs)[start::step]


# (String?) -> String
def normalize_txt(text):
    if not text:
        return ""
    text = text.decode("utf-8").lower().split()
    return " ".join(sorted({w.strip() for w in text if w}))


def idx_max_of(xs, default=None):
    u"""
    ([String?]) -> Int32?
    Возвращаем индекс максимального элемента (> 0) в массиве xs
    """
    try:
        xs = [float(x.strip()) if x else None for x in xs]
    except ValueError:
        return default
    if xs:
        max_val = max(xs)
        return xs.index(max_val) if max_val > 0 else default
    else:
        return default


def parse_dsv(s, val_sep, kv_sep):
    """
    (String?, String, String) -> Dict

    >>> parse_dsv("desktop=false&isMobile=true", "&", "=")
    {'desktop': 'false', 'isMobile': 'true'}
    """
    if not s:
        return dict()

    s = s.split(val_sep)
    s = [x.split(kv_sep, 1) for x in s if x]

    return dict([x for x in s if len(x) == 2])


def decode_regular_coord(encoded_points):
    """
    (String?) -> List<Double>
    Расшифровывает регулярные координаты пользователя, сохраненные как
      бинарные данные в кодировке base64.


    Note:
    Описание формата https://wiki.yandex-team.ru/Awaps/SocialDemo/#formatxranenijaprofiljavbazedannyx
    В июне 2016 считалось, что координата считается регулярной, если
      пользователь накопил 4 часа за 44 дня в этой точке (радиус?).

    user_regular_location - регулярные геокоординаты, формат:
        {{4 байта: Lat * 1E7 - широта точки (целое число)}{4 байта: Lon * 1E7 - долгота точки (целое число)}}

    Числа в big-endian (начиная со старшего байта)

    Example:
    >>> decode_regular_coord("H7GLshFmotYfs9i4EWpaDA==")
    [53.172933, 29.193903, 53.188012, 29.218254]

    >>> decode_regular_coord("IY4YiBooMVo=")
    [56.296052, 43.884169]

    >>> decode_regular_coord(None), decode_regular_coord("")
    []
    """

    if not encoded_points:
        return []

    try:
        decoded = encoded_points.decode("base64")
        lat_lon = map(lambda x: x/1e7, struct.unpack(">%si" % (len(decoded) / 4), decoded))
        return lat_lon
    except:
        return []


def decode_search_cats(encoded_cats):
    """decode_search_cats
    (String?)->List<Int64>
    Расшифровывает мкб-категории, сохраненные как бинарные данные 
      в кодировке base64.


    Note:
    Описание формата https://wiki.yandex-team.ru/Awaps/SocialDemo/#formatxranenijaprofiljavbazedannyx
    Справочник категорий https://aw-admin.yandex-team.ru/administrator/mcb_category/index.jsp

    user_search_cats - поисковые (МКБ?) категории, формат:
        {4 байта: номер категории (целое число)}...;

    Числа в big-endian (начиная со старшего байта).

    Example:
    >>> decode_search_cats("BfXhbwX14XIF9eF2BfXhjgX14Y8F9eGVBfXhqQX14jcF9eI8BfXiQQX14mwF9eKk"))
    [100000111, 100000114, 100000118, 100000142, 100000143, 100000149, 100000169, 100000311, 100000316, 100000321, 100000364, 100000420]
    """

    if not encoded_cats:
        return []
    try:
        decoded = encoded_cats.decode("base64")
        return sorted(struct.unpack(">%si" % (len(decoded) / 4), decoded))
    except:
        return [-1, ]


def decode_adhoc_prob(encoded_adhoc_prob):
    """
    (String?)->List<Int64>
    Расшифровывает вероятностные сегменты, сохраненные как бинарные
      данные в кодировке base64.


    Note:
    Описание формата
      https://wiki.yandex-team.ru/Awaps/SocialDemo/#formatxranenijaprofiljavbazedannyx
    Справочник коммерческих сегментов
      https://wiki.yandex-team.ru/crypta/crypta-da/segments/segments/
      keyword 217 - вероятностные model_id; про остальные keyword
        https://wiki.yandex-team.ru/Crypta/OutputToBB/#formatstrokivnutripachki

    Равнозначные названия - model_id, <id сегментной категории>, <id категории>.

    user_adhoc_prob - вероятностные сегменты крипты, формат:
        {первый байт: количество model_id},
        {{2 байта: model_id}{1 байт: число сегментов в model_id}}^n, где n - количество model_id
        {{1 байт: вероятность (0-100) для i-го сегмента в model_id}^m}^n, где n - количество model_id, m - количество сегментов в model_id;

    Числа в big-endian (начиная со старшего байта).

    Вероятности сегментов и сами <сегментные индексы> - внутренняя кухня Крипты.
      Для подавляющего большинства сегментов/model_id будет одно значение
      вероятности. В логи крипты и логи авапса попадают только значения
      вероятностей, которые оказались выше порога. Поэтому если для этого yuid'а
      указан какой-то сегмент и соответствующая вероятность, то мы отнесли этого
      пользователя к этому сегменту.

    В справочнике выше могут быть не все сегменты. Например, 193, 316 и 343 это
      наши внутренние маркетинговые сегменты и статистика по ним для
      медиапланирования не нужна.
    """
    if not encoded_adhoc_prob:
        return []
    try:
        decoded = encoded_adhoc_prob.decode("base64")

        # {1 байт: число категорий}
        cat_count = struct.unpack(">B", decoded[0])[0]

        # [{2 байта: номер категории/model_id/id сегментной категории}{1 байт: число сегментов в категории}]
        cats_id_cnt = struct.unpack(">" + "HB" * cat_count, decoded[1:cat_count * 3 + 1]) # 2 байта + 1 байт = 3 байта
    except:
        return [-1, ]

    return sorted(cats_id_cnt[::2])


def decode_adhoc_strict(encoded_cat_strict):
    """
    (String?) -> List<Int64>
    Расшифровывает строгие сегменты, сохраненные как бинарные данные в кодировке base64.


    Note:
    Описание формата https://wiki.yandex-team.ru/Awaps/SocialDemo/#formatxranenijaprofiljavbazedannyx
    Справочник коммерческих сегментов https://wiki.yandex-team.ru/crypta/crypta-da/segments/segments/
      keyword 216 - строгие model_id; про остальные keyword https://wiki.yandex-team.ru/Crypta/OutputToBB/#formatstrokivnutripachki

    Равнозначные названия - model_id, <id сегментной категории>, <id категории>.

    user_adhoc_strict - строгие сегменты крипты, формат:
        {{2 байта: model_id}{1 байт: номер сегмента}};

    Числа в big-endian (начиная со старшего байта).

    В adhocs_strict попадают эвристические ("строгие") сегменты.
    У них нет значений веротностей, а есть только их наличие или отсутствие.
    В логах крипты (0 - нет, 1 - есть, в логи попадают только сегменты с "единичками"),
    в логах авапса (1 - нет, 2 - есть, в логи соответственно должны попадать
    только сегменты с "двоечками").
    На данный момент (2016-09-13) логика для эвристических сегментов такая.
    """
    if not encoded_cat_strict:
        return []
    try:
        decoded = encoded_cat_strict.decode("base64")
        cats_id_segm_id = struct.unpack(">" + (len(decoded) / 3) * "HB", decoded)
    except:
        return [-1, ]

    return sorted(cats_id_segm_id[::2])


def decode_goals(encoded_goals):
    """
    (String?) -> List<Int64>
    Расшифровывает номера достигнутых целей, сохраненные как бинарные данные в кодировке base64.


    Note:
    Описание формата https://wiki.yandex-team.ru/Awaps/SocialDemo/#formatxranenijaprofiljavbazedannyx
    Справочник для поведенческого ретаргетинга https://wiki.yandex-team.ru/Sales/supportofcommercialservices/support/display/tematarget/

    user_goals - цели Метрики для ретаргетинга:, формат:
        { {4 байта: номер цели}{2 байта: время в часах с достижения цели до записи в лог} };

    Числа в big-endian (начиная со старшего байта).

    Example:
    >>> decode_goals("ABs8fwVzAHo9jwg8AV+oYAAO")
    [1784959, 8011151, 23046240]

    >>> decode_goals(""), decode_goals(None)
    ([], [])
    """
    if not encoded_goals:
        return []
    try:
        decoded = encoded_goals.decode("base64")
        # 6 = 4 байта + 2 байта
        goals_id_hours = struct.unpack(">" + (len(decoded) / 6) * "IH", decoded)
    except:
        return [-1, ]

    return sorted(goals_id_hours[::2])
