import threading
import os
from PIL import Image, ImageDraw, ImageFont
from StreamDeck.DeviceManager import DeviceManager
from StreamDeck.ImageHelpers import PILHelper
import paho.mqtt.client as mqtt
import json


# Generates a custom tile with run-time generated text and custom image via the
# PIL module.
def render_key_image(d, icon_filename, font_filename, label_text):
    # Create new key image of the correct dimensions, black background.
    image = PILHelper.create_image(d)
    datadir = os.getcwd() + "/deckimgs/"
    # Resize the source image asset to best-fit the dimensions of a single key,
    # and paste it onto our blank frame centered as closely as possible.
    icon = Image.open(datadir + icon_filename).convert("RGBA")
    icon.thumbnail((image.width, image.height - 20), Image.LANCZOS)
    icon_pos = ((image.width - icon.width) // 2, 0)
    image.paste(icon, icon_pos, icon)

    # Load a custom TrueType font and use it to overlay the key index, draw key
    # label onto the image.
    draw = ImageDraw.Draw(image)
    font = ImageFont.truetype(datadir + font_filename, 14)
    label_w, label_h = draw.textsize(label_text, font=font)
    label_pos = ((image.width - label_w) // 2, image.height - 20)
    draw.text(label_pos, text=label_text, font=font, fill="white")

    return PILHelper.to_native_format(d, image)


# Prints key state change information, updates rhe key image and performs any
# associated actions when a key is pressed.
def key_change_callback(d, key, state):
    # Print new key state
    print("Deck {} Key {} = {}".format(d.id(), key, state), flush=True)
    try:
        outobj["data"]["deckid"] = dnr[d.id()]
        outobj["data"]["key"] = key
        outobj["data"]["pressed"] = state
        jsonstring = json.dumps(outobj)
        client.publish("homeauto/streamdeck/output", jsonstring.encode())
    except:
        print("Error sending data")


def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    client.subscribe("homeauto/streamdeck/control/#")


def on_message(client, userdata, msg):
    # Status Data
    if msg.topic == "homeauto/streamdeck/control/status":
        try:
            data = json.loads(msg.payload.decode())
            for index, d in enumerate(streamdecks):
                if data["deckid"] == dnr[d.id()]:
                    d.set_brightness(data["brightness"])
        except:
            print("Wrong Status data")
    # Config Data write
    if msg.topic == "homeauto/streamdeck/control/config/write":
        try:
            dat = msg.payload.decode()
            writecfg(dat)
            applycfg(json.loads(dat))
        except:
            print("Wrong Config data")
    # Config Data mqtt send
    if msg.topic == "homeauto/streamdeck/control/config/read":
        try:
            cfdata = loadcfg()
            jsonstring = json.dumps(cfdata)
            client.publish("homeauto/streamdeck/currentcfg", jsonstring.encode())
        except:
            print("Config data mqtt send failed!")

def writecfg(jsondata):
    try:
        cfgfile = open("streamdeck.cfg", "w")
        cfgfile.write(jsondata)
        cfgfile.close()
    except:
        print("File write failed!")    

def loadcfg():
    try:
        cfgfile = open("streamdeck.cfg", "r")
        data = cfgfile.read()
        cfgfile.close()
        data = json.loads(data)
        return data
    except:
        print("Error loading config, creating new file...")
        return newcfg()
        
def applycfg(data):
    try:
        font = "Roboto-Regular.ttf"
        for index, d in enumerate(streamdecks):
            for i in range(0, len(data[d.get_serial_number()])):
                imgfile = data[d.get_serial_number()][i]["image"]
                txt = data[d.get_serial_number()][i]["text"]
                image = render_key_image(d, imgfile, font, txt)
                d.set_key_image(i, image)
    except:
        print("Setting config to decks failed!")
        
def newcfg():
    newcfg ={}
    for index, d in enumerate(streamdecks):
        newcfg[d.get_serial_number()] = []
        for k in range(0, 15):
            newcfg[d.get_serial_number()].append({"text": str(k),  "image": "lamp.png"})
    
    try:
        cfgfile = open("streamdeck.cfg", "w")
        cfgfile.write(json.dumps(newcfg))
        cfgfile.close()
    except:
        print("New cfg file write error!")
        
    return newcfg

if __name__ == "__main__":

    client = mqtt.Client()
    #client.username_pw_set(username='user',password='passwort')
    client.connect_async("localhost", 1883, 60)
    client.on_connect = on_connect
    client.on_message = on_message
    client.loop_start()
    outobj = {}
    dnr = {}
    outobj.update({"data": { "deckid": "" , "key": 0, "pressed": False}})

    streamdecks = DeviceManager().enumerate()

    print("Found {} Stream Deck(s).\n".format(len(streamdecks)))

    for index, deck in enumerate(streamdecks):
        deck.open()
        deck.reset()

        print("Opened '{}' device (serial number: '{}')".format(
            deck.deck_type(), deck.get_serial_number()))

        # Set initial screen brightness to 30%.
        deck.set_brightness(30)
        
        dnr.update({ deck.id():  deck.get_serial_number() })
        
        # Register callback function for when a key state changes.
        deck.set_key_callback(key_change_callback)

        # Wait until all application threads have terminated (for this example,
        # this is when all deck handles are closed).
        
    filecfg = loadcfg()
    applycfg(filecfg)
    
    for t in threading.enumerate():
        if t is threading.currentThread():
            continue

        if t.is_alive():
            t.join()
