# (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
# and is covered by GPLv3 license found in COPYING.
#
# display.py - OLED rendering
#
import machine, ssd1306, uzlib, ckcc
from ssd1306 import SSD1306_SPI
import framebuf
from uasyncio import sleep_ms
from graphics import Graphics

# we support 4 fonts
from zevvpeep import FontSmall, FontLarge, FontTiny
FontFixed = object()    # ugly 8x8 PET font

#ARROWS = [chr(i) for i in range(8592, 8592+4)]

class Display:

    WIDTH = 128
    HEIGHT = 64

    # use these negative X values for auto layout features
    CENTER = -2
    RJUST = -1

    def __init__(self):
        from machine import Pin

        spi = machine.SPI(1)
        reset_pin = Pin('PA6', Pin.OUT)
        dc_pin = Pin('PA8', Pin.OUT)
        cs_pin = Pin('PA4', Pin.OUT)

        self.dis = SSD1306_SPI(128, 64, spi, dc_pin, reset_pin, cs_pin)
        self.clear()
        self.show()

    def width(self, msg, font):
        if font == FontFixed:
            return len(msg) * 8
        else:
            return sum(font.lookup(ord(ch)).w for ch in msg)

    def icon(self, x, y, name, invert=0):
        # see graphics.py (auto generated file) for names
        w,h, bw, wbits, data = getattr(Graphics, name)

        if wbits:
            data = uzlib.decompress(data, wbits)

        if invert:
            data = bytearray(i^0xff for i in data)

        gly = framebuf.FrameBuffer(bytearray(data), w, h, framebuf.MONO_HLSB)
        self.dis.blit(gly, x, y, invert)

        return (w, h)

    def text(self, x,y, msg, font=FontSmall, invert=0):
        # Draw at x,y (top left corner of first letter)
        # using font. Use invert=1 to get reverse video

        if x is None or x < 0:
            # center/rjust
            w = self.width(msg, font)
            if x == None:
                x = max(0, (self.WIDTH - w) // 2)
            else:
                # measure from right edge (right justify)
                x = max(0, self.WIDTH - w + 1 + x)

        if y < 0:
            # measure up from bottom edge
            y = self.HEIGHT - font.height + 1 + y

        if font == FontFixed:
            # use font provided by Micropython: 8x8
            self.dis.text(msg, x, y)

            return x + (len(msg) * 8)

        for ch in msg:
            fn = font.lookup(ord(ch))
            if fn is None:
                # use last char in font as error char for junk we don't
                # know how to render
                fn = font.lookup(font.code_range.stop)
            bits = bytearray(fn.w * fn.h)
            bits[0:len(fn.bits)] = fn.bits
            if invert:
                bits = bytearray(i^0xff for i in bits)
            gly = framebuf.FrameBuffer(bits, fn.w, fn.h, framebuf.MONO_HLSB)
            self.dis.blit(gly, x, y, invert)
            x += fn.w

        return x

    def clear(self):
        self.dis.fill(0x0)

    def clear_rect(self, x,y, w,h):
        self.dis.fill_rect(x,y, w,h, 0)

    def show(self):
        self.dis.show()

    def hline(self, y):
        self.dis.line(0, y, 128, y, 1)
    def vline(self, x):
        self.dis.line(x, 0, x, 64, 1)

    def scroll_bar(self, fraction):
        # along right edge
        self.dis.fill_rect(128-5, 0, 5, 64, 0)
        self.icon(128-3, 1, 'scroll');
        mm = 64-6
        pos = min(int(mm*fraction), mm)
        self.dis.fill_rect(128-2, pos, 1, 8, 1)

    async def animate_splash(self, loop, done_fcn, func50):
        ns = 32 if not ckcc.is_simulator() else 4
        for i in range(ns):
            self.progress_bar(i / (ns-1))
            self.show()
            if i == ns//2 and func50:
                # do something at 50% mark
                func50()
            await sleep_ms(5)

        if done_fcn:
            # create a new task, so this stack can be freed
            loop.create_task(done_fcn())

    def fullscreen(self, msg, percent=None, line2=None):
        # show a simple message "fullscreen". 
        self.clear()
        if line2:
            y = 10
            self.text(None, y, msg, font=FontLarge)
            y += 24
            self.text(None, y, line2, font=FontSmall)
        else:
            y = 14
            self.text(None, y, msg, font=FontLarge)
        if percent is not None:
            self.progress_bar(percent)
        self.show()

    def splash(self):
        # display a splash screen with some version numbers
        self.clear()
        y = 4
        self.text(None,    y, 'Coldcard', font=FontLarge)
        self.text(None, y+20, 'Wallet', font=FontLarge)

        from version import get_mpy_version
        timestamp, label, *_ = get_mpy_version()

        y = self.HEIGHT-10
        self.text(0,  y, 'Version '+label, font=FontTiny)
        self.text(-1, y, timestamp, font=FontTiny)
        
        self.show()

    def progress_bar(self, percent):
        # Horizontal progress bar
        # takes 0.0 .. 1.0 as fraction of doneness
        percent = max(0, min(1.0, percent))
        self.dis.hline(0, self.HEIGHT-1, int(self.WIDTH * percent), 1)

    def progress_bar_show(self, percent):
        # useful as a callback
        self.progress_bar(percent)
        self.show()

    def splash_animate(self, loop, done_fcn=None, func50=None):
        # do a bootup delay and some work along the way
        loop.create_task(self.animate_splash(loop, done_fcn, func50))

# EOF
