import os
import sys
import re
import time
import shutil
import binascii
import struct
import random
import socket
import threading
import datetime
import hashlib
import argparse
import traceback
import platform
import codecs
from glob import glob
import pylink
from serial import Serial
from Crypto.Util import Counter
from Crypto.Cipher import AES
try:
    from PySide2 import QtCore
    qt_sign = True
except ImportError:
    qt_sign = False
try:
    from serial.tools.list_ports import comports
except ImportError:
    pass
if getattr(sys, 'frozen', False):
    app_path = os.path.dirname(sys.executable)
else:
    app_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
chip_path = os.path.join(app_path, 'chips')
python_version = struct.calcsize('P')*8
if python_version == 64:
    path_dll = os.path.join(app_path, 'utils/jlink', 'JLink_x64.dll')
else:
    path_dll = os.path.join(app_path, 'utils/jlink', 'JLinkARM.dll')
try:
    import changeconf as cgc
    conf_sign = True
except ImportError:
    cgc = None
    conf_sign = False
PY2 = sys.version_info[0] == 2
udp_clinet_dict = {}
udp_send_log = False
udp_log_local_echo = False
udp_socket_server = None
error_code_num = 'FFFF'
error_code_num_task = ['FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF']
local_log_en = True
local_log_data = ''
if conf_sign:
    bflb_error_code = {'0000': 'SUCCESS', '0001': 'FLASH_INIT_ERROR', '0002': 'FLASH_ERASE_PARA_ERROR', '0003': 'FLASH_ERASE_ERROR', '0004': 'FLASH_WRITE_PARA_ERROR', '0005': 'FLASH_WRITE_ADDR_ERROR', '0006': 'FLASH_WRITE_ERROR', '0007': 'FLASH_BOOT_PARA_ERROR', '0008': 'FLASH_SET_PARA_ERROR', '0009': 'FLASH_READ_STATUS_REG_ERROR', '000a': 'FLASH_WRITE_STATUS_REG_ERROR', '000b': 'FLASH_DECOMPRESS_WRITE_ERROR', '000c': 'FLASH_WRITE_XZ_ERROR', '000d': 'FLASH_SWITCH_BANK_ERROR', '0101': 'CMD_ID_ERROR', '0102': 'CMD_LEN_ERROR', '0103': 'CMD_CRC_ERROR', '0104': 'CMD_SEQ_ERROR', '0201': 'IMG_BOOTHEADER_LEN_ERROR', '0202': 'IMG_BOOTHEADER_NOT_LOAD_ERROR', '0203': 'IMG_BOOTHEADER_MAGIC_ERROR', '0204': 'IMG_BOOTHEADER_CRC_ERROR', '0205': 'IMG_BOOTHEADER_ENCRYPT_NOTFIT', '0206': 'IMG_BOOTHEADER_SIGN_NOTFIT', '0207': 'IMG_SEGMENT_CNT_ERROR', '0208': 'IMG_AES_IV_LEN_ERROR', '0209': 'IMG_AES_IV_CRC_ERROR', '020a': 'IMG_PK_LEN_ERROR', '020b': 'IMG_PK_CRC_ERROR', '020c': 'IMG_PK_HASH_ERROR', '020d': 'IMG_SIGNATURE_LEN_ERROR', '020e': 'IMG_SIGNATURE_CRC_ERROR', '020f': 'IMG_SECTIONHEADER_LEN_ERROR', '0210': 'IMG_SECTIONHEADER_CRC_ERROR', '0211': 'IMG_SECTIONHEADER_DST_ERROR', '0212': 'IMG_SECTIONDATA_LEN_ERROR', '0213': 'IMG_SECTIONDATA_DEC_ERROR', '0214': 'IMG_SECTIONDATA_TLEN_ERROR', '0215': 'IMG_SECTIONDATA_CRC_ERROR', '0216': 'IMG_HALFBAKED_ERROR', '0217': 'IMG_HASH_ERROR', '0218': 'IMG_SIGN_PARSE_ERROR', '0219': 'IMG_SIGN_ERROR', '021a': 'IMG_DEC_ERROR', '021b': 'IMG_ALL_INVALID_ERROR', '0301': 'IF_RATE_LEN_ERROR', '0302': 'IF_RATE_PARA_ERROR', '0303': 'IF_PASSWORDERROR', '0304': 'IF_PASSWORDCLOSE', '0401': 'EFUSE_WRITE_PARA_ERROR', '0402': 'EFUSE_WRITE_ADDR_ERROR', '0403': 'EFUSE_WRITE_ERROR', '0404': 'EFUSE_READ_PARA_ERROR', '0405': 'EFUSE_READ_ADDR_ERROR', '0406': 'EFUSE_READ_ERROR', '0407': 'EFUSE_READ_MAC_ERROR', '0408': 'EFUSE_WRITE_MAC_ERROR', '0501': 'MEMORY_WRITE_PARA_ERROR', '0502': 'MEMORY_WRITE_ADDR_ERROR', '0503': 'MEMORY_WRITE_ERROR', '0504': 'MEMORY_READ_PARA_ERROR', '0505': 'MEMORY_READ_ADDR_ERROR', '0506': 'MEMORY_READ_ERROR', '0508': 'REG_WRITE_PARA_ERROR', '0509': 'REG_WRITE_ADDR_ERROR', '050a': 'REG_WRITE_ERROR', '050b': 'REG_READ_PARA_ERROR', '050c': 'REG_READ_ADDR_ERROR', '050d': 'REG_READ_ERROR', '0601': 'ECDH_PARA_ERROR', '0602': 'ECDH_PRIVATE_KEY_ERROR', '0603': 'ECDH_SHARED_KEY_ERROR', '0604': 'ECDH_RANDOM_VAL_ERROR', '0605': 'ECDH_DECRYPT_ERROR', '0606': 'ECDH_ENCRYPT_ERROR', 'fffc': 'PLL_ERROR', 'fffd': 'INVASION_ERROR', 'fffe': 'POLLING', 'ffff': 'FAIL'}
    eflash_loader_error_code = {'0000': 'SUCCESS', '0001': 'EFLASH LOADER SHAKEHAND FAIL', '0002': 'OPTION SET UNSUPPORTED', '0003': 'LOAD HELP BIN FAIL', '0004': 'RESET CPU FAIL', '0005': 'BURN RETRY FAIL', '0006': 'LOG READ FAIL', '0009': 'CONNECT OPENOCD SERVER FAIL', '000A': 'REPEAT BURN', '000B': 'CONFIG FILE NOT FOUND', '000C': 'SET CLOCK PLL FAIL', '000D': 'SET OPT FINISH FAIL', '000E': 'IMPORT PACKET FAIL', '0020': 'EFUSE READ FAIL', '0021': 'EFUSE WRITE FAIL', '0022': 'EFUSE COMPARE FAIL', '0023': 'EFUSE READ MAC ADDR FAIL', '0024': 'EFUSE WRITE MAC ADDR FAIL', '0025': 'EFUSE MAC ADDR CRC FAIL', '0030': 'READ FLASH JEDEC ID FAIL', '0031': 'READ FLASH STATUS REGISTER FAIL', '0032': 'WRITE FLASH STATUS REGISTER FAIL', '0033': 'FLASH CHIP ERASE FAIL', '0034': 'FLASH ERASE FAIL', '0035': 'FLASH READ FAIL', '0036': 'FLASH WRITE FAIL', '0037': 'FLASH WRITE CHECK FAIL', '0038': 'FLASH READ SHA FAIL', '0039': 'FLASH XIP MODE ENTER FAIL', '003A': 'FLASH XIP MODE EXIT FAIL', '003B': 'FLASH SET PARA FAIL', '003C': 'FLASH LOAD FIRMWARE BIN FAIL', '003D': 'FLASH MATCH TYPE FAIL', '003E': 'FLASH LOAD VERIFY FAIL', '003F': 'FLASH BOOT FAIL', '0040': 'FLASH CFG NOT FIT WITH BOOTHEADER', '0041': 'FLASH LOAD ROMFS FAIL', '0042': 'FLASH SWITCH BANK FAIL', '0043': 'FLASH IDENTIFY FAIL', '0044': 'FLASH CHIPERASE CONFLICT WITH SKIP MODE', '0050': 'IMG LOAD SHAKEHAND FAIL', '0051': 'IMG LOAD BOOT CHECK FAIL', '0060': 'IMG CREATE FAIL', '0061': 'IMG FILE NOT SET', '0062': 'IMG ADDR NOT SET', '0063': 'BOOTINFO ADDR NOT SET', '0064': 'AES KEY NOT SET', '0065': 'AES IV  NOT SET', '0066': 'PUBLIC KEY NOT SET', '0067': 'PRIVATE KEY NOT SET', '0068': 'RAW DATA NOT NEED CREATE', '0069': 'FLASH ID NOT SUPPORT', '0070': 'FLASH ID NOT FOUND', '0071': 'ENCRYPT KEY LEN ERROR', '0072': 'AES IV LEN ERROR', '0073': 'AES IV SHOULD END WITH 00000000', '0074': 'IMG TYPE NOT FIT', '0075': 'IMG CREATE ENTER EXCEPT', '0076': 'PT NOT SET', '0077': 'AES KEY DATA OR LEN ERROR', '0078': 'AES IV DATA OR LEN ERROR', '0079': 'FACTORY IMG NOT FOUND', '007A': 'GENERATE ROMFS FAIL', '007B': 'PT PARCEL IS NULL', '007C': 'PT TABLE NOT SET', '007D': 'BOOT2 BIN NOT SET', '007E': 'FW BIN NOT SET', '007F': 'MEDIA NOT SET', '0080': 'ROMFS NOT SET', '0081': 'MFG BIN NOT SET', '0082': 'PT CHECK FAIL', '0083': 'D0 FW BIN NOT SET', '0084': 'IMTB BIN NOT SET', '0085': 'IMG LOADER BIN NOT SET', '0086': 'SBI BIN NOT SET', '0087': 'KERNEL BIN NOT SET', '0088': 'ROOTFS BIN NOT SET', '0089': 'KV BIN NOT SET', '0090': 'YOCBOOT BIN NOT SET', '0091': 'DTB BIN NOT SET', 'FFFF': 'BURN RETRY FAIL'}
