'''
A pure python version of the hdr_histogram code

Ported from
https://github.com/HdrHistogram/HdrHistogram (Java)
https://github.com/HdrHistogram/HdrHistogram_c (C)

Written by Alec Hothan

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.
'''
from __future__ import division, print_function
from builtins import range
from builtins import object
import math
import sys
from hdrh.iterators import AllValuesIterator
from hdrh.iterators import RecordedIterator
from hdrh.iterators import PercentileIterator
from hdrh.iterators import LinearIterator
from hdrh.iterators import LogIterator
from hdrh.codec import HdrHistogramEncoder

def get_bucket_count(value, subb_count, unit_mag):
    smallest_untrackable_value = subb_count << unit_mag
    buckets_needed = 1
    while smallest_untrackable_value <= value:
        if smallest_untrackable_value > sys.maxsize // 2:
            return buckets_needed + 1
        smallest_untrackable_value <<= 1
        buckets_needed += 1
    return buckets_needed

class HdrHistogram(object):
    '''This class supports the recording and analyzing of sampled data value
    counts across a configurable integer value range with configurable value
    precision within the range. Value precision is expressed as the number of
    significant digits in the value recording, and provides control over value
    quantization behavior across the value range and the subsequent value
    resolution at any given level.

    For example, a Histogram could be configured to track the counts of
    observed integer values between 0 and 3,600,000,000 while maintaining a
    value precision of 3 significant digits across that range. Value
    quantization within the range will thus be no larger than 1/1,000th
    (or 0.1%) of any value. This example Histogram could be used to track and
    analyze the counts of observed response times ranging between 1 microsecond
    and 1 hour in magnitude, while maintaining a value resolution of 1
    microsecond up to 1 millisecond, a resolution of 1 millisecond (or better)
    up to one second, and a resolution of 1 second (or better) up to 1,000
    seconds. At it's maximum tracked value (1 hour), it would still maintain a
    resolution of 3.6 seconds (or better).
    '''

    def __init__(self,
                 lowest_trackable_value,
                 highest_trackable_value,
                 significant_figures,
                 word_size=8,
                 b64_wrap=True,
                 hdr_payload=None):
        '''Create a new histogram with given arguments

        Params:
            lowest_trackable_value The lowest value that can be discerned
                (distinguished from 0) by the histogram.
                Must be a positive integer that is >= 1.
                May be internally rounded down to nearest power of 2.
            highest_trackable_value The highest value to be tracked by the
                histogram. Must be a positive integer that is >=
                (2 * lowest_trackable_value).
            significant_figures The number of significant decimal digits to
                which the histogram will maintain value resolution and
                separation. Must be a non-negative integer between 0 and 5.
            word_size size of counters in bytes, only 2, 4, 8-byte counters
                are supported (default is 8-byte or 64-bit counters)
            b64_wrap specifies if the encoding of this histogram should use
                base64 wrapping (only useful if you need to encode the histogram
                to save somewhere or send over the wire. By default base64
                encoding is assumed
            hdr_payload only used for associating an existing payload created
                from decoding an encoded histograme
        Exceptions:
            ValueError if the word_size value is unsupported
                if significant_figures is invalid
        '''
        if significant_figures < 1 or significant_figures > 5:
            raise ValueError('Invalid significant_figures')
        self.lowest_trackable_value = lowest_trackable_value
        self.highest_trackable_value = highest_trackable_value
        self.significant_figures = significant_figures
        self.unit_magnitude = int(math.floor(math.log(lowest_trackable_value) / math.log(2)))
        largest_value_single_unit_res = 2 * math.pow(10, significant_figures)
        subb_count_mag = int(math.ceil(math.log(largest_value_single_unit_res) / math.log(2)))
        self.sub_bucket_half_count_magnitude = subb_count_mag - 1 if subb_count_mag > 1 else 0
        self.sub_bucket_count = int(math.pow(2, self.sub_bucket_half_count_magnitude + 1))
        self.sub_bucket_half_count = self.sub_bucket_count // 2
        self.sub_bucket_mask = (self.sub_bucket_count - 1) << self.unit_magnitude
        self.bucket_count = get_bucket_count(highest_trackable_value,
                                             self.sub_bucket_count,
                                             self.unit_magnitude)
        self.min_value = sys.maxsize
        self.max_value = 0
        self.total_count = 0
        self.counts_len = (self.bucket_count + 1) * (self.sub_bucket_count // 2)
        self.word_size = word_size

        if hdr_payload:
            payload = hdr_payload.payload