import tkinter as tk
from tkinter import ttk, messagebox
from ctypes import * 
from ICPDAS_USBIO import *

class AO_API_TestApp:
    def __init__(self, master):
        self.master = master
        self.master.title("AO_API_Test")
        self.master.geometry("1050x510")
        
        self.usbio = USBIO()
        self.i_ao_total = 0
        self.power_on_var = tk.StringVar(value="disable")  # 默认值设为 "disable"
        self.create_widgets()

    def create_widgets(self):
        # Main frame
        main_frame = ttk.Frame(self.master, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # Left column
        left_frame = ttk.Frame(main_frame)
        left_frame.grid(row=0, column=0, sticky=(tk.N, tk.S), padx=(0, 10))

        ttk.Label(left_frame, text="Device ID").grid(row=0, column=0, sticky=tk.W)
        self.device_id = ttk.Combobox(left_frame, values=["USB-2026"])
        self.device_id.grid(row=0, column=1, sticky=tk.W)
        self.device_id.set("USB-2026")

        ttk.Label(left_frame, text="Board ID").grid(row=1, column=0, sticky=tk.W)
        self.board_id = ttk.Entry(left_frame, width=10)
        self.board_id.grid(row=1, column=1, sticky=tk.W)
        self.board_id.insert(0, "1")

        ttk.Button(left_frame, text="Open", command=self.open_device).grid(row=2, column=0, sticky=tk.W)
        ttk.Button(left_frame, text="Close", command=self.close_device).grid(row=2, column=1, sticky=tk.W)

        ttk.Button(left_frame, text="Default", command=self.load_default).grid(row=3, column=0, sticky=tk.W)
        ttk.Button(left_frame, text="Refresh", command=self.refresh_device).grid(row=3, column=1, sticky=tk.W)

        ttk.Label(left_frame, text="Nickname").grid(row=4, column=0, sticky=tk.W)
        self.nickname = ttk.Entry(left_frame, width=10)
        self.nickname.grid(row=4, column=1, sticky=tk.W)
        ttk.Button(left_frame, text="Set", command=self.on_set_nickname).grid(row=5, column=1, sticky=tk.W)

        ttk.Label(left_frame, text="WDT").grid(row=6, column=0, sticky=tk.W)
        self.wdt = ttk.Entry(left_frame, width=10)
        self.wdt.grid(row=6, column=1, sticky=tk.W)
        ttk.Button(left_frame, text="Set", command=self.on_set_wdt_timeout).grid(row=7, column=1, sticky=tk.W)

        self.info_listbox = tk.Listbox(left_frame, width=30, height=10)
        self.info_listbox.grid(row=8, column=0, columnspan=2, sticky=(tk.W, tk.E))

        # Middle column (Getting Function)
        middle_frame = ttk.LabelFrame(main_frame, text="Getting Function")
        middle_frame.grid(row=0, column=1, sticky=(tk.N, tk.S), padx=10)

        get_buttons = [
            ("Support Type Code", self.get_support_type_code),
            ("Type Code", self.get_type_code),
            ("Channel Enable", self.get_channel_enable),
            ("Resolution", self.get_resolution),
            ("Pwr-On Enable", self.get_power_on_enable),
            ("Pwr-On Value", self.get_power_on_value),
            ("Safety Enable", self.get_safety_enable),
            ("Safety Value", self.get_safety_value)
        ]

        for i, (text, command) in enumerate(get_buttons):
            ttk.Button(middle_frame, text=text, command=command).grid(row=i, column=0, pady=5)

        # Right column (Setting Function)
        right_frame = ttk.LabelFrame(main_frame, text="Setting Function")
        right_frame.grid(row=0, column=2, sticky=(tk.N, tk.S))

        # Type code
        type_frame = ttk.LabelFrame(right_frame, text="Type code (txtchannel can be empty for all channel)")
        type_frame.grid(row=0, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(type_frame, text="Type 0x").grid(row=0, column=0, sticky=tk.W)
        self.type_value = ttk.Entry(type_frame, width=10)
        self.type_value.grid(row=0, column=1, sticky=tk.W)

        ttk.Label(type_frame, text="Channel").grid(row=0, column=2, sticky=tk.W)
        self.type_channel = ttk.Entry(type_frame, width=10)
        self.type_channel.grid(row=0, column=3, sticky=tk.W)

        ttk.Button(type_frame, text="Set", command=self.set_type_code).grid(row=0, column=4, sticky=tk.W)

        # Channel enable/disable
        channel_frame = ttk.LabelFrame(right_frame, text="Channel enable/disable")
        channel_frame.grid(row=1, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(channel_frame, text="Mask 0x").grid(row=0, column=0, sticky=tk.W)
        self.channel_enable = ttk.Entry(channel_frame, width=10)
        self.channel_enable.grid(row=0, column=1, sticky=tk.W)

        ttk.Button(channel_frame, text="Set", command=self.set_channel_enable).grid(row=0, column=2, sticky=tk.W)

        # Power-On enable/disable
        power_on_frame = ttk.LabelFrame(right_frame, text="Power-On enable/disable")
        power_on_frame.grid(row=2, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(power_on_frame, text="Channel").grid(row=0, column=0, sticky=tk.W)
        self.power_on_enable_ch = ttk.Entry(power_on_frame, width=10)
        self.power_on_enable_ch.grid(row=0, column=1, sticky=tk.W)

        self.power_on_var = tk.StringVar(value="enable")
        ttk.Radiobutton(power_on_frame, text="Enable", variable=self.power_on_var, value="enable").grid(row=0, column=2, sticky=tk.W)
        ttk.Radiobutton(power_on_frame, text="Disable", variable=self.power_on_var, value="disable").grid(row=0, column=3, sticky=tk.W)

        ttk.Button(power_on_frame, text="Set", command=self.set_power_on).grid(row=0, column=4, sticky=tk.W)

        # Power-On Value
        power_on_value_frame = ttk.LabelFrame(right_frame, text="Power-On Value (txtchannel can be empty for all channel)")
        power_on_value_frame.grid(row=3, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(power_on_value_frame, text="Value 0x").grid(row=0, column=0, sticky=tk.W)
        self.power_on_value = ttk.Entry(power_on_value_frame, width=10)
        self.power_on_value.grid(row=0, column=1, sticky=tk.W)

        ttk.Label(power_on_value_frame, text="Channel").grid(row=0, column=2, sticky=tk.W)
        self.power_on_value_ch = ttk.Entry(power_on_value_frame, width=10)
        self.power_on_value_ch.grid(row=0, column=3, sticky=tk.W)

        ttk.Button(power_on_value_frame, text="Set", command=self.set_power_on_value).grid(row=0, column=4, sticky=tk.W)

        # Safety Enable
        safety_enable_frame = ttk.LabelFrame(right_frame, text="Safety Enable (txtchannel can be empty for all channel)")
        safety_enable_frame.grid(row=4, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(safety_enable_frame, text="Mask 0x").grid(row=0, column=0, sticky=tk.W)
        self.safety_enable_ch = ttk.Entry(safety_enable_frame, width=10)
        self.safety_enable_ch.grid(row=0, column=1, sticky=tk.W)

        ttk.Button(safety_enable_frame, text="Set", command=self.set_safety_enable).grid(row=0, column=2, sticky=tk.W)
        # Safety Value
        safety_value_frame = ttk.LabelFrame(right_frame, text="Safety Value (txtchannel can be empty for all channel)")
        safety_value_frame.grid(row=5, column=0, pady=5, padx=5, sticky=(tk.W, tk.E))

        ttk.Label(safety_value_frame, text="Value 0x").grid(row=0, column=0, sticky=tk.W)
        self.safety_value = ttk.Entry(safety_value_frame, width=10)
        self.safety_value.grid(row=0, column=1, sticky=tk.W)

        ttk.Label(safety_value_frame, text="Channel").grid(row=0, column=2, sticky=tk.W)
        self.safety_value_ch = ttk.Entry(safety_value_frame, width=10)
        self.safety_value_ch.grid(row=0, column=3, sticky=tk.W)

        ttk.Button(safety_value_frame, text="Set", command=self.set_safety_value).grid(row=0, column=4, sticky=tk.W)

    def open_device(self):
        #device_id = self.device_id.get()
        device_id = USBIO_MINPID + self.device_id.current() + 26
        board_id = int(self.board_id.get())
        try:
            self.usbio.OpenDevice(device_id, board_id)  # Adjust as needed
            self.refresh_device()
            messagebox.showinfo("Success", f"Device {device_id} opened successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to open device: {str(e)}")

    def close_device(self):
        try:
            self.usbio.CloseDevice()
            self.info_listbox.delete(0, tk.END)
            messagebox.showinfo("Success", "Device closed successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to close device: {str(e)}")

    def load_default(self):
        try:
            self.usbio.LoadDefault()
            self.refresh_device()
            messagebox.showinfo("Success", "Default settings loaded")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load default settings: {str(e)}")

    def refresh_device(self):
        try:
            self.usbio.RefreshDeviceInfo()
            self.update_info()
        except Exception as e:
            messagebox.showerror("Error", f"Failed to refresh device info: {str(e)}")

    def update_info(self):
        self.info_listbox.delete(0, tk.END)
        try:
            deviceID = ctypes.c_ulong()
            self.usbio.GetDeviceID(ctypes.byref(deviceID))
            device_id=deviceID.value            
            fw_ver = ctypes.c_int()  #c_ushort()
            self.usbio.GetFwVer(ctypes.byref(fw_ver))
            le_fw_ver = int.from_bytes(fw_ver, byteorder='little')
            device_nickname = (ctypes.c_char * USBIO_NICKNAME_LENGTH)()
            self.usbio.GetDeviceNickName(device_nickname)
            nickname = ''.join([chr(i) for i in device_nickname.value]).rstrip('\x00')            
            device_sn = (ctypes.c_char * USBIO_SN_LENGTH)()
            self.usbio.GetDeviceSN(device_sn)
            sn = ''.join([chr(i) for i in device_sn.value]).rstrip('\x00')
            AOtotal_ch =  c_char()
            self.usbio.GetAOTotal(byref(AOtotal_ch))
            self.i_ao_total = ord(AOtotal_ch.value)

            self.info_listbox.insert(tk.END, f"Device ID: {deviceID}")        
            self.info_listbox.insert(tk.END, f"Firmware Version: {le_fw_ver}")
            self.info_listbox.insert(tk.END, f"Nickname: {nickname}")
            self.info_listbox.insert(tk.END, f"Serial Number: {sn}")
            self.info_listbox.insert(tk.END, f"AO Total: {self.i_ao_total}")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to update device info: {str(e)}")

    def get_support_type_code(self):
        try:
            total_sup_type = c_char()
            self.usbio.AO_GetTotalSupportType(byref(total_sup_type)) 
            i_total_sup_type=ord(total_sup_type.value)

            message = f"{i_total_sup_type} supported types for this module.\n"
            if i_total_sup_type > 0:
                sup_type = (c_char*USBIO_MAX_SUPPORT_TYPE)()
                self.usbio.AO_GetSupportTypeCode(sup_type)
                message += "Support types are:\n"
                for i in range(i_total_sup_type):
                 message += f"0x{ord(sup_type[i]):02X}\n"
            messagebox.showinfo("Support Type Codes", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get support type code: {str(e)}")

    def get_type_code(self):
        try:
            #i_ao_total=ord(self.ao_total.value)
            ch_type_code = (c_char*self.i_ao_total)()
            self.usbio.AO_GetTypeCode(ch_type_code)
                    
            message = "Module current channel type:\n"
            for idx in range(self.i_ao_total):
                message += f"Ch{idx}: 0x{ord(ch_type_code[idx]):02X}\n"                
            messagebox.showinfo("Type Codes", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get type code: {str(e)}")

    def get_channel_enable(self):
        try:
            ao_byte_count  = (self.i_ao_total + 7) // 8
            i_byChenable = (c_char * ao_byte_count )()   
            self.usbio.AO_GetChEnable(i_byChenable)
                        
            message = "Channel Enable Status:\n"
            # transfer c_char to c_ubyte
            ubyte_array = cast(i_byChenable, POINTER(c_ubyte))

            for idx in range(ao_byte_count):
                enabled = ubyte_array[idx]
                for bit in range(8):
                    channel = idx * 8 + bit
                    if channel >= self.i_ao_total:
                        break
                    is_enabled = (enabled & (1 << bit)) != 0
                    message += f"Channel {channel} is {'enabled' if is_enabled else 'disabled'}\n"           
            messagebox.showinfo("Channel Enable", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get channel enable status: {str(e)}")

    def get_resolution(self):
        try:
            resolution = (c_char * self.i_ao_total)()           
            self.usbio.AO_GetResolution(resolution)            
            if resolution is None:
                messagebox.showerror("Error", "Failed to get resolution")
            else:
                message = "Module current channel resolution:\n"
                for idx in range(self.i_ao_total):
                    message += f"Ch{idx}: {ord(resolution[idx])}\n"
                messagebox.showinfo("Information", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get resolution: {str(e)}")

    def get_power_on_enable(self):
        try:
            power_on_enable = (c_char * self.i_ao_total)()           
            self.usbio.AO_GetPowerOnEnable(power_on_enable)          
            message = "Power-On Enable Status:\n"
            #for idx, enabled in enumerate(power_on_enable):
            #    message += f"Ch{idx}: {'Enabled' if enabled else 'Disabled'}\n"
            for idx in range(self.i_ao_total):
                    message += f"Ch{idx}: {ord(power_on_enable[idx])}\n"                
            messagebox.showinfo("Power-On Enable", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get power-on enable status: {str(e)}")

    def get_power_on_value(self):
        try:
            AO_power_on_value=(c_float*self.i_ao_total)()
            self.usbio.AO_GetPowerOnValueAnalog(AO_power_on_value)
            message = "Power-On Values:\n"
            for idx, value in enumerate(AO_power_on_value):
                message += f"Ch{idx}: {value}\n"
            messagebox.showinfo("Power-On Values", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get power-on values: {str(e)}")

    def get_safety_enable(self):
        try:            
            ao_byte_count  = (self.i_ao_total + 7) // 8
            i_byChenable = (c_char * ao_byte_count )()   
            self.usbio.AO_GetSafetyEnable(i_byChenable)
                        
            message = "Safety Enable Status:\n"
            # transfer c_char to c_ubyte
            ubyte_array = cast(i_byChenable, POINTER(c_ubyte))

            for idx in range(ao_byte_count):
                enabled = ubyte_array[idx]
                for bit in range(8):
                    channel = idx * 8 + bit
                    if channel >= self.i_ao_total:
                        break
                    is_enabled = (enabled & (1 << bit)) != 0
                    message += f"Channel {channel} is {'enabled' if is_enabled else 'disabled'}\n"                   
            messagebox.showinfo("Safety Enable", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get safety enable status: {str(e)}")

    def get_safety_value(self):
        try:
            AO_safety_value=(c_float*self.i_ao_total)()
            self.usbio.AO_GetSafetyValueAnalog(AO_safety_value)
            message = "Safety Values:\n"
            for idx, value in enumerate(AO_safety_value):
                message += f"Ch{idx}: {value}\n"
            messagebox.showinfo("Safety Values", message)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to get safety values: {str(e)}")

    def set_type_code(self):
        try:
            type_code = int(self.type_value.get(), 16)
            channel = int(self.type_channel.get()) if self.type_channel.get() else None
            if channel is not None:
                self.usbio.AO_SetTypeCode(channel, type_code)
            else:
                type_code_list=(c_char*self.i_ao_total)()
                for i in range(self.i_ao_total):
                    type_code_list[i] = type_code
                self.usbio.AO_SetTypeCodes(type_code_list)
            messagebox.showinfo("Success", "Type code set successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to set type code: {str(e)}")

    def set_channel_enable(self):
        try:
            enable_mask = int(self.channel_enable.get(), 16)              
            #enable_mask_bytes = enable_mask.to_bytes(4, byteorder='little')
            #c_enable_mask = c_char_p(enable_mask_bytes)
            ao_byte_count =(USBIO_AO_MAX_CHANNEL + 7) // 8
            ch_enable = (c_char * ao_byte_count )()             
            for iIdx in range(ao_byte_count ):
                ch_enable[iIdx] = (enable_mask >> (iIdx * 8)) & 0xFF

            self.usbio.AO_SetChEnable(ch_enable)
            messagebox.showinfo("Success", "Channel enable set successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to set channel enable: {str(e)}")

    def set_power_on(self):
        try:
            channel = self.power_on_enable_ch.get()
            is_enable = self.power_on_var.get() == "enable"            
            #i_ao_maxtotal=(self.i_ao_total + 7) // 8
            ch_enable = (c_char * self.i_ao_total)() 
            
            if channel:     
                i_channel = int(channel)                             
                if is_enable:
                    ch_enable[i_channel] =0x1;
                else:
                    ch_enable[i_channel] =0x0;
                        #ch_enable[iIdx] = (ubyte_array[iIdx] & (1 << (i_channel%8))) & 0xFF                          
            else:
               if is_enable:
                    for iIdx in range(self.i_ao_total):
                        ch_enable[iIdx] = 0x1
               else:
                    for iIdx in range(self.i_ao_total):
                        ch_enable[iIdx] =  0x00
            self.usbio.AO_SetPowerOnEnable(ch_enable)
            messagebox.showinfo("Success", "Power-on enable set successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to set power-on enable: {str(e)}")

    def set_safety_enable(self):
        try:           
            enable_mask = int(self.safety_enable_ch.get(), 16)
            ao_byte_count =(USBIO_AO_MAX_CHANNEL + 7) // 8
            ch_enable = (c_char * ao_byte_count )()  
            for iIdx in range(ao_byte_count ):
                ch_enable[iIdx] = (enable_mask >> (iIdx * 8)) & 0xFF

            self.usbio.AO_SetSafetyEnable(ch_enable)
            messagebox.showinfo("Success", "Safety enable set successfully")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to set safety enable: {str(e)}")
    def on_set_nickname(self):
        nickname = self.nickname.get()
        err_code = self.usbio.SetDeviceNickName(nickname.encode())
        if err_code != 0:
            messagebox.showerror("Error", f"[{err_code}]")
        else:
            messagebox.showinfo("Success", "Nickname set successfully")

    def on_set_wdt_timeout(self):
        timeout = int(self.wdt.get())
        err_code = self.usbio.SetSoftWDTTimeout(timeout)
        if err_code != 0:
            messagebox.showerror("Error", f"[{err_code}]")
        else:
            messagebox.showinfo("Success", "WDT timeout set successfully")

    def set_power_on_value(self):
        try:
            value = int(self.power_on_value.get(), 16)  # Convert hex string to integer
            channel = self.power_on_value_ch.get()
            
            if channel:
                channel = int(channel)
                #err_code = self.usbio.AO_SetPowerOnValue(channel, value)
                err_code = self.usbio.AO_SetPowerOnChannelValueDigital(channel, value)
            else:
                # Assuming the function to set all channels is named AO_SetPowerOnValueAll
                # If it's named differently in your USBIO class, please adjust accordingly
                power_on_all=(c_ulong*self.i_ao_total)()
                for iIdx in range(self.i_ao_total):
                    power_on_all[iIdx]=value
                    #err_code = self.usbio.AO_SetPowerOnChannelValueDigital(iIdx, value)
                err_code = self.usbio.AO_SetPowerOnValueDigital(power_on_all)
            if err_code != 0:
                messagebox.showerror("Error", f"Failed to set power-on value. Error code: {err_code}")
            else:
                messagebox.showinfo("Success", "Power-on value set successfully")
        except ValueError:
            messagebox.showerror("Error", "Invalid input. Please enter a valid hexadecimal value for 'Value' and an integer for 'Channel'.")

    def set_safety_value(self):
        try:
            value = int(self.safety_value.get(), 16)  # Convert hex string to integer
            channel = self.safety_value_ch.get()
            
            if channel:
                channel = int(channel)
                err_code = self.usbio.AO_SetSafetyChannelValueDigital(channel, value)
            else:
                Safety_all=(c_ulong*self.i_ao_total)()
                for iIdx in range(self.i_ao_total):
                    Safety_all[iIdx]=value
                    #err_code = self.usbio.AO_SetPowerOnChannelValueDigital(iIdx, value)
                err_code = self.usbio.AO_SetSafetyValueDigital(Safety_all)
            
            if err_code != 0:
                messagebox.showerror("Error", f"Failed to set safety value. Error code: {err_code}")
            else:
                messagebox.showinfo("Success", "Safety value set successfully")
        except ValueError:
            messagebox.showerror("Error", "Invalid input. Please enter a valid hexadecimal value for 'Value' and an integer for 'Channel'.")

if __name__ == "__main__":
    root = tk.Tk()
    app = AO_API_TestApp(root)
    root.mainloop()
