from imgui_bundle import imgui, immapp
from tkinter import filedialog
import os
import re
import shutil
import subprocess

def detect_version(proj_folder) -> str:
    verilog_vnum = None
    sw_ver_filename = os.path.join(proj_folder, "src/sw_ver.v")
    # Example:
    # `define SW_VER  16'h0049
    myregex = re.compile(r"SW_VER\s*16'h([0-9A-Fa-f]{4})")
    try:
        with open(sw_ver_filename,'r') as swfil:
            for lines in swfil.readlines():
                m = myregex.search(lines)
                if m:
                    verilog_vnum = m.group(1)
    except FileNotFoundError:
        pass # verilog_vnum will remain as None

    if verilog_vnum:
        return str(int(verilog_vnum[0:2])) + "." + verilog_vnum[2:3] + "." + verilog_vnum[3:4]
    else:
        return ""
    
def create_bin(appfile, bootfile, outfile) -> str:
    offset = 0x100000
    end_status_text = ""

    with open(bootfile, 'rb') as bfi:
        bootdata = bfi.read()
    with open(appfile, 'rb') as afi:
        appdata = afi.read()

    end_status_text += f"Bootloader: {bootfile}, length: {len(bootdata)}, {hex(len(bootdata))}\n"
    end_status_text += f"Application: {appfile}, length: {len(appdata)}, {hex(len(appdata))}\n"
    end_status_text += f"Offset: {offset}, {hex(offset)}\n"

    if offset < len(bootdata):
        end_status_text = "Error. Offset is less than bootloader file size.\n"
        exit()

    with open(outfile, 'wb') as ofi:
        ofi.write(bootdata)
        ofi.write(bytes([0xFF for _ in range(offset - len(bootdata))]))
        ofi.write(appdata)
        end_status_text += f"Wrote {outfile}, , length: {offset + len(appdata)}, {hex(offset + len(appdata))}\n"

    return end_status_text

def create_fs(appfile, bootfile, outfile) -> str:
    num_bits = 0
    offset = 0x100000
    end_status_text = ""

    with open(outfile, 'w') as ofi:
        with open(bootfile, 'r') as bfi:
            for line in bfi:
                line = line.strip()
                if line.startswith('//'):
                    # comment line, we need to save all of these in the output
                    ofi.write(line+'\n')
                else:
                    # data line. series of 1s and 0s.
                    # all we need to do is copy over to output file, and count the total bits
                    num_bits += len(line)
                    ofi.write(line+'\n')
        extra_bits = offset*8 - num_bits
        end_status_text += f"Bootloader: {bootfile}, length: {int(num_bits/8)}, {hex(int(num_bits/8))}\n"
        end_status_text += f"Offset: {offset}, {hex(offset)}\n"
        if extra_bits < 0:
            end_status_text += "Error. Offset is less than bootloader file size.\n"
        while extra_bits > 512:
            ofi.write('1'*512+'\n')
            extra_bits -= 512
        if extra_bits > 0:
            ofi.write('1'*extra_bits+'\n')
        
        app_num_bits = 0
        # append the application file
        with open(appfile, 'r') as afi:
            for line in afi:
                line = line.strip()
                if line.startswith('//'):
                    # skip all these comment lines
                    pass
                else:
                    ofi.write(line+'\n')
                    app_num_bits += len(line)
            end_status_text += f"Application: {appfile}, length: {int(app_num_bits/8)}, {hex(int(app_num_bits/8))}\n"
        end_status_text += f"Wrote {outfile}, length: {int(((offset*8) + app_num_bits)/8)}, {hex(int(((offset*8) + app_num_bits)/8))}\n"

    return end_status_text
    
class mygui:
    def __init__(self):
        self.project_folder = os.curdir
        self.version_number = detect_version(self.project_folder)
        self.app_folder = "impl/pnr"
        self.app_name = "bs2-USB-Video-Class"
        self.boot_folder = "../bs2_fpga_bootloader/build"
        self.boot_name = "bs2_fpga_bootloader_v0.1.7"
        self.output_name = "eyetracking_firm"
        self.output_folder = "build"
        self.status_text = ""

    def gui(self):
        imgui.separator_text("Options")
        imgui.label_text("Project folder", self.project_folder)
        imgui.same_line()
        if imgui.button("Select Project Dir"):
            self.project_folder = filedialog.askdirectory(initialdir=".",mustexist=True)
            os.chdir(self.project_folder)
        _, self.app_folder = imgui.input_text("App folder", self.app_folder)
        _, self.app_name = imgui.input_text("App name (without extension)", self.app_name)
        _, self.boot_folder = imgui.input_text("Bootloader folder", self.boot_folder)
        _, self.boot_name = imgui.input_text("Bootloader name (without extension)", self.boot_name)
        imgui.set_next_item_width(100)
        _, self.version_number = imgui.input_text("Version string", self.version_number)
        imgui.same_line()
        if imgui.button("Auto-detect from sw_ver.v"):
            self.version_number = detect_version(self.project_folder)
        _, self.output_name = imgui.input_text("Output name (without version or extension)", self.output_name)
        _, self.output_folder = imgui.input_text("Output folder", self.output_folder)
        imgui.separator()
        imgui.separator_text("Output")
        imgui.separator()
        if imgui.button("Create builds"):
            app_full = os.path.join(self.app_folder, self.app_name)
            boot_full = os.path.join(self.boot_folder, self.boot_name)
            out_full = os.path.join(self.project_folder, self.output_folder, self.output_name + "_" + self.version_number)
            out_full_full = os.path.join(self.project_folder, self.output_folder, self.output_name + "_full_" + self.version_number)
            self.status_text += create_bin(app_full+".bin", boot_full+".bin", out_full_full+".bin")
            self.status_text += create_fs(app_full+".fs", boot_full+".fs", out_full_full+".fs")

            shutil.copyfile(app_full +".bin", out_full + ".bin")
            shutil.copyfile(app_full +".bin", out_full + ".dfu")
            shutil.copyfile(app_full + ".fs", out_full + ".fs")
            version_split = self.version_number.split(".")
            if len(version_split) != 3:
                raise Exception("Couldn't split version number into 3 parts")
            version_raw = f"{version_split[0]:>02}{version_split[1]:01}{version_split[2]:01}"
            subprocess.run(["dfu-suffix","-v","35BD","-p","0282","-d",version_raw,"-a",out_full+".dfu"])

        imgui.text_wrapped(self.status_text)



if __name__ == '__main__':

    the_gui = mygui()

    immapp.run(
        gui_function=the_gui.gui,  # The Gui function to run
        window_title="Create Build",  # the window title
        # window_size_auto=True,  # Auto size the application window given its widgets
        window_size=(800,480)
        # Uncomment the next line to restore window position and size from previous run
        # window_restore_previous_geometry==True
    )