#! /usr/bin/env python

from __future__ import (print_function, division)

import time
import sys
import os
import smbus
from datetime import datetime
import RPi.GPIO as GPIO

from PIL import Image, ImageDraw, ImageFont

from papirus import Papirus
from papirus import LM75B
from papirus import get_hwclock

WHITE = 1
BLACK = 0

LM75BD_ADR   = 0x48
MCP7940N_ADR = 0x6F

FONT_FILE   = '/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf'
BITMAP_PATH = '/usr/local/bitmaps/'
BITMAP_FILE = BITMAP_PATH + 'papirus-logo.bmp'

VENDOR  = '/proc/device-tree/hat/vendor'
PRODUCT = '/proc/device-tree/hat/product'
UUID    = '/proc/device-tree/hat/uuid'

GPIO.setmode(GPIO.BCM)

# Check for HAT
if os.path.exists(PRODUCT):
    isHAT = True
    with open(VENDOR, 'r') as fvendor, open(PRODUCT, 'r') as fproduct, open(UUID, 'r') as fuuid:
        vendor = fvendor.read()
        product = fproduct.read()
        serial = fuuid.read()
    if os.path.exists('/dev/rtc'):
       devrtc = True
    else:
       devrtc = False
    SW1 = 16
    SW2 = 26
    SW3 = 20
    SW4 = 21
    SW5 = -1
    GPIO.setup(SW1, GPIO.IN)
    GPIO.setup(SW2, GPIO.IN)
    GPIO.setup(SW3, GPIO.IN)
    GPIO.setup(SW4, GPIO.IN)
else:
    isHAT = False
    devrtc = False
    SW1 = 21
    SW2 = 16
    SW3 = 20
    SW4 = 19
    SW5 = 26
    GPIO.setup(SW1, GPIO.IN)
    GPIO.setup(SW2, GPIO.IN)
    GPIO.setup(SW3, GPIO.IN)
    GPIO.setup(SW4, GPIO.IN)
    GPIO.setup(SW5, GPIO.IN)

# Create an instance of papirus
papirus = Papirus()

def main(argv):
    """main program"""

    bus = smbus.SMBus(1)  # 1 indicates /dev/i2c-1
    if isHAT:
        print("Assuming this is a PaPiRus HAT board\n")
    else:
        print("Assuming this is a PaPiRus Zero board\n")

    i2c_detect(bus) # detect devices connected to the bus

    panel_info(papirus, bus) # provide board and screen information

    if not break_detect(papirus): # trying to use the screen
        print('Screen status OK')
        time.sleep(1)
        print('Displaying an image using full update')
        display_file(papirus, BITMAP_FILE)
        time.sleep(1)
        print('Displaying an animation using partial update')
        display_animated(papirus, BITMAP_PATH)
        time.sleep(1)
        print('Displaying information about the board and screen')
        display_papirusdata(papirus)
        print('')

        # Test the switches
        if isHAT:
            print('Board layout SW4--SW3--SW2--SW1')
            print('Board pins    36   37   38   40')
            print('BCM GPIO      16   26   20   21')
        else:
            print('Board layout SW5--SW4--SW3--SW2--SW1')
            print('Board pins    40   36   38   35   37')
            print('BCM GPIO      21   16   20   19   26')
        print('Press SW4 for board info, SW3 for animation, SW2 for single image, SW1 to exit')

        while True:
            # Exit when SW1 is pressed
            if GPIO.input(SW1) == False:
                print('SW1 Pressed - Exiting')
                papirus.clear()
                sys.exit()
            if GPIO.input(SW2) == False:
                print('SW2 Pressed')
                display_file(papirus, BITMAP_FILE)
            if GPIO.input(SW3) == False:
                print('SW3 Pressed')
                display_animated(papirus, BITMAP_PATH)
            if GPIO.input(SW4) == False:
                print('SW4 Pressed')
                display_papirusdata(papirus)
            if (SW5 != -1) and (GPIO.input(SW5) == False):
                print('SW5 Pressed')

            time.sleep(0.2)  # debounce
    else:
        print('The screen might be broken, cannot continue further')

def i2c_detect(bus):
    """Detects if the temp and RTC chips are available"""

    print("---- Detect temperature sensor and RTC (RTC for HAT only) ----")

    if devrtc:
       print("Real Time Clock detected via /dev/rtc (RTC kernel module)")

    for device in range(128):
        try:
            bus.read_i2c_block_data(device, 0)
            if device == LM75BD_ADR:
                print("NXP LM75BD detected at: 0x{d:02x}".format(d=device))
            elif device == MCP7940N_ADR:
                print("MCP7940N detected at: 0x{d:02x}".format(d=device))
            else:
                print("Other device found at: {d:x}".format(d=device))
        except:  # exception if I2C read fails
            pass
    print("")

def date_time_rtc(bus):
    """Reads time from Real Time Clock using I2C"""
    data = bus.read_i2c_block_data(MCP7940N_ADR, 0, 7)
    sec   = (data[0] & 0x7f) // 16 * 10 + (data[0] & 0x0f)
    min   = data[1] // 16 * 10 + (data[1] & 0x0f)
    hour  = data[2] // 16 * 10 + (data[2] & 0x0f)
    day   = data[4] // 16 * 10 + (data[4] & 0x0f)
    month = (data[5] & 0x10) // 16 * 10 + (data[5] & 0x0f)
    year  = data[6] // 16 * 10 + (data[6] & 0x0f)
    dt = datetime(2000+year, month, day, hour, min, sec)
    return dt

