import wx
import os

from wx.core import SL_LEFT, SYS_CAN_DRAW_FRAME_DECORATIONS
from log_utils import LogUtils
from settings import Settings
from pynrfjprog import LowLevel, Hex
import threading


class Programmer(wx.Panel):
    '''利用pynrfjprog烧录固件'''
    def __init__(self, parent):
        super(Programmer, self).__init__(parent)

        self.api = None
        self.snr = None

        self.settings = Settings()

        self._init_ui()

        # 实例化log模块
        self.logger = LogUtils.sharedInstance('2')

        #ports = list(serial.tools.list_ports.comports())
        #for p in ports:
        #    print(p.description)

        self._start_thread()

    def _init_ui(self):
        # 最底层水平布局vbox
        self.vbox = wx.BoxSizer(wx.VERTICAL)

        #------------------------------------------------
        # 文件加载框部件布局
        self.file_sbox = wx.StaticBox(self,
                                      size=self.settings.programmer_file_size)
        self.file_sbox_sizer = wx.StaticBoxSizer(self.file_sbox, wx.VERTICAL)

        self.file_gbs = wx.GridBagSizer(4, 16)
        # 子部件
        # line 1
        self.cb_0 = wx.CheckBox(self.file_sbox)
        self.file_path_0 = wx.TextCtrl(self.file_sbox, style=wx.TE_READONLY)
        self.file_load_0 = wx.Button(self.file_sbox, label="...")
        # line 2
        self.cb_1 = wx.CheckBox(self.file_sbox)
        self.file_path_1 = wx.TextCtrl(self.file_sbox, style=wx.TE_READONLY)
        self.file_load_1 = wx.Button(self.file_sbox, label="...")
        # line 3
        self.cb_2 = wx.CheckBox(self.file_sbox)
        self.file_path_2 = wx.TextCtrl(self.file_sbox, style=wx.TE_READONLY)
        self.file_load_2 = wx.Button(self.file_sbox, label="...")
        # line 4
        self.cb_3 = wx.CheckBox(self.file_sbox)
        self.file_path_3 = wx.TextCtrl(self.file_sbox, style=wx.TE_READONLY)
        self.file_load_3 = wx.Button(self.file_sbox, label="...")
        # line5
        self.file_clear_btn = wx.Button(self, label='Clear')

        self.file_gbs.Add(self.cb_0,
                          pos=(0, 0),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_path_0,
                          pos=(0, 1),
                          span=(1, 15),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_load_0,
                          pos=(0, 16),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.cb_1,
                          pos=(1, 0),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_path_1,
                          pos=(1, 1),
                          span=(1, 15),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_load_1,
                          pos=(1, 16),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.cb_2,
                          pos=(2, 0),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_path_2,
                          pos=(2, 1),
                          span=(1, 15),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_load_2,
                          pos=(2, 16),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.cb_3,
                          pos=(3, 0),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_path_3,
                          pos=(3, 1),
                          span=(1, 15),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_load_3,
                          pos=(3, 16),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)
        self.file_gbs.Add(self.file_clear_btn,
                          pos=(4, 16),
                          flag=wx.EXPAND | wx.ALL,
                          border=self.settings.programmer_file_item_border)

        self.file_gbs.AddGrowableRow(0)

        self.file_sbox_sizer.Add(self.file_gbs)
        #------------------------------------------------

        #------------------------------------------------
        # 按键绑定
        self.file_load_0.Bind(wx.EVT_BUTTON, self._file_load_0_btn_handler)
        self.file_load_1.Bind(wx.EVT_BUTTON, self._file_load_1_btn_handler)
        self.file_load_2.Bind(wx.EVT_BUTTON, self._file_load_2_btn_handler)
        self.file_load_3.Bind(wx.EVT_BUTTON, self._file_load_3_btn_handler)
        self.file_clear_btn.Bind(wx.EVT_BUTTON, self._file_clear_btn_handler)
        #------------------------------------------------

        #------------------------------------------------
        # 文件加载框部件布局
        self.setting_sbox = wx.StaticBox(
            self, size=self.settings.programmer_setting_size)
        self.setting_sbox_sizer = wx.StaticBoxSizer(self.setting_sbox,
                                                    wx.VERTICAL)

        self.setting_gbs = wx.GridBagSizer(6, 16)
        # 子部件
        self.erase_all_btn = wx.Button(
            self.setting_sbox,
            label='Erase all',
            size=self.settings.programmer_setting_item_size)
        self.erase_and_write_btn = wx.Button(
            self.setting_sbox,
            label="Erase and Write",
            size=self.settings.programmer_setting_item_size)
        self.write_btn = wx.Button(
            self.setting_sbox,
            label='Write',
            size=self.settings.programmer_setting_item_size)
        self.read_btn = wx.Button(
            self.setting_sbox,
            label='Read',
            size=self.settings.programmer_setting_item_size)
        self.reset_btn = wx.Button(
            self.setting_sbox,
            label='Reset',
            size=self.settings.programmer_setting_item_size)

        self.target_title = wx.StaticText(
            self.setting_sbox,
            label='Target: ',
            style=wx.ALIGN_LEFT,
            size=self.settings.programmer_setting_item_size)
        lst = ['None']
        self.target_cbo = wx.ComboBox(
            self.setting_sbox,
            choices=lst,
            value=lst[0],
            style=wx.TE_PROCESS_ENTER,
            size=self.settings.programmer_setting_item_size)
        self.mergehex_btn = wx.Button(
            self.setting_sbox,
            label='MergeHex',
            size=self.settings.programmer_setting_item_size)

        self.setting_gbs.Add(
            self.erase_all_btn,
            pos=(0, 0),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.erase_and_write_btn,
            pos=(1, 0),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.write_btn,
            pos=(2, 0),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.read_btn,
            pos=(3, 0),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.target_title,
            pos=(0, 2),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.target_cbo,
            pos=(1, 2),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.reset_btn,
            pos=(2, 2),
            border=self.settings.programmer_setting_item_border)
        self.setting_gbs.Add(
            self.mergehex_btn,
            pos=(3, 2),
            border=self.settings.programmer_setting_item_border)

        self.setting_sbox_sizer.Add(self.setting_gbs)
        #------------------------------------------------

        #------------------------------------------------
        # 按键绑定
        self.erase_all_btn.Bind(wx.EVT_BUTTON, self._erase_all_btn_handler)
        self.erase_and_write_btn.Bind(wx.EVT_BUTTON,
                                      self._erase_and_write_btn_handler)
        self.write_btn.Bind(wx.EVT_BUTTON, self._write_btn_handler)
        self.read_btn.Bind(wx.EVT_BUTTON, self._read_btn_handler)
        self.reset_btn.Bind(wx.EVT_BUTTON, self._reset_btn_handler)
        self.mergehex_btn.Bind(wx.EVT_BUTTON, self._mergehex_btn_handler)

        # 连接设备后按键才激活
        self.erase_all_btn.Disable()
        self.erase_and_write_btn.Disable()
        self.write_btn.Disable()
        self.read_btn.Disable()
        self.reset_btn.Disable()
        self.mergehex_btn.Disable()
        #------------------------------------------------

        # 下拉列表更新绑定
        self.target_cbo.Bind(wx.EVT_COMBOBOX_DROPDOWN,
                             self._target_cb_dropdown_handler)
        self.target_cbo.Bind(wx.EVT_COMBOBOX, self._target_cb_selected_handler)

        self.vbox.Add(self.file_sbox_sizer)
        self.vbox.Add(self.setting_sbox_sizer)
        self.SetSizerAndFit(self.vbox)

        #print(self.file_sbox.GetSize())
        #print(self.setting_sbox.GetSize())

    def _start_thread(self):
        '''检测Jlink线程'''
        self.thread = threading.Thread(target=self._jlink_thread)
        self.thread.setDaemon(1)
        self.thread.start()

    def _stop_thread(self):
        if self.thread is not None:
            self.thread.join()
            self.thread = None

    def _jlink_thread(self):
        '''读取jlink属性'''
        self.api = LowLevel.API(LowLevel.DeviceFamily.UNKNOWN)
        self.api.open()
        self.logger.info('Target: ')
        self.logger.info(self.api.dll_version())
        self.logger.info(self.api.find_jlink_path())
        snrs = self.api.enum_emu_snr()
        self.logger.info(snrs)
        if snrs == None:
            self.target_cbo.Clear()
            self.target_cbo.SetValue('None')
            # 连接设备后按键才激活
            self.erase_all_btn.Disable()
            self.erase_and_write_btn.Disable()
            self.write_btn.Disable()
            self.read_btn.Disable()
            self.reset_btn.Disable()
        else:
            self.target_cbo.Clear()
            self.target_cbo.SetValue('Select target')
            for i in range(0, len(snrs)):
                self.target_cbo.Append(str(snrs[i]))
        #self.api.close()

    def _target_cb_dropdown_handler(self, e):
        self._jlink_thread()

    def _target_cb_selected_handler(self, e):
        self.logger.info('Target: ' + self.target_cbo.GetValue())
        self.snr = int(self.target_cbo.GetValue())
        if self.api is not None:
            self.api.connect_to_emu_with_snr(self.snr)
            device_family = self.api.read_device_family()
            self.logger.info('Family: ' + device_family)
            self.api.select_family(device_family)
            #device_version = self.api.read_device_version()
            #self.logger.info('Device version: ' + device_version)
            device_info = self.api.read_device_info()
            for i in range(0, len(device_info)):
                self.logger.info(device_info[i])

            # 激活按键
            self.erase_all_btn.Enable()
            self.erase_and_write_btn.Enable()
            self.write_btn.Enable()
            self.read_btn.Enable()
            self.reset_btn.Enable()

    #------------------------------------------------
    def _load_file(self):
        return wx.FileDialog(
            self,
            "Open",
            ".",
            "",
            "hex file (*.hex)|*.hex",
            wx.FD_OPEN | wx.FD_FILE_MUST_EXIST,
        )

    #------------------------------------------------
    def _erase_all_btn_handler(self, e):
        if self.api is not None:
            self.logger.info('Erase all ...')
            result = self.api.erase_all()
            if result is None:
                self.logger.info('Erase done.')
            else:
                self.logger.info("erase failed. err_code: %d" % (result))

    def _erase_and_write_btn_handler(self, e):
        if self.api is not None:
            #-----------------------------------------
            # erase all
            self.logger.info('Erase all ...')
            result = self.api.erase_all()
            if result is None:
                self.logger.info('Erase done.')
            else:
                self.logger.info("erase failed. err_code: %d" % (result))
                return

            #-----------------------------------------
            # write all
            if self.file_path_0.GetValue() and self.cb_0.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_0.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_0.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 0 NULL, write failed.')

            if self.file_path_1.GetValue() and self.cb_1.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_1.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_1.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 1 NULL, write failed.')

            if self.file_path_2.GetValue() and self.cb_2.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_2.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_2.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 2 NULL, write failed.')

            if self.file_path_3.GetValue() and self.cb_3.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_3.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_3.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 3 NULL, write failed.')

        else:
            self.logger.info('Please check the target!!!')

    def _write_btn_handler(self, e):
        if self.api is not None:
            # 检测是否有选中固件
            # 检测是否有加载文件
            if self.file_path_0.GetValue() and self.cb_0.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_0.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_0.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 0 NULL.')

            if self.file_path_1.GetValue() and self.cb_1.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_1.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_1.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 1 NULL.')

            if self.file_path_2.GetValue() and self.cb_2.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_2.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_2.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 2 NULL.')

            if self.file_path_3.GetValue() and self.cb_3.GetValue():
                self.logger.info('# Parsing hex file into segments.')
                app_program = Hex.Hex(self.file_path_3.GetValue())
                self.logger.info("# Writing %s to device." %
                                 self.file_path_3.GetValue())
                for segment in app_program:
                    self.logger.info('# write address: 0x%X' % segment.address)
                    self.api.write(segment.address, segment.data, True)
                self.logger.info('# write OK.')
            else:
                self.logger.info('Item 3 NULL.')

        else:
            self.logger.info('Please check the target!!!')

    def _read_btn_handler(self, e):
        self.logger.info('Todo')

    def _reset_btn_handler(self, e):
        self.logger.info('Todo')

    def _mergehex_btn_handler(self, e):
        self.logger.info('Todo')

    #------------------------------------------------
    def _file_load_0_btn_handler(self, e):
        file = self._load_file()
        file.ShowModal()
        self.logger.info('Path: ' + file.GetPath())
        self.file_path_0.SetValue(file.GetPath())
        file.Destroy()

    def _file_load_1_btn_handler(self, e):
        file = self._load_file()
        file.ShowModal()
        self.logger.info('Path: ' + file.GetPath())
        self.file_path_1.SetValue(file.GetPath())
        file.Destroy()

    def _file_load_2_btn_handler(self, e):
        file = self._load_file()
        file.ShowModal()
        self.logger.info('Path: ' + file.GetPath())
        self.file_path_2.SetValue(file.GetPath())
        file.Destroy()

    def _file_load_3_btn_handler(self, e):
        file = self._load_file()
        file.ShowModal()
        self.logger.info('Path: ' + file.GetPath())
        self.file_path_3.SetValue(file.GetPath())
        file.Destroy()

    def _file_clear_btn_handler(self, e):
        self.file_path_0.Clear()
        self.file_path_1.Clear()
        self.file_path_2.Clear()
        self.file_path_3.Clear()
        self.cb_0.SetValue(0)
        self.cb_1.SetValue(0)
        self.cb_2.SetValue(0)
        self.cb_3.SetValue(0)
        self.logger.info('All clear.')
