import ac
import acsys
import time
import json
from pyKey import press
from keyboard import is_pressed
from third_party.acdynamicers_sim_info import SimInfo
import threading
from os.path import dirname, exists, join
from os import mkdir
import ctypes
from ctypes import wintypes
import configparser


appName = "ACDynamicERS"
width, height = 365 , 240
simInfo = SimInfo()

config_path = None
configNames = ['hotlap.txt', 'race.txt', 'charge.txt']
configSelected = 0

ers_down = ""
ers_up = ""
recovery_down = ""
recovery_up = ""
ers_zones = []


ers_prev = 0.0
recovery_prev = 0
distance = 0.0
dist = 0.0
timee = time.time()

car = simInfo.static.carModel
track = ac.getTrackName(0) + "_" + ac.getTrackConfiguration(0)

label = None
btn_start, btn_hotlap, btn_race, btn_charge, btn_record = None, None, None, None, None

record = False
start = False
running = False
flag, flagBtn, flagBtnrec, flagBtnMapUp, flagBtnMapDown = False, False, False, False, False


with open(dirname(__file__) + "/keyBindings.json", "r") as d:
    keys = json.load(d)
    start_key = keys['start_key'].lower()
    record_key = keys['record_key'].lower()
    map_up = keys['map_up'].lower()
    map_down = keys['map_down'].lower()
    use_keyboard = keys['use_keyboard']


def get_documents_path():
    CSIDL_PERSONAL = 5  # My Documents 
    SHGFP_TYPE_CURRENT = 0  # Get default value
    buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
    ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf)
    return buf.value


def get_buttons():
    global ers_down, ers_up, recovery_down, recovery_up

    config = configparser.ConfigParser()
    config.read(get_documents_path() + "/Assetto Corsa/cfg/controls.ini")

    delivery_up = config['MGUK_DELIVERY_UP']['KEY'].lower()
    delivery_down = config['MGUK_DELIVERY_DN']['KEY'].lower()
    recovery_up = config['MGUK_RECOVERY_UP']['KEY'].lower()
    recovery_down = config['MGUK_RECOVERY_DN']['KEY'].lower()

    try:
        ers_up = delivery_up.split(" ; ")[1]
        ers_down = delivery_down.split(" ; ")[1]
        recovery_up = recovery_up.split(" ; ")[1]
        recovery_down = recovery_down.split(" ; ")[1]
    except:
        ac.log(appName + ": You have not set up the keyboard keys to control ERS delivery or MGU-H Recovery")


def get_ers():
    return int(simInfo.physics.ersPowerLevel)


def get_recovery():
    return int(simInfo.physics.ersRecoveryLevel)


def get_distance():
    return float(round(simInfo.graphics.normalizedCarPosition*100, 4))


def check_folder():
    path = dirname(__file__) + "/data"
    if not exists(path):
        mkdir(path)
        ac.log(appName + ": Creating folder" + path)
    
    if not exists(path + "/" + track):
        mkdir(path + "/" + track)
        ac.log(appName + ": Creating folder" + path + "/" + track)

    if not exists(path + "/" + track + "/" + car):
        mkdir(path + "/" + track + "/" + car)
        ac.log(appName + ": Creating folder" + path + "/" + track + "/" + car)

    ac.log(appName + ": Using config on: " + path + "/" + track + "/" + car)
    return path + "/" + track + "/" + car


def reload_configs():
    global config_path, ers_zones, configNames, configSelected
    
    ers_zones = []
    
    if config_path is not None:
        try:
            with open(config_path + "/" + configNames[configSelected % len(configNames)], "r") as input_file:
                for i in input_file.readlines():
                    ers, recovery, dist = i.split("#")
                    try:
                        ers_zones.append((float(ers), float(recovery), float(dist)))
                    except ValueError:
                        ac.log(appName + ": Error while converting " + ers + " - " + dist + " to float")
        except FileNotFoundError:
            ac.log(appName + ": no ers config files found")
            
            with open(config_path + "/" + configNames[configSelected % len(configNames)], "w") as input_file:
                input_file.write("")
                reload_configs()
    
    else:
        config_path = check_folder()
        reload_configs()


class Keys(threading.Thread):
    def __init__(self, ers, recovery, label):
        threading.Thread.__init__(self)
        self.ers = int(ers)
        self.recovery = int(recovery)
        self.label = label

    def run(self):
        global running
        self.modify_ers()
        self.modify_recovery()
            
        ac.setText(self.label, "Status: Started")
        running = False
    
    def modify_recovery(self):
        global recovery_up, recovery_down, running
        cur_recovery = get_recovery()
        count = 0
        while count != 10:
            if self.recovery == cur_recovery:
                break

            #if self.recovery > cur_recovery:
            if 11 - cur_recovery + self.recovery < 6:
                # + rec
                press(recovery_up, 0.1)
            
            else:
                # - rec
                press(recovery_down, 0.1)

            count += 1
            cur_recovery = get_recovery()
            time.sleep(.08)
    
    def modify_ers(self):
        global ers_down, ers_up, recovery_up, recovery_down, running
        cur_ers = get_ers()
        count = 0
        while count != 10:
            if self.ers == cur_ers:
                break

            #if self.ers > cur_ers:
            if 6 - cur_ers + self.ers < 3:
                # + ers
                press(ers_up, 0.1)
            
            else:
                # - ers
                press(ers_down, 0.1)

            count += 1
            cur_ers = get_ers()
            time.sleep(.08)