def date_time_devrtc():
    """Reads time from Real Time Clock using /dev/rtc (RTC kernel module)"""
    return get_hwclock()

def panel_info(papirus, bus):
    """Retrieves information about the screen"""

    print("---- Screen Information ----")

    if isHAT:
        print("Vendor: {v:s}".format(v=vendor))
        print("Product: {p:s}".format(p=product))
        print("Serial: {s:s}".format(s=serial))
        if devrtc == False:
            date_time = date_time_rtc(bus)
            print("Date/Time from RTC: {d:s}".format(d=date_time.strftime("%A %d %B %Y - %H:%M:%S")))
        elif os.getuid() != 0:
            print("\n*** To read time from Real Time Clock via /dev/rtc run script as root ***\n")
        else:
            date_time = date_time_devrtc()
            print("Date/Time from RTC: {d:s}".format(d=date_time.strftime("%A %d %B %Y - %H:%M.%S")))

    sensor = LM75B()
    print("Temperature from sensor: {t:d} degrees Celsius" .format(t=sensor.getTempC()))

    print("Panel info: {p:s} {w:d} x {h:d} version={v:s} COG={g:d} FILM={f:d}".format(p=papirus.panel, w=papirus.width,
                                                                            h=papirus.height, v=papirus.version,
                                                                            g=papirus.cog, f=papirus.film))
    print("")

def break_detect(papirus):
    """Try using the screen to determine if it's broken"""
    # initially set all white background
    image = Image.new('1', papirus.size, WHITE)
    # prepare for drawing
    draw = ImageDraw.Draw(image)
    # one pixel in top left corner
    draw.point((0, 0), fill=BLACK)
    # display image on the panel
    papirus.display(image)
    papirus.update()
    # capture the status of the screen via fuse
    with open('/dev/epd/error', 'r') as errfile:
       display_status = str(errfile.read()).rstrip(' \n\r\0')
    if display_status == 'OK':
        return False
    else:
        return True

def display_papirusdata(papirus):
    """Displaying information about the board and screen"""
    w = papirus.width
    h = papirus.height
    if h<=96:
        text_font_size1 = 6
        text_font_size2 = 8
        text_font_size3 = 20
    else:
        text_font_size1 = 11
        text_font_size2 = 16
        text_font_size3 = 36
    sensor = LM75B()

    text_font1 = ImageFont.truetype(FONT_FILE, text_font_size1)
    text_font2 = ImageFont.truetype(FONT_FILE, text_font_size2)
    text_font3 = ImageFont.truetype(FONT_FILE, text_font_size3)

    # initially set all white background
    image = Image.new('1', papirus.size, WHITE)

    # prepare for drawing
    draw = ImageDraw.Draw(image)
    draw.rectangle((1, 1, w - 1, h - 1), fill=WHITE, outline=BLACK)
    draw.rectangle((2, 2, w - 2, h - 2), fill=WHITE, outline=BLACK)
    # display some information if it is a HAT others if is a Zero
    if isHAT:
        draw.text((4, 3), vendor[:-1], fill=BLACK, font=text_font3)
        draw.text((4, h / 3 + 10), product[:-1], fill=BLACK, font=text_font2)
    else:
        draw.text((4, 3), "Pi Supply", fill=BLACK, font=text_font3)
        draw.text((4, h / 3 + 10), "PaPiRus Zero", fill=BLACK, font=text_font2)

    draw.text((4, h / 2 + 10), "Temperature " + str(sensor.getTempC()) + "\xb0C", fill=BLACK, font=text_font2)

    if w>128 and isHAT:
        draw.text((5, h-text_font_size1 - 2), serial[:-1], fill=BLACK, font=text_font1)

    # display image on the screen
    papirus.display(image)
    papirus.update()

def display_file(papirus, file_name):
    """Display resized image using full update"""

    image = Image.open(file_name)
    rs = image.resize((papirus.width, papirus.height), Image.ANTIALIAS)
    bw = rs.convert("1", dither=Image.FLOYDSTEINBERG)

    papirus.display(bw)
    papirus.update()

def display_animated(papirus, file_path):
    """Display animation using partial update"""

    for i in range(0, len(os.listdir(file_path))):
        name = file_path + '/' + str(i) + '.gif'
        if os.path.isfile(name):
            image = Image.open(name)
            rs = image.resize((papirus.width, papirus.height), Image.ANTIALIAS)
            bw = rs.convert("1", dither=Image.FLOYDSTEINBERG)

            papirus.display(bw)
            papirus.partial_update()

    papirus.update()

# main
if "__main__" == __name__:
    if len(sys.argv) < 1:
        sys.exit('usage: {p:s}'.format(p=sys.argv[0]))

    try:
        main(sys.argv[1:])
    except KeyboardInterrupt:
        sys.exit('interrupted')
        pass
