// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[derive(Copy, Clone, Debug)]
pub struct Prefix {
    prefix: u8,
    len: u8,
    mask: u8,
}

impl Prefix {
    pub fn new(prefix: u8, len: u8) -> Self {
        // len should never be larger than 7.
        // Most of Prefixes are instantiated as consts bellow. The only place where this construcrtor is used
        // is in tests and when literals are encoded and the Huffman bit is added to one of the consts bellow.
        // create_prefix guaranty that all const have len < 7 so we can safely assert that len is <=7.
        assert!(len <= 7);
        assert!((len == 0) || (prefix & ((1 << (8 - len)) - 1) == 0));
        Self {
            prefix,
            len,
            mask: if len == 0 {
                0xFF
            } else {
                ((1 << len) - 1) << (8 - len)
            },
        }
    }

    pub fn len(self) -> u8 {
        self.len
    }

    pub fn prefix(self) -> u8 {
        self.prefix
    }

    pub fn cmp_prefix(self, b: u8) -> bool {
        (b & self.mask) == self.prefix
    }
}

#[macro_export]
macro_rules! create_prefix {
    ($n:ident) => {
        pub const $n: Prefix = Prefix {
            prefix: 0x0,
            len: 0,
            mask: 0xFF,
        };
    };
    ($n:ident, $v:expr, $l:expr) => {
        static_assertions::const_assert!($l < 7);
        static_assertions::const_assert!($v & ((1 << (8 - $l)) - 1) == 0);
        pub const $n: Prefix = Prefix {
            prefix: $v,
            len: $l,
            mask: ((1 << $l) - 1) << (8 - $l),
        };
    };
    ($n:ident, $v:expr, $l:expr, $m:expr) => {
        static_assertions::const_assert!($l < 7);
        static_assertions::const_assert!($v & ((1 << (8 - $l)) - 1) == 0);
        static_assertions::const_assert!((((1 << $l) - 1) << (8 - $l)) >= $m);
        pub const $n: Prefix = Prefix {
            prefix: $v,
            len: $l,
            mask: $m,
        };
    };
}

create_prefix!(NO_PREFIX);
//=====================================================================
// Decoder instructions prefix
//=====================================================================

// | 1 |      Stream ID (7+)       |
create_prefix!(DECODER_HEADER_ACK, 0x80, 1);

// | 0 | 1 |     Stream ID (6+)    |
create_prefix!(DECODER_STREAM_CANCELLATION, 0x40, 2);

// | 0 | 0 |     Increment (6+)    |
create_prefix!(DECODER_INSERT_COUNT_INCREMENT, 0x00, 2);

//=====================================================================
// Encoder instructions prefix
//=====================================================================

// | 0 | 0 | 1 |   Capacity (5+)   |
create_prefix!(ENCODER_CAPACITY, 0x20, 3);

// | 1 | T |    Name Index (6+)    |
// T == 1 static
// T == 0 dynamic
create_prefix!(ENCODER_INSERT_WITH_NAME_REF_STATIC, 0xC0, 2);
create_prefix!(ENCODER_INSERT_WITH_NAME_REF_DYNAMIC, 0x80, 2);

// | 0 | 1 | H | Name Length (5+)  |
// H is not relevant for decoding this prefix, therefore the mask is 1100 0000 = 0xC0
create_prefix!(ENCODER_INSERT_WITH_NAME_LITERAL, 0x40, 2);

// | 0 | 0 | 0 |    Index (5+)     |
create_prefix!(ENCODER_DUPLICATE, 0x00, 3);

//=====================================================================
//Header block encoding prefixes
//=====================================================================

create_prefix!(BASE_PREFIX_POSITIVE, 0x00, 1);
create_prefix!(BASE_PREFIX_NEGATIVE, 0x80, 1);

// | 1 | T |  index(6+) |
// T == 1 static
// T == 0 dynamic
create_prefix!(HEADER_FIELD_INDEX_STATIC, 0xC0, 2);
create_prefix!(HEADER_FIELD_INDEX_DYNAMIC, 0x80, 2);

// | 0 | 0 | 0 | 1 |  Index(4+) |
create_prefix!(HEADER_FIELD_INDEX_DYNAMIC_POST, 0x10, 4);

// | 0 | 1 | N | T |  Index(4+) |
// T == 1 static
// T == 0 dynamic
// N is ignored, therefore the mask is 1101 0000 = 0xD0
create_prefix!(HEADER_FIELD_LITERAL_NAME_REF_STATIC, 0x50, 4, 0xD0);
create_prefix!(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC, 0x40, 4, 0xD0);

// | 0 | 0 | 0 | 0 | N |  Index(3+) |
// N is ignored, therefore the mask is 1111 0000 = 0xF0
create_prefix!(HEADER_FIELD_LITERAL_NAME_REF_DYNAMIC_POST, 0x00, 5, 0xF0);

// | 0 | 0 | 1 | N | H |  Index(3+) |
// N is ignored and H is not relevant for decoding this prefix, therefore the mask is 1110 0000 = 0xE0
create_prefix!(HEADER_FIELD_LITERAL_NAME_LITERAL, 0x20, 4, 0xE0);