else:
    bflb_error_code = {'0000': 'BFLB_SUCCESS', '0001': 'BFLB_FLASH_INIT_ERROR', '0002': 'BFLB_FLASH_ERASE_PARA_ERROR', '0003': 'BFLB_FLASH_ERASE_ERROR', '0004': 'BFLB_FLASH_WRITE_PARA_ERROR', '0005': 'BFLB_FLASH_WRITE_ADDR_ERROR', '0006': 'BFLB_FLASH_WRITE_ERROR', '0007': 'BFLB_FLASH_BOOT_PARA_ERROR', '0008': 'BFLB_FLASH_SET_PARA_ERROR', '0009': 'BFLB_FLASH_READ_STATUS_REG_ERROR', '000a': 'BFLB_FLASH_WRITE_STATUS_REG_ERROR', '000b': 'BFLB_FLASH_DECOMPRESS_WRITE_ERROR', '000c': 'BFLB_FLASH_WRITE_XZ_ERROR', '000d': 'BFLB_FLASH_SWITCH_BANK_ERROR', '0101': 'BFLB_CMD_ID_ERROR', '0102': 'BFLB_CMD_LEN_ERROR', '0103': 'BFLB_CMD_CRC_ERROR', '0104': 'BFLB_CMD_SEQ_ERROR', '0201': 'BFLB_IMG_BOOTHEADER_LEN_ERROR', '0202': 'BFLB_IMG_BOOTHEADER_NOT_LOAD_ERROR', '0203': 'BFLB_IMG_BOOTHEADER_MAGIC_ERROR', '0204': 'BFLB_IMG_BOOTHEADER_CRC_ERROR', '0205': 'BFLB_IMG_BOOTHEADER_ENCRYPT_NOTFIT', '0206': 'BFLB_IMG_BOOTHEADER_SIGN_NOTFIT', '0207': 'BFLB_IMG_SEGMENT_CNT_ERROR', '0208': 'BFLB_IMG_AES_IV_LEN_ERROR', '0209': 'BFLB_IMG_AES_IV_CRC_ERROR', '020a': 'BFLB_IMG_PK_LEN_ERROR', '020b': 'BFLB_IMG_PK_CRC_ERROR', '020c': 'BFLB_IMG_PK_HASH_ERROR', '020d': 'BFLB_IMG_SIGNATURE_LEN_ERROR', '020e': 'BFLB_IMG_SIGNATURE_CRC_ERROR', '020f': 'BFLB_IMG_SECTIONHEADER_LEN_ERROR', '0210': 'BFLB_IMG_SECTIONHEADER_CRC_ERROR', '0211': 'BFLB_IMG_SECTIONHEADER_DST_ERROR', '0212': 'BFLB_IMG_SECTIONDATA_LEN_ERROR', '0213': 'BFLB_IMG_SECTIONDATA_DEC_ERROR', '0214': 'BFLB_IMG_SECTIONDATA_TLEN_ERROR', '0215': 'BFLB_IMG_SECTIONDATA_CRC_ERROR', '0216': 'BFLB_IMG_HALFBAKED_ERROR', '0217': 'BFLB_IMG_HASH_ERROR', '0218': 'BFLB_IMG_SIGN_PARSE_ERROR', '0219': 'BFLB_IMG_SIGN_ERROR', '021a': 'BFLB_IMG_DEC_ERROR', '021b': 'BFLB_IMG_ALL_INVALID_ERROR', '0301': 'BFLB_IF_RATE_LEN_ERROR', '0302': 'BFLB_IF_RATE_PARA_ERROR', '0303': 'BFLB_IF_PASSWORDERROR', '0304': 'BFLB_IF_PASSWORDCLOSE', '0401': 'BFLB_EFUSE_WRITE_PARA_ERROR', '0402': 'BFLB_EFUSE_WRITE_ADDR_ERROR', '0403': 'BFLB_EFUSE_WRITE_ERROR', '0404': 'BFLB_EFUSE_READ_PARA_ERROR', '0405': 'BFLB_EFUSE_READ_ADDR_ERROR', '0406': 'BFLB_EFUSE_READ_ERROR', '0407': 'BFLB_EFUSE_READ_MAC_ERROR', '0408': 'BFLB_EFUSE_WRITE_MAC_ERROR', '0501': 'BFLB_MEMORY_WRITE_PARA_ERROR', '0502': 'BFLB_MEMORY_WRITE_ADDR_ERROR', '0503': 'BFLB_MEMORY_WRITE_ERROR', '0504': 'BFLB_MEMORY_READ_PARA_ERROR', '0505': 'BFLB_MEMORY_READ_ADDR_ERROR', '0506': 'BFLB_MEMORY_READ_ERROR', '0508': 'BFLB_REG_WRITE_PARA_ERROR', '0509': 'BFLB_REG_WRITE_ADDR_ERROR', '050a': 'BFLB_REG_WRITE_ERROR', '050b': 'BFLB_REG_READ_PARA_ERROR', '050c': 'BFLB_REG_READ_ADDR_ERROR', '050d': 'BFLB_REG_READ_ERROR', '0601': 'BFLB_ECDH_PARA_ERROR', '0602': 'BFLB_ECDH_PRIVATE_KEY_ERROR', '0603': 'BFLB_ECDH_SHARED_KEY_ERROR', '0604': 'BFLB_ECDH_RANDOM_VAL_ERROR', '0605': 'BFLB_ECDH_DECRYPT_ERROR', '0606': 'BFLB_ECDH_ENCRYPT_ERROR', 'fffc': 'BFLB_PLL_ERROR', 'fffd': 'BFLB_INVASION_ERROR', 'fffe': 'BFLB_POLLING', 'ffff': 'BFLB_FAIL'}
    eflash_loader_error_code = {'0000': 'BFLB SUCCESS', '0001': 'BFLB EFLASH LOADER SHAKEHAND FAIL', '0002': 'BFLB OPTION SET UNSUPPORTED', '0003': 'BFLB LOAD HELP BIN FAIL', '0004': 'BFLB RESET CPU FAIL', '0005': 'BFLB BURN RETRY FAIL', '0006': 'BFLB LOG READ FAIL', '0009': 'BFLB CONNECT OPENOCD SERVER FAIL', '000A': 'BFLB REPEAT BURN', '000B': 'BFLB CONFIG FILE NOT FOUND', '000C': 'BFLB SET CLOCK PLL FAIL', '000D': 'BFLB SET OPT FINISH FAIL', '000E': 'BFLB IMPORT PACKET FAIL', '0020': 'BFLB EFUSE READ FAIL', '0021': 'BFLB EFUSE WRITE FAIL', '0022': 'BFLB EFUSE COMPARE FAIL', '0023': 'BFLB EFUSE READ MAC ADDR FAIL', '0024': 'BFLB EFUSE WRITE MAC ADDR FAIL', '0025': 'BFLB EFUSE MAC ADDR CRC FAIL', '0030': 'BFLB READ FLASH JEDEC ID FAIL', '0031': 'BFLB READ FLASH STATUS REGISTER FAIL', '0032': 'BFLB WRITE FLASH STATUS REGISTER FAIL', '0033': 'BFLB FLASH CHIP ERASE FAIL', '0034': 'BFLB FLASH ERASE FAIL', '0035': 'BFLB FLASH READ FAIL', '0036': 'BFLB FLASH WRITE FAIL', '0037': 'BFLB FLASH WRITE CHECK FAIL', '0038': 'BFLB FLASH READ SHA FAIL', '0039': 'BFLB FLASH XIP MODE ENTER FAIL', '003A': 'BFLB FLASH XIP MODE EXIT FAIL', '003B': 'BFLB FLASH SET PARA FAIL', '003C': 'BFLB FLASH LOAD FIRMWARE BIN FAIL', '003D': 'BFLB FLASH MATCH TYPE FAIL', '003E': 'BFLB FLASH LOAD VERIFY FAIL', '003F': 'BFLB FLASH BOOT FAIL', '0040': 'BFLB FLASH CFG NOT FIT WITH BOOTHEADER', '0041': 'BFLB FLASH LOAD ROMFS FAIL', '0042': 'BFLB FLASH SWITCH BANK FAIL', '0043': 'BFLB FLASH IDENTIFY FAIL', '0044': 'BFLB FLASH CHIPERASE CONFLICT WITH SKIP MODE', '0050': 'BFLB IMG LOAD SHAKEHAND FAIL', '0051': 'BFLB IMG LOAD BOOT CHECK FAIL', '0060': 'BFLB IMG CREATE FAIL', '0061': 'BFLB IMG FILE NOT SET', '0062': 'BFLB IMG ADDR NOT SET', '0063': 'BFLB BOOTINFO ADDR NOT SET', '0064': 'BFLB AES KEY NOT SET', '0065': 'BFLB AES IV NOT SET', '0066': 'BFLB PUBLIC KEY NOT SET', '0067': 'BFLB PRIVATE KEY NOT SET', '0068': 'BFLB RAW DATA NOT NEED CREATE', '0069': 'BFLB FLASH ID NOT SUPPORT', '0070': 'BFLB FLASH ID NOT FOUND', '0071': 'BFLB ENCRYPT KEY LEN ERROR', '0072': 'BFLB AES IV LEN ERROR', '0073': 'BFLB AES IV SHOULD END WITH 00000000', '0074': 'BFLB IMG TYPE NOT FIT', '0075': 'BFLB IMG CREATE ENTER EXCEPT', '0076': 'BFLB PT NOT SET', '0077': 'BFLB AES KEY DATA OR LEN ERROR', '0078': 'BFLB AES IV DATA OR LEN ERROR', '0079': 'BFLB FACTORY IMG NOT FOUND', '007A': 'BFLB GENERATE ROMFS FAIL', '007B': 'BFLB PT PARCEL IS NULL', '007C': 'BFLB PT TABLE NOT SET', '007D': 'BFLB BOOT2 BIN NOT SET', '007E': 'BFLB FW BIN NOT SET', '007F': 'BFLB MEDIA NOT SET', '0080': 'BFLB ROMFS NOT SET', '0081': 'BFLB MFG BIN NOT SET', '0082': 'BFLB PT CHECK FAIL', '0083': 'D0 FW BIN NOT SET', '0084': 'IMTB BIN NOT SET', '0085': 'IMG LOADER BIN NOT SET', '0086': 'SBI BIN NOT SET', '0087': 'KERNEL BIN NOT SET', '0088': 'ROOTFS BIN NOT SET', '0089': 'KV BIN NOT SET', '0090': 'YOCBOOT BIN NOT SET', 'FFFF': 'BFLB BURN RETRY FAIL'}

