'''
Script written by d4g
Build upon original script by stuartm2 (https://github.com/stuartm2/CircuitPython_BMP_Reader/tree/master/lib)
But refactored and optimized regarding memory consumption and merged into one file.

How To Use:
- copy file to /apps/bmp_reader/
- place images into /apps/bmp_reader/res/
- images have to be 24 bit bitmaps with the size 179x79 or less
- use right buttons to cycle through images

Display Size 179x79
'''
import gc
import display
import buttons
import os
class BMPReader(object):
  def __init__(self, filename):
    self._filename = filename
    self._read_img_header()
    with open(self._filename, 'rb') as f:
      self.f = bytes(f.read())

  def do_pixels(self):
    width = self.width
    # line length must be a multiple of 4
    # with the bitmask, the alignment is enforced
    with display.open() as d:
      d.clear()
      d.update()
      for line in range(self.height):
        # align image
        pitch = int(( (self.width * 3)  +3 ) & 0xFFFFFFFC)
        start = self.start_pos + line * pitch
        pixel_data = bytes(self.f[start:start+self.width*3])
        i = 0
        t = len(pixel_data) 
        while True:
          r = b = g = -1
          try:
            i += 1
            r = pixel_data[t - i]
            i += 1
            g = pixel_data[t - i]
            i += 1
            b = pixel_data[t - i]
            if i > t:
              break
            d.pixel(int(self.width - int(i/3)) , self.height - line  ,col=(r,g,b))
          except:
            # Somethin went wrong
            print('FOOO r: %s g: %s b: %s' % (r,g,b))
            break
      d.update()
      gc.collect()
    return 


  def _read_img_header(self):
    def _lebytes_to_int(bytes):
      n = 0x00
      while len(bytes) > 0:
        n <<= 8
        n |= bytes.pop()
      return int(n)

    with open(self._filename, 'rb') as f:
      img_bytes = list(bytearray(f.read(38)))
    assert img_bytes[0:2] == [66, 77], "Not a valid BMP file"
    # scan for compression
    assert _lebytes_to_int(img_bytes[30:34]) == 0, \
      "Compression is not supported"
    # read color depth
    assert _lebytes_to_int(img_bytes[28:30]) == 24, \
      "Only 24-bit colour depth is supported"

    # determin where the image data lies
    self.start_pos = _lebytes_to_int(img_bytes[10:14])
    self.end_pos = self.start_pos + _lebytes_to_int(img_bytes[34:38])

    # read width and height
    self.width = _lebytes_to_int(img_bytes[18:22])
    self.height = _lebytes_to_int(img_bytes[22:26])
    print ("height %s, width %s, start_pos %s, end_pos %s" % (self.height, self.width, self.start_pos, self.end_pos))



#img = BMPReader('/apps/bmp_reader/image.bmp')


def display_bmp(filename):
  gc.collect()
  print("Attempting to display %s" % filename)
  print("Mem: %s" % gc.mem_free())

  img = BMPReader(filename)
  img.do_pixels()

  del img

print("Mem: %s" % gc.mem_free())
gc.enable()
gc.collect()
print("Mem: %s" % gc.mem_free())


bmps = os.listdir("/apps/bmp_reader/res")
imgc = len(bmps)
print("Images: %s" % imgc)
i = 0
display_bmp("/apps/bmp_reader/res/%s" % bmps[i])
while True:
  pressed = buttons.read(
    buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
  )
  if pressed != 0:
    if i < (imgc - 1):
      i += 1
    else:
      i = 0
    display_bmp("/apps/bmp_reader/res/%s" % bmps[i])