def acMain(ac_version):
    global appWindow, label, ers_prev, btn_start, btn_hotlap, btn_race, btn_charge, btn_record
    
    get_buttons()

    appWindow = ac.newApp(appName)
    ac.setTitle(appWindow, appName)
    ac.setSize(appWindow, width, height)
    
    btn_record = ac.addButton(appWindow, "Not Recording")
    ac.setSize(btn_record, 170, 40)
    ac.setPosition(btn_record, 5, 50)
    ac.setFontAlignment(btn_record, "center")
    ac.setBackgroundColor(btn_record, 1, 0, 0)
    ac.setFontSize(btn_record, 20)
    ac.addOnClickedListener(btn_record, recordListener)
    
    btn_delete = ac.addButton(appWindow, "Delete")
    ac.setPosition(btn_delete, 180, 50)
    ac.setSize(btn_delete, 55, 40)
    ac.setFontAlignment(btn_delete, "center")
    ac.addOnClickedListener(btn_delete, deleteListener)
    
    btn_showHide = ac.addButton(appWindow, "Hide")
    ac.setPosition(btn_showHide, 260, 50)
    ac.setSize(btn_showHide, 100, 40)
    ac.setFontAlignment(btn_showHide, "center")
    ac.addOnClickedListener(btn_showHide, showHideListener)
    
    btn_hotlap = ac.addButton(appWindow, "HOTLAP")
    ac.setPosition(btn_hotlap, 5, 100)
    ac.setSize(btn_hotlap, 115, 60)
    ac.setFontAlignment(btn_hotlap, "center")
    ac.setFontSize(btn_hotlap, 25)
    ac.addOnClickedListener(btn_hotlap, hotlapListener)
    
    btn_race = ac.addButton(appWindow, "RACE")
    ac.setPosition(btn_race, 125, 100)
    ac.setSize(btn_race, 115, 60)
    ac.setFontAlignment(btn_race, "center")
    ac.setFontSize(btn_race, 25)
    ac.addOnClickedListener(btn_race, raceListener)
    
    btn_charge = ac.addButton(appWindow, "CHARGE")
    ac.setPosition(btn_charge, 245, 100)
    ac.setSize(btn_charge, 115, 60)
    ac.setFontAlignment(btn_charge, "center")
    ac.setFontSize(btn_charge, 25)
    ac.addOnClickedListener(btn_charge, chargeListener)
    
    btn_start = ac.addButton(appWindow, "Stopped")
    ac.setPosition(btn_start, 5, 170)
    ac.setSize(btn_start, 355, 30)
    ac.setFontAlignment(btn_start, "center")
    ac.setBackgroundColor(btn_start, 1, 0, 0)
    ac.setFontSize(btn_start, 20)
    ac.addOnClickedListener(btn_start, startListener)

    label = ac.addLabel(appWindow, "Status: Stopped")
    ac.setPosition(label, 5, 210)
    
    ac.setBackgroundOpacity(btn_charge, .5)
    ac.setBackgroundOpacity(btn_race, .5)
    ac.setBackgroundOpacity(btn_hotlap, .5)
    
    hotlapListener(None, None)

    return appName


def updateConfigs():
    global label, start, flag, record, configNames, configSelected
    
    old_start = start
    record = False
    start = False
    flag = False

    reload_configs()
    if old_start:
        startListener(None, None)
    ac.setText(label, "Status: config updated")
    ac.log(appName + ": Using config " + configNames[configSelected % len(configNames)])


def resetBgColor():
    global btn_charge, btn_hotlap, btn_hotlap
    ac.setBackgroundColor(btn_hotlap, .5, .5, .5)
    ac.setBackgroundColor(btn_race, .5, .5, .5)
    ac.setBackgroundColor(btn_charge, .5, .5, .5)


def hotlapListener(a, b):
    global configNames, btn_hotlap, btn_race, btn_charge, configSelected
    resetBgColor()
    ac.setBackgroundColor(btn_hotlap, .5, 1, .5)
    configSelected = 0
    updateConfigs()


def raceListener(a, b):
    global configNames, btn_hotlap, btn_race, btn_charge, configSelected
    resetBgColor()
    ac.setBackgroundColor(btn_race, .5, 1, .5)
    configSelected = 1
    updateConfigs()
    
    
def chargeListener(a, b):
    global configNames, btn_hotlap, btn_race, btn_charge, configSelected
    resetBgColor()
    ac.setBackgroundColor(btn_charge, .5, 1, .5)
    configSelected = 2
    updateConfigs()


def showHideListener(a, b):
    global appWindow
    ac.setVisible(appWindow, 0)