def convert_path(path:str) -> str:
    return path.replace('\\/'.replace(os.sep, ''), os.sep)

def printf(*args):
    global local_log_data
    data = ''
    for arg in args:
        data += str(arg)
    if data:
        if conf_sign:
            for (key, value) in cgc.replace_name_list.items():
                data = data.replace(key, value)
        now_time = datetime.datetime.now().strftime('[%H:%M:%S.%f')[:-3] + '] - '
        data = now_time + data
        if local_log_en is True:
            local_log_data += data + '\n'
        else:
            local_log_data = ''
        if udp_send_log:
            tid = str(threading.get_ident())
            if udp_log_local_echo:
                print('[' + tid + ':]' + data.strip())
            try:
                udp_socket_server.sendto((data.strip() + '\r\n').encode('utf-8'), udp_clinet_dict[tid])
            except Exception as e:
                print(e)
        elif qt_sign and QtCore.QThread.currentThread().objectName():
            print('[Task%s]' % str(QtCore.QThread.currentThread().objectName()) + data.strip())
        else:
            print(data.strip())
    sys.stdout.flush()

def local_log_enable(en=False):
    global local_log_en
    if en is True:
        local_log_en = True
    else:
        local_log_en = False

def local_log_save(local_path='log', key_word=''):
    global local_log_data
    log_dir = os.path.join(app_path, local_path)
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)
    if local_log_en is True:
        try:
            rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
            log_name = rq + '_' + key_word + '.log'
            log_path = os.path.join(log_dir, log_name)
            with codecs.open(log_path, 'w', encoding='utf-8') as fp:
                fp.write(local_log_data)
        except Exception as e:
            printf(e)
            traceback.print_exc(limit=5, file=sys.stdout)
    local_log_data = ''

