#!/usr/bin/python3
# -*- coding: UTF8 -*-

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""
Display data from MQTT broker on ePaper screen

@author: arofarn
"""
__version__ = 0.3

###########
# IMPORTS #
###########

from cameteo_conf import *
import paho.mqtt.client as mqtt
import time
import json
import netifaces, socket
import inkyphat
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

##########
# Config #
##########
mqtt_client_id = "epaper_display"

# Colors codes
white = inkyphat.WHITE
black = inkyphat.BLACK
grey = inkyphat.RED

new_data_flag = False

#############
# CALLBACKS #
#############

def on_connect(client, userdata, flags, rc):
    """On connection callback : topics subscription"""
    print(mqtt.connack_string(rc))
    if rc == 0:
        print("Subscribing to %s ..." % mqtt_topic)
        client.subscribe([(mqtt_topic, 0),
                          (camera_mqtt_topic + "/#", 0)]
                        )
        print("OK")


def on_message(client, userdata, msg):
    """Callback for managing data sent by MQTT broker"""

    global new_data_flag

    top=msg.topic[len(mqtt_topic)-1:].strip()
    #List of the subtopics
    subtopics = top.split("/")

    #Display atmospheric data from BME280 sensor
    if subtopics[0] == "BME":
        payload = msg.payload.decode()
        val = json.loads(payload)

        #Is value consistent ?
        try:
            val['value'] = float(val['value'] )
        except:
            print("Value error: {}".format(val['value']))
            val['value'] = float('nan')

        #Manage special character "degree"
        val['unit'] = val['unit'].replace('deg', '°')

        #List of 'displayable' data types and their coordinate on screen
        coord_type = { 'AT'     : (1, data1_line),
                       'RH'     : (inkyphat.WIDTH / 2 + 12, data1_line),
                       'AP'     : (1, data2_line),
                       'VBAT'   : (inkyphat.WIDTH / 2 + 12, data2_line),
                      }
        print(val)
        if val['type'] in coord_type:
            #Erase old data
            inkyphat.rectangle(coord_type[val['type']] + (coord_type[val['type']][0] + inkyphat.WIDTH / 2 - 2,
                                                          coord_type[val['type']][1] + 16),
                               fill=white)
            #Draw new values
            inkyphat.text(coord_type[val['type']],
                          "{:6.1f}{}  ".format(val['value'], val['unit']),
                          font=data_font,
                          fill=black)
            new_data_flag = True
            update_display()

    # Print feather's date and time on epaper display
    elif subtopics[0] == "SYS":
        payload = msg.payload.decode()
        val = json.loads(payload)
        if val['type'] == "TIME":
            try:
                #Check the date and time and offset to the right timezone
                dt_texte = datetime.strftime(datetime.strptime(val['value'], "%Y/%m/%d_%H:%M:%S") + TimeZone.utcoffset(None),
                                             "%d/%m/%Y %H:%M:%S")
            except:
                dt_texte = payload
            print(dt_texte)
            # First erase old text
            inkyphat.rectangle((0, date_line, inkyphat.WIDTH, data1_line - 2), fill=white)
            # and draw the new date
            inkyphat.text((0, 31), dt_texte, font=font12, fill=black)
            new_data_flag = True
            update_display()

def on_message_camera(client, userdata, msg):
    """Update display with info from camera (camera shooting new picture or name
    of the last photo)"""

    global new_data_flag

    pl = msg.payload.decode()
    print("{} : {} ({})".format(msg.topic, pl, type(pl)))

    # If a picture is been taken
    if pl == "1" and msg.topic == camera_mqtt_topic + "/shooting" :
        text = "Shooting photo...              "
        inkyphat.rectangle((0, inkyphat.HEIGHT - 12, inkyphat.WIDTH, inkyphat.HEIGHT), fill=white)
        inkyphat.text((0, inkyphat.HEIGHT - 12), text, font=font12, fill=black)
        new_data_flag = True

    #Last picture name (with date/time)
    if msg.topic == camera_mqtt_topic + "/last_photo":
        text = "Last: {}".format(pl)
        new_data_flag = True
        inkyphat.rectangle((0, inkyphat.HEIGHT - 12, inkyphat.WIDTH, inkyphat.HEIGHT), fill=white)
        inkyphat.text((0, inkyphat.HEIGHT - 12), text, font=font12, fill=black)

def on_disconnect(client, userdata, msg):
    """Disconnection callback"""
    if msg != 0:
        print("Unexpected disconnection : {}".format(msg))
    exit()

############
# Function #
############

def update_display():
    global new_data_flag
    global refresh_timer
    global refresh_rate

    if new_data_flag == True:
        if refresh_timer < 0:
            # New data just arrived : wait for other data for a short period
            print("new data...")
            refresh_timer = time.time() + refresh_rate
        elif time.time() >= refresh_timer :
            # No new data to display : update the screen and reset the flags!
            print("Refresh eink screen with new data ! ({} / {})".format(refresh_timer, time.time()))
            inkyphat.show()
            new_data_flag = False
            refresh_timer = -1
            print("OK")
    return

########
# Main #
########

#Screen init.
inkyphat.set_colour("black")

#List of TrueType fonts
font10 =        inkyphat.ImageFont.truetype(epd_font_file, 10)
font11 =        inkyphat.ImageFont.truetype(epd_font_file, 11)
font12 =        inkyphat.ImageFont.truetype(epd_font_file, 12)
font14 =        inkyphat.ImageFont.truetype(epd_font_file, 14)
font16 =        inkyphat.ImageFont.truetype(epd_font_file, 16)
font20 =        inkyphat.ImageFont.truetype(epd_font_file, 20)
font12_bold =   inkyphat.ImageFont.truetype(epd_bold_font_file, 12)
font14_bold =   inkyphat.ImageFont.truetype(epd_bold_font_file, 14)
font16_bold =   inkyphat.ImageFont.truetype(epd_bold_font_file, 16)
font20_bold =   inkyphat.ImageFont.truetype(epd_bold_font_file, 20)
#font12_obl =    inkyphat.ImageFont.truetype(epd_italic_font_file, 12)
#font14_obl =    inkyphat.ImageFont.truetype(epd_italic_font_file, 14)
#font16_obl =    inkyphat.ImageFont.truetype(epd_italic_font_file, 16)

# Lines and fonts
title_line = 1
title_font = font14_bold
hostname_line = 19
hostname_font = font11
date_line = 31
date_font = font12
data1_line = 44
data2_line = 62
data_font = font16_bold
data_table_bottom_line = data2_line + 17

#Draw title in white on black
inkyphat.rectangle((0, 0, inkyphat.WIDTH, 17), fill=grey)
inkyphat.text((60, 1), "Projet Camétéo", font=title_font, fill=white)

#Write hostname and IP address
inkyphat.text((0, 19),
              "{} : {}".format(socket.gethostname(),
                                   netifaces.ifaddresses('wlan0')[2][0]['addr']),
              font = hostname_font,
              fill = black )
# Draw the black lines for data table
inkyphat.line((0, data1_line - 1, inkyphat.WIDTH, data1_line - 1), fill=black)
inkyphat.line((0, data2_line - 1, inkyphat.WIDTH, data2_line - 1), fill=black)
inkyphat.line((0, data_table_bottom_line, inkyphat.WIDTH, data_table_bottom_line), fill=black)
inkyphat.line((inkyphat.WIDTH / 2, data1_line - 1, inkyphat.WIDTH / 2, data_table_bottom_line), fill=black)

#Connect to MQTT broker and loop...
mqtt_client = mqtt.Client(mqtt_client_id, clean_session=True)
mqtt_client.username_pw_set(mqtt_user, mqtt_pass)
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message
mqtt_client.on_disconnect = on_disconnect
#Special callback for camera
mqtt_client.message_callback_add(camera_mqtt_topic + "/#", on_message_camera)

print(mqtt_host + ":" + str(mqtt_port))
mqtt_client.connect(mqtt_host, int(mqtt_port), 60)

refresh_rate = 5
refresh_timer = -1

while True:
    mqtt_client.loop()
    update_display()
