#-*- coding:utf-8; mode:python; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*-

import struct

class bit_reader(object):

  ENDIAN_LE = '<'
  ENDIAN_BE = '>'
  _STRUCT_FORMATS = {
    ENDIAN_LE: {
      1: '<B',
      2: '<H',
      4: '<I',
      8: '<Q',
    },
    ENDIAN_BE    : {
      1: '>B',
      2: '>H',
      4: '>I',
      8: '>Q',
    },
  }
  
  def __init__(self, stream, endian):
    self._endian = endian
    self._stream = stream
    self._current_byte = None
    self._current_byte_read_count = None
    
  def read(self, num_bits):
    assert num_bits >= 1
    assert num_bits <= 64
    num_bytes = self.num_bytes_for_bits(num_bits)
    num_odd_bits = num_bits % 8
    if num_odd_bits == 0:
      return self._read_even_bits(num_bits)
    else:
      return self._read_odd_bits(num_bits)

  def _read_even_bits(self, num_bits):
    assert (num_bits % 8) == 0
    num_bytes = self.num_bytes_for_bits(num_bits)
    data = self._read_bytes(num_bytes)
    assert num_bytes in [ 1, 2, 4, 8 ]
    result = struct.unpack(self._STRUCT_FORMATS[self._endian][num_bytes], data)[0]
    return result
  
  def _read_odd_bits(self, num_bits):
    assert (num_bits % 8) != 0
    assert num_bits in range(1, 7)

    if not self._current_byte:
      self._current_byte = self.read_int8()
      self._current_byte_read_count = 0

    assert self._current_byte_read_count in range(0, 8)
      
    num_to_shift = num_bits + self._current_byte_read_count
    shifted = self._current_byte >> (8 - (num_to_shift))
    result = shifted & self._make_mask(num_bits)
    self._current_byte_read_count += num_bits
    assert self._current_byte_read_count <= 8
    return result

  @classmethod
  def _make_mask(clazz, n):
    assert n >= 0
    mask = 0x0
    for i in range(0, n):
      mask |= (0x1 << i)
    return mask
  
  @classmethod
  def num_bytes_for_bits(clazz, num_bits):
    num_odd_bits = num_bits % 8
    num_bytes = num_bits / 8
    if num_odd_bits > 0:
      num_bytes += 1
    return num_bytes

#  size = struct.unpack('<H', fin.read(2))[0]
  
  def _read_bytes(self, num_bytes):
    return self._stream.read(num_bytes)

  def read_word(self, size):
    assert size in [ 1, 2, 4, 8 ]
    return struct.unpack(self._STRUCT_FORMATS[self._endian][size], self._read_bytes(size))[0]

  def read_int8(self):
    return self.read_word(1)
  
  def read_int16(self):
    return self.read_word(2)
  
  def read_int32(self):
    return self.read_word(4)
  
  def read_int64(self):
    return self.read_word(8)

  def decode_bits(self, word, size, offset, num):
    assert size in [ 1, 2, 4, 8 ]
    size_in_bits = size * 8
    shifted = word >> (size_in_bits - num)
    mask = self._make_mask(num)
    return shifter & mask

  size = 3
  size_in_bits = 12
  
  12 - 12
  
  
    
    #  origin = (caca >> 14) & 3
#  tagged = (caca >> 13) & 1
#  addressable = (caca >> 12) & 1
#  protocol = caca & 4095

  