def get_bflb_error_code(code):
    return bflb_error_code[code]

def clear_global():
    global error_code_num, error_code_num_task
    error_code_num = 'FFFF'
    error_code_num_task = []

def set_error_code(num_str, task=None):
    global error_code_num
    if task != None:
        if len(error_code_num_task) == 0:
            for i in range(66):
                error_code_num_task.append('FFFF')
        if error_code_num_task[task] == 'FFFF':
            error_code_num_task[task] = num_str
        if num_str == 'FFFF':
            error_code_num_task[task] = num_str
    else:
        if error_code_num == 'FFFF':
            error_code_num = num_str
        if num_str == 'FFFF':
            error_code_num = num_str

def get_error_code(task=None):
    if task != None:
        return error_code_num_task[task]
    return error_code_num

def errorcode_msg(task=None):
    if task != None:
        return '{"ErrorCode": "' + error_code_num_task[task] + '", "ErrorMsg":"' + eflash_loader_error_code[error_code_num_task[task]] + '"}'
    return '{"ErrorCode": "' + error_code_num + '", "ErrorMsg":"' + eflash_loader_error_code[error_code_num] + '"}'

def get_security_key():
    return (hexstr_to_bytearray('424F554646414C4F4C41424B45594956'), hexstr_to_bytearray('424F554646414C4F4C41424B00000000'))

