# encoding: utf-8
"""
labels.py

Created by Thomas Mangin on 2012-07-08.
Copyright (c) 2009-2017 Exa Networks. All rights reserved.
License: 3-clause BSD. (See the COPYRIGHT file)
"""

from struct import pack
from struct import unpack


# ======================================================================= Labels
# RFC 3107


def opt_raw_label(label, format=' (%d)'):
    return format % label if label else ''


class Labels(object):
    MAX = pow(2, 20) - 1

    def __init__(self, labels, bos=True, raw_labels=None):
        self.labels = labels
        self.raw_labels = raw_labels
        packed = []
        if raw_labels:
            for label in raw_labels:
                packed.append(pack('!L', label)[1:])
            # fill self.labels as well, not for packing, but to allow
            # consistent string representations
            if not self.labels:
                self.labels = [x >> 4 for x in self.raw_labels]
        else:
            for label in labels:
                # shift to 20 bits of the label to be at the top of three bytes and then truncate.
                packed.append(pack('!L', label << 4)[1:])
            # Mark the bottom of stack with the bit
            if packed and bos:
                packed.pop()
                packed.append(pack('!L', (label << 4) | 1)[1:])
            self.raw_labels = [None for _ in self.labels]
        self.packed = b''.join(packed)
        self._len = len(self.packed)

    def __eq__(self, other):
        return self.labels == other.labels

    def __neq__(self, other):
        return self.labels != other.labels

    def __lt__(self, other):
        raise RuntimeError('comparing EthernetTag for ordering does not make sense')

    def __le__(self, other):
        raise RuntimeError('comparing EthernetTag for ordering does not make sense')

    def __gt__(self, other):
        raise RuntimeError('comparing EthernetTag for ordering does not make sense')

    def __ge__(self, other):
        raise RuntimeError('comparing EthernetTag for ordering does not make sense')

    def pack(self):
        return self.packed

    def __len__(self):
        return self._len

    def json(self):
        if len(self.labels) >= 1:
            return '"label": [ %s ]' % ', '.join(
                ["[%d%s]" % (l, opt_raw_label(r, ', %d')) for (l, r) in zip(self.labels, self.raw_labels)]
            )
        else:
            return ''

    def __str__(self):
        if len(self.labels) > 1:
            return ' label [ %s ]' % ' '.join(
                ["%d%s" % (l, opt_raw_label(r)) for (l, r) in zip(self.labels, self.raw_labels)]
            )
        elif len(self.labels) == 1:
            return ' label %d%s' % (self.labels[0], opt_raw_label(self.raw_labels[0]))
        else:
            return ''

    def __repr__(self):
        if len(self.labels) > 1:
            return '[ %s ]' % ','.join(["%d%s" % (l, opt_raw_label(r)) for (l, r) in zip(self.labels, self.raw_labels)])
        elif len(self.labels) == 1:
            return '%d%s' % (self.labels[0], opt_raw_label(self.raw_labels[0]))
        else:
            return '[ ]'

    @classmethod
    def unpack(cls, data):
        labels = []
        raw_labels = []
        while len(data):
            label = unpack('!L', bytes([0]) + data[:3])[0]
            data = data[3:]
            labels.append(label >> 4)
            raw_labels.append(label)
            if label & 0x001:
                break
        return cls(labels, raw_labels=raw_labels)


Labels.NOLABEL = Labels([])
