# -*- coding: utf-8 -*-
import logging
from ConfigParser import ConfigParser
from contextlib import contextmanager
import time
import re
import io
from base64 import b64decode


def parse_duration(duration):
    """
    Stolen from yandex-tank
    Parse duration string, such as '3h2m3s' into seconds
    >>> parse_duration('3h2m3s')
    10923
    >>> parse_duration('5')
    5
    """
    _re_token = re.compile('([-0-9.]+)([dhms]?)')

    def parse_token(dur, multiplier):
        multipliers = {
            'd': 86400,
            'h': 3600,
            'm': 60,
            's': 1,
        }
        if multiplier:
            return int(float(dur) * multipliers.get(multiplier, 1))
        else:
            return int(float(dur))

    return sum(parse_token(*token) for token in _re_token.findall(duration))


def coolify(id):
    id = str(id)
    if len(id) <= 4:
        return id
    decimal_part = id[-4:]
    icao_dec = int(id[:-4])
    icao_part = ''
    while icao_dec > 0:
        icao_dec, digit = divmod(icao_dec, 26)
        icao_part += chr(ord('A') + digit)
    return '%s-%s' % (icao_part[::-1], decimal_part)


def coolify_even_more(id):
    id = str(id)
    if len(id) <= 4:
        return id
    cool_id = coolify(id)
    dictionary = {
        'A': 'Alpha', 'B': 'Bravo', 'C': 'Charlie', 'D': 'Delta', 'E': 'Echo', 'F': 'Foxtrot', 'G': 'Golf',
        'H': 'Hotel', 'I': 'India', 'J': 'Juliet', 'K': 'Kilo', 'L': 'Lima', 'M': 'Mike', 'N': 'November', 'O': 'Oscar',
        'P': 'Papa', 'Q': 'Quebec', 'R': 'Romeo', 'S': 'Sierra', 'T': 'Tango', 'U': 'Uniform', 'V': 'Victor',
        'W': 'Whiskey', 'X': 'Xray', 'Y': 'Yankee', 'Z': 'Zulu'
    }
    alpha_part, digital_part = cool_id.split('-')
    cooler_id = ''
    for a in alpha_part:
        cooler_id += dictionary[a] + '-'
    cooler_id += digital_part
    return cooler_id


def uncoolify(cool_id):
    cool_id = str(cool_id)
    if len(cool_id) <= 4:
        return cool_id
    dictionary = {
        'A': 'Alpha', 'B': 'Bravo', 'C': 'Charlie', 'D': 'Delta', 'E': 'Echo', 'F': 'Foxtrot', 'G': 'Golf',
        'H': 'Hotel', 'I': 'India', 'J': 'Juliet', 'K': 'Kilo', 'L': 'Lima', 'M': 'Mike', 'N': 'November', 'O': 'Oscar',
        'P': 'Papa', 'Q': 'Quebec', 'R': 'Romeo', 'S': 'Sierra', 'T': 'Tango', 'U': 'Uniform', 'V': 'Victor',
        'W': 'Whiskey', 'X': 'Xray', 'Y': 'Yankee', 'Z': 'Zulu'
    }
    dictionary = {v: k for k, v in dictionary.iteritems()}
    parts = cool_id.split('-')
    alpha_part = ''
    digital_part = 0
    for part in parts:
        if part.isdigit():
            digital_part += int(part)
        elif part.lower().capitalize() in dictionary.keys():
            alpha_part += dictionary[part]
        else:
            alpha_part += part
    multiplier = 1
    accumulator = 0
    for ch in reversed(alpha_part):
        accumulator += (ord(ch) - ord('A')) * multiplier
        multiplier *= 26
    return str(accumulator * 10000 + int(digital_part))


@contextmanager
def log_time_context(tag='', longer=0):
    """

    :param tag: log time preceeded with this tag
    :param longer: log only if execution took longer than this (seconds)
    :return:
    """
    start = time.time()
    yield
    end = time.time()
    if end - start > longer:
        logging.error('CONTEXT TIMER %s: %s' % (tag, round(end - start, 3)))


def xss_escape(s):
    return s.replace('>', '&gt;').replace('<', '&lt;')


def xss_unescape(s):
    return s.replace('&gt;', '>').replace('&lt;', '<')


def escape_string(s):
    return s.replace('\\', '\\\\').replace("'", "\\'")


def count_percentage(count_data):
    """
    returns list
    :param count_data: list of count
    """
    summa = float(sum(count_data))
    percent_values = [round(float(value) * 100 / summa, 3)
                      for value in count_data]
    return percent_values


def transliterate(text):
    transdict = {
        u"а": "a", u"А": "A",
        u"Б": "B", u"б": "b",
        u"В": "V", u"в": "v",
        u"Г": "G", u"г": "g",
        u"Д": "D", u"д": "d",
        u"Е": "E", u"е": "e",
        u"Ё": "Yo", u"ё": "yo",
        u"Ж": "Zh", u"ж": "zh",
        u"З": "Z", u"з": "z",
        u"И": "I", u"и": "i",
        u"К": "K", u"к": "k",
        u"Л": "L", u"л": "l",
        u"М": "M", u"м": "m",
        u"Н": "N", u"н": "n",
        u"О": "O", u"о": "o",
        u"П": "p", u"п": "p",
        u"Р": "R", u"р": "r",
        u"С": "S", u"с": "s",
        u"Т": "T", u"т": "t",
        u"У": "U", u"у": "u",
        u"Ф": "F", u"ф": "f",
        u"Х": "Kh", u"х": "kh",
        u"Ц": "Ts", u"ц": "ts",
        u"Ч": "Ch", u"ч": "ch",
        u"Ш": "Sh", u"ш": "sh",
        u"Щ": "Sch", u"щ": "sch",
        u"Ъ": "'", u"ъ": "'",
        u"Ы": "Y", u"ы": "y",
        u"Ь": "'", u"ь": "'",
        u"Э": "E", u"э": "e",
        u"Ю": "Yu", u"ю": "yu",
        u"Я": "Ya", u"я": "ya",
        u"Й": "Y", u"й": "y",
        u" ": "-", u"_": "-",
    }
    return ''.join(transdict.get(symbol, symbol) for symbol in text)