def hexstr_to_bytearray_b(hexstring):
    return bytearray.fromhex(hexstring)

def hexstr_to_bytearray(hexstring):
    return bytearray.fromhex(hexstring)

def hexstr_to_bytearray_l(hexstring):
    b = bytearray.fromhex(hexstring)
    b.reverse()
    return b

def int_to_2bytearray_l(intvalue):
    return struct.pack('<H', intvalue)

def int_to_2bytearray_b(intvalue):
    return struct.pack('>H', intvalue)

def int_to_4bytearray_l(intvalue):
    src = bytearray(4)
    src[3] = intvalue >> 24 & 255
    src[2] = intvalue >> 16 & 255
    src[1] = intvalue >> 8 & 255
    src[0] = intvalue >> 0 & 255
    return src

def int_to_4bytearray_b(intvalue):
    val = int_to_4bytearray_l(intvalue)
    val.reverse()
    return val

def bytearray_reverse(a):
    length = len(a)
    b = bytearray(length)
    i = 0
    while i < length:
        b[i] = a[length - i - 1]
        i = i + 1
    return b

def bytearray_to_int(b):
    return int(binascii.hexlify(b), 16)

def string_to_bytearray(string):
    return bytes(string, encoding='utf8')

def bytearray_to_str(bytesarray):
    return str(bytesarray)