def startListener(a, b):
    global start, label, btn_start, btn_record, record
    start = not start
    record = False
    reload_configs()
    ac.setBackgroundColor(btn_start, .5, .5, .5)
    if start:
        ac.setText(label, "Status: Started")
        ac.setText(btn_start, "Started")
        ac.setBackgroundColor(btn_start, 0, 1, 0)
        ac.setBackgroundColor(btn_record, .5, .5, .5)
        ac.setBackgroundColor(btn_record, 1, 0, 0)
        ac.setText(btn_record, "Not Recording")
        ac.log(appName + ": Started")
    
    else:
        ac.setText(btn_start, "Stoped")
        ac.setText(label, "Status: Stopped")
        ac.setBackgroundColor(btn_start, 1, 0, 0)
        ac.log(appName + ": Stopped")


def deleteListener(a, b):
    global label, config_path, start, flag, record, configNames, configSelected
    record = False
    start = False
    flag = False

    with open(config_path + "/"+configNames[configSelected % len(configNames)], "w") as input_file:
        input_file.write("")
    ac.setText(label, "Status: config deleted")
    ac.log(appName + ": config deleted")


def recordListener(a, b):
    global record, label, ers_prev, start, flag, config_path, configNames, btn_record, configSelected
    
    if record:
        record = False
        ac.setBackgroundColor(btn_record, .5, .5, .5)
        ac.setBackgroundColor(btn_record, 1, 0, 0)
        ac.setText(btn_record, "Not Recording")
        reload_configs()
        ac.setText(label, "Status: recording stopped")
        ac.log(appName + ": recording stopped")
    
    else:
        ers_prev = get_ers()
        config_path = check_folder()
        with open(config_path + "/"+configNames[configSelected % len(configNames)], "w") as input_file:
            input_file.write("")

        record = True
        if start:
            startListener(None, None)
        flag = False
        ac.setBackgroundColor(btn_record, .5, .5, .5)
        ac.setBackgroundColor(btn_record, 0, 1, 0)
        ac.setText(btn_record, "Recording")
        ac.setText(label, "Status: recording started")
        ac.log(appName + ": recording started")


def acUpdate(deltaT):
    global record, ers_prev, timee, car, track, label, flag, start, distance, config_path , ers_zones, dist, running, start_key, flagBtn, recovery_prev, configNames, record_key, flagBtnrec
    global use_keyboard, flagBtnMapDown, flagBtnMapUp, configSelected

    ers = get_ers()
    recovery = get_recovery()
    distance = get_distance()
    
    if use_keyboard:
        if is_pressed(start_key) and not flagBtn:
            startListener(None, None)
            flagBtn = True
        elif not is_pressed(start_key) and flagBtn:
            flagBtn = False
        
        
        if is_pressed(record_key) and not flagBtnrec:
            recordListener(None, None)
            flagBtnrec = True
        elif not is_pressed(record_key) and flagBtnrec:
            flagBtnrec = False
        
        
        if is_pressed(map_up) and not flagBtnMapUp:
            configSelected += 1
            if configSelected % 3 == 0:
                hotlapListener(None, None)
            elif configSelected % 3 == 1:
                raceListener(None, None)
            else:
                chargeListener(None, None)
            flagBtnMapUp = True
        elif not is_pressed(map_up) and flagBtnMapUp:
            flagBtnMapUp = False
        
        
        if is_pressed(map_down) and not flagBtnMapDown:
            configSelected -= 1
            if configSelected % 3 == 0:
                hotlapListener(None, None)
            elif configSelected % 3 == 1:
                raceListener(None, None)
            else:
                chargeListener(None, None)
            flagBtnMapDown = True
        elif not is_pressed(map_down) and flagBtnMapDown:
            flagBtnMapDown = False

            

    if (ers_prev != ers or recovery_prev != recovery) and record:
        ers_prev = ers
        recovery_prev = recovery
        timee = time.time()
        dist = distance
        flag = True
        ac.setText(label, "Status: Ers set to " + str(ers) + ", recovery set to " + str(recovery) + " at " + str(dist))


    else:
        if time.time() - timee >= 3 and record and flag:
            flag = False
            if config_path is not None:
                with open(config_path + "/" + configNames[configSelected % len(configNames)], "a+") as input_file:
                    input_file.write(str(ers) + "#" + str(recovery) + "#" + str(dist) + "\n")
            
            else:
                config_path = check_folder()
                with open(config_path + "/" + configNames[configSelected % len(configNames)], "a+") as input_file:
                    input_file.write(str(ers) + "#" + str(recovery) + "#" + str(dist) + "\n")

            ac.log(appName + ": ERS saved")
            ac.setText(label, "Status: Ers saved " + str(ers) + " at " + str(dist))
    
    if start and not record and len(ers_zones) > 0:
        for ers, recovery, distt in ers_zones:
            if distance-0.1 <= distt <= distance+0.1 and simInfo.physics.speedKmh > 0 and not running:
                running = True
                Keys(ers, recovery, label).start()
                ac.setText(label, "Status: changed Ers to " + str(ers))
