from machine import I2C
import sys

_DISPLAY_BLINK_CMD = 0x80
_DISPLAY_BLINK_DISPLAYON = 0x01
_DISPLAY_CMD_BRIGHTNESS = 0xE0
_DISPLAY_OSCILATOR_ON = 0x21

SET_CONTRAST        = const(0x81)
SET_ENTIRE_ON       = const(0xa4)
SET_NORM_INV        = const(0xa6)
SET_DISP            = const(0xae)
SET_MEM_ADDR        = const(0x20)
SET_COL_ADDR        = const(0x21)
SET_PAGE_ADDR       = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP       = const(0xa0)
SET_MUX_RATIO       = const(0xa8)
SET_COM_OUT_DIR     = const(0xc0)
SET_DISP_OFFSET     = const(0xd3)
SET_COM_PIN_CFG     = const(0xda)
SET_DISP_CLK_DIV    = const(0xd5)
SET_PRECHARGE       = const(0xd9)
SET_VCOM_DESEL      = const(0xdb)
SET_CHARGE_PUMP     = const(0x8d)

currentBoard=""
if(sys.platform=="esp8266"):
  currentBoard="esp8266"
elif(sys.platform=="esp32"):
  currentBoard="esp32"
elif(sys.platform=="pyboard"):
  currentBoard="pyboard"


class SSD1306:
  def __init__(self, width, height, external_vcc):
    self.width = width
    self.height = height
    self.external_vcc = external_vcc
    self.pages = self.height // 8
    self.buffer = bytearray(self.pages * self.width)
    self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MVLSB)
    self.poweron()
    self.init_display()

  def init_display(self):
    for cmd in (
      SET_DISP | 0x00, # off
      SET_MEM_ADDR, 0x00, # horizontal
      SET_DISP_START_LINE | 0x00,
      SET_SEG_REMAP | 0x01,
      SET_MUX_RATIO, self.height - 1,
      SET_COM_OUT_DIR | 0x08,
      SET_DISP_OFFSET, 0x00,
      SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
      SET_DISP_CLK_DIV, 0x80,
      SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
      SET_VCOM_DESEL, 0x30, # 0.83*Vcc
      SET_CONTRAST, 0xff,
      SET_ENTIRE_ON,
      SET_NORM_INV,
      SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
      SET_DISP | 0x01):
      self.write_cmd(cmd)
    self.fill(0)
    self.show()

  def poweroff(self):
    self.write_cmd(SET_DISP | 0x00)

  def contrast(self, contrast):
    self.write_cmd(SET_CONTRAST)
    self.write_cmd(contrast)

  def invert(self, invert):
    self.write_cmd(SET_NORM_INV | (invert & 1))

  def show(self):
    x0 = 0
    x1 = self.width - 1
    if self.width == 64:
      x0 += 32
      x1 += 32
    self.write_cmd(SET_COL_ADDR)
    self.write_cmd(x0)
    self.write_cmd(x1)
    self.write_cmd(SET_PAGE_ADDR)
    self.write_cmd(0)
    self.write_cmd(self.pages - 1)
    self.write_data(self.buffer)

  def fill(self, col):
    self.framebuf.fill(col)

  def pixel(self, x, y, col):
    self.framebuf.pixel(x, y, col)

  def scroll(self, dx, dy):
    self.framebuf.scroll(dx, dy)

  def text(self, string, x, y, col=1):
    self.framebuf.text(string, x, y, col)

  def hline(self, x, y, w, col):
    self.framebuf.hline(x, y, w, col)

  def vline(self, x, y, h, col):
    self.framebuf.vline(x, y, h, col)

  def line(self, x1, y1, x2, y2, col):
    self.framebuf.line(x1, y1, x2, y2, col)

  def rect(self, x, y, w, h, col):
    self.framebuf.rect(x, y, w, h, col)

  def fill_rect(self, x, y, w, h, col):
    self.framebuf.fill_rect(x, y, w, h, col)

  def blit(self, fbuf, x, y):
    self.framebuf.blit(fbuf, x, y)

class SSD1306_I2C(SSD1306):
  def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
    self.i2c = i2c
    self.addr = addr
    self.temp = bytearray(2)
    super().__init__(width, height, external_vcc)

  def write_cmd(self, cmd):
    self.temp[0] = 0x80 # Co=1, D/C#=0
    self.temp[1] = cmd
    global currentBoard
    if currentBoard=="esp8266" or currentBoard=="esp32":
      self.i2c.writeto(self.addr, self.temp)
    elif currentBoard=="pyboard":
      self.i2c.send(self.temp,self.addr)

  def write_data(self, buf):
    self.temp[0] = self.addr << 1
    self.temp[1] = 0x40 # Co=0, D/C#=1
    global currentBoard
    if currentBoard=="esp8266" or currentBoard=="esp32":
      self.i2c.start()
      self.i2c.write(self.temp)
      self.i2c.write(buf)
      self.i2c.stop()
    elif currentBoard=="pyboard":
     self.i2c.mem_write(buf,self.addr,0x40)

  def poweron(self):
    pass

  def show_str(self,s1,s2,s3,s4):
    self.text(s1, 0 , 0)
    self.text(s2, 0 , 16)
    self.text(s3, 0 , 32)
    self.text(s4, 0 , 48)
    self.show()

  def show_rect(self,x,y,w,h,b):
    self.rect(x,y,w,h,b)
    self.show()  

  def show_fill_rect(self,x,y,w,h,b):
    self.fill_rect(x,y,w,h,b)
    self.show() 

  def show_vline(self,x,y,h,b):
    self.vline(x,y,h,b)
    self.show()     

  def show_hline(self,x,y,w,b):
    self.hline(x,y,w,b)
    self.show()

  def show_line(self,x,y,x2,y2,b):
    self.line(x,y,x2,y2,b)
    self.show()