def get_random_hexstr(n_bytes):
    hextring = ''
    i = 0
    while i < n_bytes:
        hextring = hextring + str(binascii.hexlify(random.randint(0, 255)))
        i = i + 1
    return hextring

def get_crc32_bytearray(data):
    crc = binascii.crc32(data)
    return int_to_4bytearray_l(crc)

def add_to_16(par):
    while len(par) % 16 != 0:
        par += b'\x00'
    return par

def enable_udp_send_log(local_echo):
    global udp_send_log, udp_log_local_echo, udp_socket_server
    udp_send_log = True
    udp_log_local_echo = local_echo
    udp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

def add_udp_client(tid, upd_client):
    udp_clinet_dict[tid] = upd_client

def remove_udp_client(tid):
    del udp_clinet_dict[tid]

def update_cfg(cfg, section, key, value):
    if cfg.has_option(section, key):
        cfg.set(section, key, str(value))

def get_byte_array(string):
    return string.encode('utf-8')

def verify_hex_num(string):
    length = len(string)
    i = 0
    while True:
        if re.match('\\A[0-9a-fA-F]+\\Z', string[i:i + 1]) is None:
            return False
        i += 1
        if i >= length:
            break
    return True

def get_eflash_loader(xtal):
    xtal_suffix = str(xtal).lower().replace('.', 'p').replace('M', 'm').replace('RC', 'rc')
    return 'eflash_loader_' + xtal_suffix + '.bin'

def str_endian_switch(string):
    s = string[6:8] + string[4:6] + string[2:4] + string[0:2]
    return s

def img_create_sha256_data(data_bytearray):
    hashfun = hashlib.sha256()
    hashfun.update(data_bytearray)
    return hexstr_to_bytearray(hashfun.hexdigest())

def img_create_encrypt_data(data_bytearray, key_bytearray, iv_bytearray, flash_img):
    if flash_img == 0:
        cryptor = AES.new(key_bytearray, AES.MODE_CBC, iv_bytearray)
        ciphertext = cryptor.encrypt(data_bytearray)
    else:
        iv = Counter.new(128, initial_value=int(binascii.hexlify(iv_bytearray), 16))
        cryptor = AES.new(key_bytearray, AES.MODE_CTR, counter=iv)
        ciphertext = cryptor.encrypt(data_bytearray)
    return ciphertext

def aes_decrypt_data(data, key_bytearray, iv_bytearray, flash_img):
    if flash_img == 0:
        cryptor = AES.new(key_bytearray, AES.MODE_CBC, iv_bytearray)
        plaintext = cryptor.decrypt(data)
    else:
        iv = Counter.new(128, initial_value=int(binascii.hexlify(iv_bytearray), 16))
        cryptor = AES.new(key_bytearray, AES.MODE_CTR, counter=iv)
        plaintext = cryptor.decrypt(data)
    return plaintext

