import threading

import usb.util
import usb.core
import json
import os
import time
import usb.util
import usb.core
from PIL import ImageDraw, Image
from psutil import disk_partitions
from GPIO_Init import displayImage, getFont
from config import config, savePaths

__author__ = "Hsuan Han Lai (Edward Lai)"
__date__ = "2019-04-02"

workDir = os.path.dirname(os.path.realpath(__file__))

currentStorageStatus = {
    "sampler": 0,
    "synth": 0,
    "drum": 0
}


def is_connected():
    if usb.core.find(idVendor=config["USB_VENDOR"], idProduct=config["USB_PRODUCT"]) is not None:
        return True
    if usb.core.find(idVendor=config["USB_VENDOR"], idProduct=config["OP-Z_USB_PRODUCT"]) is not None:
        return True
    return False


# =========system shell calls to mount and unmount devices=========
def mountdevice(source, target):
    ret = os.system('sudo -E mount {} {}'.format(source, target))
    if ret not in (0, 8192):
        raise RuntimeError("Error mounting {} on {}: {}".format(source, target, ret))


def unmountdevice(target):
    if "op1" in target:
        ret = os.system('umount {}'.format(target))
        if ret != 0:
            raise RuntimeError("Error unmounting {}: {}".format(target, ret))
        os.system("sudo rm -R " + config["OP_1_Mounted_Dir"])
        config["OP_1_Mounted_Dir"] = ""
    else:
        ret = os.system('umount {}'.format(target))
        if ret != 0:
            raise RuntimeError("Error unmounting {}: {}".format(target, ret))
        os.system("sudo rm -R " + config["OP_Z_Mounted_Dir"])
        config["OP_Z_Mounted_Dir"] = ""


def getmountpath(device):
    """
    Search base on device's USB ID
    Then get the system mount path - EX: /dev/sda
    :param device:
    :return:
    """
    o = None
    if device == "OP1":
        o = os.popen('readlink -f /dev/disk/by-id/' + config["OP_1_USB_ID"]).read()
    elif device == "OPZ":
        o = os.popen('readlink -f /dev/disk/by-id/' + config["OP_Z_USB_ID"]).read()
    return o.rstrip()


def getMountPath(device):
    """
    Checks if the partition is mounted if not it return ""
    :param device: Target device being string "OP1" or "OPZ"
    :return: "" is not found
    """
    mountpath = getmountpath(device)
    # mountPoint = ""
    for i, disk in enumerate(disk_partitions()):
        print(disk)
        if disk.device == mountpath:
            mountPoint = disk.mountpoint
            if device == "OP1":
                config["OP_1_Mounted_Dir"] = mountPoint
                print(config["OP_1_Mounted_Dir"])
            elif device == "OPZ":
                config["OP_Z_Mounted_Dir"] = mountPoint
                print(config["OP_Z_Mounted_Dir"])
            return mountPoint
    return ""


def do_mount(device):
    if getMountPath(device) == "":
        try:
            print("-- device not mounted")
            mountpath = getmountpath(device)
            config["USB_Mount_Path"] = mountpath
            print (mountpath)
            if device == "OP1" and mountpath != "":
                print(config["TargetOp1MountDir"])
                # config["TargetOp1MountDir"] = mountpath
                os.system("sudo mkdir -p " + config["TargetOp1MountDir"])
                os.system("sudo chmod 0777 " + config["TargetOp1MountDir"])
                mountdevice(config["USB_Mount_Path"], config["TargetOp1MountDir"])

            elif device == "OPZ" and mountpath != "":
                print(config["TargetOpZMountDir"])
                # config["TargetOp1MountDir"] = mountpath
                os.system("sudo mkdir -p " + config["TargetOpZMountDir"])
                os.system("sudo chmod 0777 " + config["TargetOpZMountDir"])
                mountdevice(config["USB_Mount_Path"], config["TargetOpZMountDir"])
        except:
            return False
        return False
    else:
        return True


# ======== OP1 Mount/Unmount Helper Functions ===========
def check_OP_1_Connection_Silent():
    if is_connected():
        if do_mount("OP1"):
            return True
    else:
        return False


def check_OP_1_Connection():
    connected = Image.new('1', (128, 64))
    draw = ImageDraw.Draw(connected)
    draw.text((0, 25), "Connecting.....", font=getFont(), fill='white')
    displayImage(connected)

    # if is_connected():
    if do_mount("OP1"):
        connected = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(connected)
        draw.text((0, 25), "Connected", font=getFont(), fill='white')
        displayImage(connected)
        return True
    else:
        connected = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(connected)
        draw.text((0, 25), "No Connection!", font=getFont(), fill='white')
        displayImage(connected)
        config["USB_Mount_Path"] = ""
        config["OP_1_Mounted_Dir"] = ""
        time.sleep(1)
        return False


