""" HipopayFlake - 64bit

    0 - 00000000 00000000 00000000 00000000 00000000 0 \
        - 00 - 00000000 - 00000000 0000
    1bit符号位(保留) - 41bit时间戳 - \
        2bit时钟回拨调整- 8bit机器id(4bit dataCenterID + 4bit workerId) - 12bit序列号

"""

from datetime import datetime
import logging


__Author__ = 'KuangQingxu'


_logger = logging.getLogger('HipopayFlake')


class HipopayIDWorker(object):

    _timestamp = -1
    _timestamp_start = 1600574408000
    _clock_backwards = 0
    _bits_clock_backwards = 2
    _bits_center_id = 4
    _bits_worker_id = 4
    _max_worker_id = -1 ^ (-1 << _bits_worker_id)
    _max_center_id = -1 ^ (-1 << _bits_center_id)
    _bits_sequence = 12

    _worker_id_shift = _bits_sequence
    _center_id_shift = (_worker_id_shift + _bits_worker_id)
    _clock_backwards_shift = (_center_id_shift + _bits_center_id)
    _timestamp_shift = (_clock_backwards_shift + _bits_clock_backwards)

    _sequence_mask = -1 ^ (-1 << _bits_sequence)
    _clock_backwards_mask = -1 ^ (-1 << _bits_clock_backwards)

    def __init__(self, center_id, worker_id, sequence, debug=False):

        if debug:
            self._enable_logger()

        try:
            center_id = int(center_id)
            worker_id = int(worker_id)
            sequence = int(sequence)
        except Exception:
            raise ValueError('worker_id/center_id/sequence need `int`')

        if (center_id < 0) or (center_id > self._max_center_id):
            raise ValueError('center_id {0} error'.format(center_id))

        if (worker_id < 0) or (worker_id > self._max_worker_id):
            raise ValueError('worker_id {0} error'.format(worker_id))

        self._worker_id = worker_id
        self._center_id = center_id
        self._sequence = sequence

    def next_id(self):

        ts_now = self._generate_timestamp()

        if ts_now < self._timestamp:
            _logger.info("{0} ------> clock backwards!!!".format(
                self._clock_backwards))
            self._clock_backwards = (self._clock_backwards + 1) \
                & self._clock_backwards_mask
            _logger.info("======> {0} clock backwards!!!".format(
                self._clock_backwards))

        if ts_now == self._timestamp:
            self._sequence = (self._sequence + 1) & self._sequence_mask
            if self._sequence == 0:
                ts_now = self._wait_next_timestamp(self._timestamp)
        else:
            self._sequence = 0

        self._timestamp = ts_now

        return ((ts_now - self._timestamp_start) << self._timestamp_shift) | \
            (self._clock_backwards << self._clock_backwards_shift) | \
            (self._center_id << self._center_id_shift) | \
            (self._worker_id << self._worker_id_shift) | \
            self._sequence

    def _generate_timestamp(self):
        return int(float(datetime.now().strftime('%s.%f')) * 1000)

    def _wait_next_timestamp(self, last_timestamp):
        ts_now = self._generate_timestamp()
        while(ts_now < last_timestamp):
            ts_now = self._generate_timestamp()
        return ts_now

    def _enable_logger(self):
        handler = logging.StreamHandler()
        formatter = logging.Formatter(
                '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        handler.setFormatter(formatter)
        _logger.addHandler(handler)
        _logger.setLevel(logging.DEBUG)


if __name__ == '__main__':

    worker = HipopayIDWorker(1, 1, 1, True)

    for _ in range(0, 3000):
        x = worker.next_id()
        _logger.info('---->  {0}'.format(x))