def open_file(file, mode='rb'):
    fp = open(os.path.join(app_path, file), mode)
    return fp

def copyfile(srcfile, dstfile):
    if os.path.isfile(srcfile):
        (fpath, fname) = os.path.split(dstfile)
        if not os.path.exists(fpath):
            os.makedirs(fpath)
        shutil.copyfile(srcfile, dstfile)
    else:
        printf('Src file not exists')

def get_systype():
    type_ = platform.system().lower()
    arch = platform.machine().lower()
    if type_ == 'windows':
        arch = 'amd64' if platform.architecture()[0] == '64bit' else 'x86'
    if arch:
        return '%s_%s' % (type_, arch)
    return type_

def get_serial_ports():
    try:
        from serial.tools.list_ports import comports
    except ImportError:
        return
    WINDOWS = sys.platform.startswith('win')
    result = []
    for (p, d, h) in comports():
        if not p:
            continue
        if WINDOWS and PY2:
            try:
                d = unicode(d, errors='ignore')
            except TypeError:
                pass
        if 'VID:PID' in h:
            result.append({'port': p, 'description': d, 'hwid': h})
    if not result:
        if 'darwin' in get_systype():
            for p in glob('/dev/tty.*'):
                result.append({'port': p, 'description': 'n/a', 'hwid': 'n/a'})
    return result

def serial_enumerate():
    prog_ports = []
    sdio_ports = []
    sdio_file_ser_dict = {}
    uart_ports = []
    file_dict = {}
    ports = []
    if sys.platform.startswith('win'):
        for (p, d, h) in comports():
            if not ('Virtual' in d or not p) and 'STM' not in d:
                continue
            if 'PID=1D6B' in h.upper():
                ser_value = h.split(' ')[2][4:]
                if ser_value not in sdio_file_ser_dict:
                    sdio_file_ser_dict[ser_value] = p
                elif 'LOCATION' in h.upper():
                    file_dict[sdio_file_ser_dict[ser_value]] = p
                    sdio_ports.append(sdio_file_ser_dict[ser_value] + ' (SDIO)')
                else:
                    file_dict[p] = sdio_file_ser_dict[ser_value]
                    sdio_ports.append(p + ' (SDIO)')
                    if 'FACTORYAIOT_PROG' in h.upper() or 'PID=42BF:B210' in h.upper():
                        prog_ports.append(p + ' (PROG)')
                    else:
                        uart_ports.append(p)
            elif 'FACTORYAIOT_PROG' in h.upper() or 'PID=42BF:B210' in h.upper():
                prog_ports.append(p + ' (PROG)')
            else:
                uart_ports.append(p)
        try:
            uart_ports = sorted(uart_ports, key=lambda x: int(re.match('COM(\\d+)', x).group(1)))
        except Exception:
            uart_ports = sorted(uart_ports)
        ports = sorted(prog_ports) + sorted(sdio_ports) + uart_ports
    elif sys.platform.startswith('linux'):
        for (p, d, h) in comports():
            if not p:
                continue
            if 'PID=1D6B' in h.upper():
                ser_value = h.split(' ')[2][4:]
                if ser_value not in sdio_file_ser_dict:
                    sdio_file_ser_dict[ser_value] = p
                elif sdio_file_ser_dict[ser_value] > p:
                    file_dict[p] = sdio_file_ser_dict[ser_value]
                    sdio_ports.append(p + ' (SDIO)')
                else:
                    file_dict[sdio_file_ser_dict[ser_value]] = p
                    sdio_ports.append(sdio_file_ser_dict[ser_value] + ' (SDIO)')
            elif 'FACTORYAIOT PROG' in h.upper():
                prog_ports.append(p + ' (PROG)')
            else:
                uart_ports.append(p)
        ports = sorted(prog_ports) + sorted(sdio_ports) + sorted(uart_ports)
    elif sys.platform.startswith('darwin'):
        for dev in glob('/dev/tty.usb*'):
            ports.append(dev)
    return ports

def pylink_enumerate():
    try:
        if sys.platform == 'win32':
            obj_dll = pylink.Library(dllpath=path_dll)
            obj = pylink.JLink(lib=obj_dll)
        else:
            obj = pylink.JLink()
    except Exception:
        return []
    else:
        return obj.connected_emulators()