class Ini2Json(object):
    """
    Not actually a JSON, but an OrderedDict of OrderedDicts
    """

    def __init__(self, ini_conf):
        self.ini_conf = ini_conf
        if self.ini_conf:
            # try:
            #     self.ini_conf = b64decode(self.ini_conf)
            # except TypeError:
            #     pass
            try:
                self.ini_conf = self.ini_conf.encode('utf-8')
            except UnicodeDecodeError:
                self.ini_conf = self.ini_conf
        self.cp = ConfigParser()

    def convert(self):
        if self.ini_conf:
            self.cp.readfp(io.BytesIO(self.ini_conf))
            json_conf = self.cp._sections
            for section in json_conf:
                del json_conf[section]['__name__']
                for param in json_conf[section]:
                    json_conf[section][param] = json_conf[section][param].replace('\n', '\n\t')
        else:
            json_conf = {}
        return json_conf


class Json2Ini(object):
    """
    supports plain dict and no nested options for ini
    """
    def __init__(self, conf_json):
        self.conf_json = conf_json

    def convert(self):
        conf_ini = ''
        for section in self.conf_json.iteritems():
            conf_ini += '[%s]\n' % unicode(section[0])
            for option in section[1].iteritems():
                try:
                    conf_ini += '%s=%s\n' % tuple(o.decode('utf-8') for o in option)
                except UnicodeEncodeError:
                    conf_ini += '%s=%s\n' % option
        return conf_ini[:-1]


clickhouse_db_struct_replicated = '''
CREATE TABLE IF NOT EXISTS rt_microsecond_histograms (job_date Date, job_id UInt32, tag String, time DateTime, bin UInt32, cnt UInt32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_microsecond_histograms', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS rt_quantiles (job_date Date, job_id UInt32, tag String, time DateTime, q50 Float64,  q75 Float64, q80 Float64,  q85 Float64,  q90 Float64,  q95 Float64,  q98 Float64,  q99 Float64,  q100 Float64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_quantiles', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS rt_microsecond_details (job_date Date, job_id UInt32, tag String, time DateTime, connect_time_sum UInt64, send_time_sum UInt64, latency_sum UInt64, receive_time_sum UInt64, reqps UInt32, resps UInt32, threads UInt32, igress UInt32, egress UInt32, self_load UInt8) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_microsecond_details', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS net_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/net_codes', '{replica}', job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS proto_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/proto_codes', '{replica}', job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS monitoring_verbose_data (job_date Date, job_id UInt32, target_host String, metric_name String, time DateTime, value Float64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/monitoring_verbose_data', '{replica}', job_date, job_id, (job_id, target_host, metric_name, time), 8192);


CREATE TABLE IF NOT EXISTS rt_microsecond_histograms_buffer AS rt_microsecond_histograms ENGINE = Buffer(loaddb, rt_microsecond_histograms, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS rt_quantiles_buffer AS rt_quantiles ENGINE = Buffer(loaddb, rt_quantiles, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS rt_microsecond_details_buffer AS rt_microsecond_details ENGINE = Buffer(loaddb, rt_microsecond_details, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS net_codes_buffer AS net_codes ENGINE = Buffer(loaddb, net_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS proto_codes_buffer AS proto_codes ENGINE = Buffer(loaddb, proto_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS monitoring_verbose_data_buffer AS monitoring_verbose_data ENGINE = Buffer(loaddb, monitoring_verbose_data, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
'''

clickhouse_db_struct = '''
CREATE TABLE IF NOT EXISTS rt_microsecond_histograms (job_date Date, job_id UInt32, tag String, time DateTime, bin UInt32, cnt UInt32) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS rt_quantiles (job_date Date, job_id UInt32, tag String, time DateTime, q50 Float64,  q75 Float64, q80 Float64,  q85 Float64,  q90 Float64,  q95 Float64,  q98 Float64,  q99 Float64,  q100 Float64) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS rt_microsecond_details (job_date Date, job_id UInt32, tag String, time DateTime, connect_time_sum UInt64, send_time_sum UInt64, latency_sum UInt64, receive_time_sum UInt64, reqps UInt32, resps UInt32, threads UInt32, igress UInt32, egress UInt32, self_load UInt8) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS net_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt64) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS proto_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt64) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS monitoring_verbose_data (job_date Date, job_id UInt32, target_host String, metric_name String, time DateTime, value Float64) Engine = MergeTree(job_date, job_id, (job_id, target_host, metric_name, time), 8192);

CREATE TABLE IF NOT EXISTS rt_microsecond_histograms_buffer AS rt_microsecond_histograms ENGINE = Buffer(loaddb, rt_microsecond_histograms, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS rt_quantiles_buffer AS rt_quantiles ENGINE = Buffer(loaddb, rt_quantiles, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS rt_microsecond_details_buffer AS rt_microsecond_details ENGINE = Buffer(loaddb, rt_microsecond_details, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS net_codes_buffer AS net_codes ENGINE = Buffer(loaddb, net_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS proto_codes_buffer AS proto_codes ENGINE = Buffer(loaddb, proto_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS monitoring_verbose_data_buffer AS monitoring_verbose_data ENGINE = Buffer(loaddb, monitoring_verbose_data, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
'''