def unmount_OP_1():
    if getMountPath("OP1") != "":
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((30, 25), "Ejecting!", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        unmountdevice(config["OP_1_Mounted_Dir"])
        config["OP_1_Mounted_Dir"] = ""
        config["USB_Mount_Path"] = ""
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((30, 25), "Ejected", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        time.sleep(1)
        return True
    elif os.path.isdir(config["OP_Z_Mounted_Dir"]):
        unmountdevice(config["OP_Z_Mounted_Dir"])
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((15, 25), "Ejected", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        time.sleep(1)
        return True

    else:
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((15, 25), "No Device to Eject", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        time.sleep(1)
        return False


# ============= OP1 Helper tools =================
def get_abbreviation(text):
    """
    Rename texts to abbreviations in order to fit better to the screen
    """
    if text == "Element":
        return "Elem"
    elif text == "Tremolo":
        return "Tremo"
    elif text == "Random":
        return "Rand"
    elif text == "Sampler":
        return "Sample"
    else:
        return text


def getFileCount(startPath):
    """
    For OP1 only
    Given path to dir, and return the counts of .aif files and all child directory
    :param startPath: path to folder
    :return: int: total count of aif files
    """
    filesCount = 0
    for root, dirs, files in os.walk(startPath):
        for f in files:
            if f.endswith('.aif') and not f.startswith("."):
                filesCount += 1
    return filesCount


def analyzeAIF(pathTOAIF):
    """
    For OP1 only
    path to the op1 AIF file extracting json format from the meta data and analyze patch type
    :param pathTOAIF: path to OP1 aif file
    :return: tuple of three strings (type, fx,lfo)
    """
    with open(pathTOAIF, 'rb') as reader:
        file = str(reader.read())
    strBuilder = ""
    startflag = False
    for i in file:
        if i == "}":
            strBuilder += "}"
            break
        if startflag:
            strBuilder += str(i)
        if not startflag and i == "{":
            strBuilder += str(i)
            startflag = True
    data = json.loads(strBuilder)
    return data.get("type").capitalize(), data.get("fx_type").capitalize(), data.get("lfo_type").capitalize()


def update_Current_Storage_Status():
    currentStorageStatus["sampler"] = getFileCount(config["OP_1_Mounted_Dir"] + "/synth")
    currentStorageStatus["synth"] = getFileCount(config["OP_1_Mounted_Dir"] + "/drum")
    currentStorageStatus["drum"] = getFileCount(config["OP_1_Mounted_Dir"] + "/drum")
    return currentStorageStatus["sampler"], currentStorageStatus["synth"], currentStorageStatus["drum"]


# ======== OPZ Mount/Unmount Helper Functions ===========
def check_OP_Z_Connection():
    connected = Image.new('1', (128, 64))
    draw = ImageDraw.Draw(connected)
    draw.text((0, 25), "Connecting.....", font=getFont(), fill='white')
    displayImage(connected)
    if do_mount("OPZ"):
        connected = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(connected)
        draw.text((0, 25), "Connected", font=getFont(), fill='white')
        displayImage(connected)
        time.sleep(1)
        return True
    else:
        connected = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(connected)
        draw.text((0, 25), "No Connection!", font=getFont(), fill='white')
        displayImage(connected)
        config["USB_Mount_Path"] = ""
        time.sleep(1)
        return False


def unmount_OP_Z():
    if getMountPath("OPZ") != "":
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((30, 25), "Ejecting!", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        unmountdevice(config["OP_Z_Mounted_Dir"])
        config["USB_Mount_Path"] = ""
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((30, 25), "Ejected!", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        time.sleep(1)
        return True
    else:
        unmountDisplay = Image.new('1', (128, 64))
        draw = ImageDraw.Draw(unmountDisplay)
        draw.text((15, 25), "No Device to Eject", font=getFont(), fill='white')
        displayImage(unmountDisplay)
        time.sleep(1)
        return False


def autoMountUnmontThread():
    while 1:
        print("Unmount Checking Thread")
        # if config["OP_1_Mounted_Dir"] == "":
        #     if do_mount("OP1"):
        #         print("============ Thread OP1 Mounted ==============")
        #     else:
        #         print("============ Thread Waiting OP1 ==============")
        # if config["OP_Z_Mounted_Dir"] == "":
        #     if do_mount("OPZ"):
        #         print("============ Thread OPZ Mounted ==============")
        #     else:
        #         print("============ Thread Waiting OPZ ==============")
        # Device Mounted check for unmount
        if config["OP_1_Mounted_Dir"] != "":
            if not os.listdir(config["OP_1_Mounted_Dir"]):
                unmountdevice(config["OP_1_Mounted_Dir"])
                config["OP_1_Mounted_Dir"] = ""
                print("============ Thread OP1 Auto Unmount==============")
        if config["OP_Z_Mounted_Dir"] != "":
            if not os.listdir(config["TargetOpZMountDir"]):
                unmountdevice(config["OP_Z_Mounted_Dir"])
                config["OP_z_Mounted_Dir"] = ""
                print("============ Thread OPZ Auto Unmount==============")
        time.sleep(3)