def cklink_openocd_enumerate():
    ports_cklink = []
    ports_openocd = []
    if sys.platform.startswith('win'):
        for (p, d, h) in comports():
            if not p:
                continue
            if 'FACTORYAIOT_PROG' in h.upper():
                match1 = re.search('FACTORYAIOT_PROG_([a-zA-Z0-9]{6}) LOCATION', h.upper(), re.I)
                match2 = re.search('FACTORYAIOT_PROG_([a-zA-Z0-9]{6})$', h.upper(), re.I)
                if match1 is not None:
                    ports_cklink.append(match1.group(1))
                if match2 is not None:
                    ports_openocd.append(match2.group(1))
    elif sys.platform.startswith('linux'):
        for (p, d, h) in comports():
            if not p:
                continue
            if 'FactoryAIOT Prog' in h:
                match1 = re.search('FactoryAIOT Prog ([a-zA-Z0-9]{6}) LOCATION', h, re.I)
                if match1 is not None:
                    ports_cklink.append(match1.group(1))
    return (ports_cklink, ports_openocd)

def image_create_parser_init():
    parser = argparse.ArgumentParser(description='bouffalolab image create command')
    parser.add_argument('--chipname', dest='chipname', help='chip name')
    parser.add_argument('--imgfile', dest='imgfile', help='image file')
    parser.add_argument('--security', dest='security', help='security save efusedata')
    parser.add_argument('-i', '--image', dest='image', help='image type: media or if')
    parser.add_argument('-c', '--cpu', dest='cpu', help='cpu type: cpu0 cpu1 or all')
    parser.add_argument('-g', '--group', dest='group', help='group type')
    parser.add_argument('-s', '--signer', dest='signer', help='signer')
    return parser

def eflash_loader_parser_init():
    parser = argparse.ArgumentParser(description='bouffalolab eflash loader command')
    parser.add_argument('--chipname', dest='chipname', help='chip name')
    parser.add_argument('--chipid', dest='chipid', action='store_true', help='chip id')
    parser.add_argument('--usage', dest='usage', action='store_true', help='display usage')
    parser.add_argument('--flash', dest='flash', action='store_true', help='target is flash')
    parser.add_argument('--efuse', dest='efuse', action='store_true', help='target is efuse')
    parser.add_argument('--ram', dest='ram', action='store_true', help='target is ram')
    parser.add_argument('-w', '--write', dest='write', action='store_true', help='write to flash/efuse')
    parser.add_argument('-e', '--erase', dest='erase', action='store_true', help='erase flash')
    parser.add_argument('-r', '--read', dest='read', action='store_true', help='read from flash/efuse')
    parser.add_argument('-n', '--none', dest='none', action='store_true', help='eflash loader environment init')
    parser.add_argument('-p', '--port', dest='port', help='serial port to use')
    parser.add_argument('-b', '--baudrate', dest='baudrate', type=int, help='the speed at which to communicate')
    parser.add_argument('-c', '--config', dest='config', help='eflash loader config file')
    parser.add_argument('-i', '--interface', dest='interface', help='interface type: uart/jlink/openocd')
    parser.add_argument('--xtal', dest='xtal', help='xtal type')
    parser.add_argument('--start', dest='start', help='start address')
    parser.add_argument('--end', dest='end', help='end address')
    parser.add_argument('--addr', dest='addr', help='address to write')
    parser.add_argument('--mac', dest='mac', help='mac address to write')
    parser.add_argument('--file', dest='file', help='file to store read data or file to write')
    parser.add_argument('--skip', dest='skip', help='skip write file to flash')
    parser.add_argument('--packet', dest='packet', help=' import packet to replace burn file')
    parser.add_argument('--efusefile', dest='efusefile', help='efuse file to write efuse')
    parser.add_argument('--data', dest='data', help='data to write')
    parser.add_argument('--mass', dest='mass', help='load mass bin')
    parser.add_argument('--loadstr', dest='loadstr', help='')
    parser.add_argument('--loadfile', dest='loadfile', help='')
    parser.add_argument('--userarea', dest='userarea', help='user area')
    parser.add_argument('--romfs', dest='romfs', help='romfs data to write')
    parser.add_argument('--csvfile', dest='csvfile', help='csv file contains 3/5 tuples')
    parser.add_argument('--csvaddr', dest='csvaddr', help='address to write for csv file')
    parser.add_argument('--para', dest='para', help='efuse para')
    parser.add_argument('--isp', dest='isp', action='store_true', help='isp config')
    parser.add_argument('--createcfg', dest='createcfg', help='img create cfg file')
    parser.add_argument('--key', dest='key', help='aes key for socket')
    parser.add_argument('--ecdh', dest='ecdh', action='store_true', help='open ecdh function')
    parser.add_argument('--echo', dest='echo', action='store_true', help='open local log echo')
    parser.add_argument('-a', '--auto', dest='auto', action='store_true', help='auto flash')
    parser.add_argument('-v', '--version', dest='version', action='store_true', help='display version')
    return parser

