'''

History:




'''


# import xl filel open / read module
import openpyxl
import time
import os
import shutil

# Open output file in binary write mode
binary_file = open("nvmdata_without_crc.bin", "wb")
  
# load excel with its path
nvm_wrkbk = openpyxl.load_workbook("obc_nvm_parameters.xlsx",data_only=True)

# Variable declaration
nvm_sheet = nvm_wrkbk.active

tmp_arr=[]
tmp_str1=""


software_version   = nvm_sheet.cell(row = 3, column = 3)
sft_ver = software_version.value
#print(sft_ver)
parts = sft_ver.split(".")
#print(parts[0])
#print(parts[1])
sft_major = int(parts[0])
sft_minor = int(parts[1])
print("NVM version: " , sft_major , "." ,sft_minor)
#print(sft_minor)

byte_length   = nvm_sheet.cell(row = 2, column = 3)
blength = int(byte_length.value)
print("Default data byte length:" ,blength)

nvm_default_values = bytearray(b'\xEF')
nvm_default_values.extend(sft_major.to_bytes(1,'big'))
nvm_default_values.extend(sft_minor.to_bytes(1,'big'))
nvm_default_values.extend(blength.to_bytes(4,'big'))

#nvm_bin_header = bytearray(b'\xEF')
#nvm_bin_header.extend(sft_major.to_bytes(1,'big'))
#nvm_bin_header.extend(sft_minor.to_bytes(1,'big'))
#nvm_bin_header.extend(blength.to_bytes(4,'big'))

xor =   nvm_default_values[0] ^ nvm_default_values[1]  
xor =   xor ^ nvm_default_values[2]  
xor =   xor ^ nvm_default_values[3]  
xor =   xor ^ nvm_default_values[4]  
xor =   xor ^ nvm_default_values[5]  
xor =   xor ^ nvm_default_values[6]  

nvm_default_values.extend(xor.to_bytes(1,'big'))


time.sleep(1)



def ft_Crc32(Crc, Data):
    index = 0

    Crc = Crc ^ Data
    for index in range(32):
        if (Crc & 0x80000000):
            Crc = (Crc << 1) ^ 0x104C11DB7  # Polynomial used in STM32
        else:
            Crc = (Crc << 1)

    return Crc




def calc_checksum_bulkdata(data_buf, length):
    read_buffer = 0xFFFFFFFF
    calc_CRC = 0xFFFFFFFF
    buf_inc = 0

    # Checking if the file exists or not
    if data_buf is None:
        return 0

    while length > 3:
        read_buffer = int.from_bytes(data_buf[buf_inc:buf_inc+4], byteorder='little')
        calc_CRC = ft_Crc32(calc_CRC, read_buffer)
        length -= 4
        buf_inc += 4

    return calc_CRC


def reflect_data(x, width):
    # See: https://stackoverflow.com/a/20918545
    if width == 8:
        x = ((x & 0x55) << 1) | ((x & 0xAA) >> 1)
        x = ((x & 0x33) << 2) | ((x & 0xCC) >> 2)
        x = ((x & 0x0F) << 4) | ((x & 0xF0) >> 4)
    elif width == 16:
        x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1)
        x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2)
        x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4)
        x = ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)
    elif width == 32:
        x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1)
        x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2)
        x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4)
        x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8)
        x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16)
    else:
        raise ValueError('Unsupported width')
    return x

def crc_poly(data, n, poly, crc=0, ref_in=False, ref_out=False, xor_out=0):
    g = 1 << n | poly  # Generator polynomial

    # Loop over the data
    for d in data:
        # Reverse the input byte if the flag is true
        if ref_in:
            d = reflect_data(d, 8)

        # XOR the top byte in the CRC with the input byte
        crc ^= d << (n - 8)

        # Loop over all the bits in the byte
        for _ in range(8):
            # Start by shifting the CRC, so we can check for the top bit
            crc <<= 1

            # XOR the CRC if the top bit is 1
            if crc & (1 << n):
                crc ^= g

    # Reverse the output if the flag is true
    if ref_out:
        crc = reflect_data(crc, n)

    # Return the CRC value
    return crc ^ xor_out




# Method to write a seqence of bytes into a output file
def filewrite(tmp_byte):
    binary_file.write(tmp_byte)
    

# iterate excel per each row
for row in nvm_sheet.iter_rows(min_row=5):
    for cell in row:
        #print(row)
        #exit()
        #print(cell.value)
        tmp_arr.append(cell.value)
        #print(cell.value, end=" ")

    print(tmp_arr)
    if (tmp_arr[10] == 'NO'):
        if (str(tmp_arr[11]) == "None"):
            print("Null data: Default value byte array column(J) not updated")
            binary_file.close()
            exit(1)
        else:
            data=tmp_arr[11].split(",")
        #print(len(data),tmp_arr[7])
        if (len(data) != tmp_arr[7]):
            print("Lengh not matched: Default value byte array(J) & BYTE LEN(H) Column values are length mis-matching")
            binary_file.close()
            exit(1)
        else:
            #print("matched")        
            #print(data)
            for i in data:
                #print(i,len(i))
                if (len(i) < 4):
                    i=i.replace('0x','0x0')
                    #print(type(i),i,len(i))
                tmp_str1=tmp_str1+i
            #print(tmp_str1)
            tmp_str1=tmp_str1.replace('0x','')
            #print(tmp_str1)
            tmp_byte = bytes.fromhex(tmp_str1)
            #print(tmp_byte)
    elif (tmp_arr[10] == 'YES'):
        allzero_size = tmp_arr[7]
        tmp_byte = bytes(allzero_size)
        #print(tmp_byte)
    else:
        print("Invalid input: ALL zero status Column(K) not updated")
        binary_file.close()
        exit(1)

    filewrite(tmp_byte)
    nvm_default_values.extend(tmp_byte)
    #print(tmp_arr[3])
    if (tmp_arr[3] != 'NVM_DUMMY_BYTES'):
        tmp_str1=""
        tmp_arr=[]
    else:
        print("")
        print("NVM Binary File Creation Completed Successfully !!!")
        binary_file.close()
        #print("NVM raw: ")
        length = len(nvm_default_values)
        crc = calc_checksum_bulkdata(nvm_default_values, length)
        print("CRC value:" ,hex(crc))
        binary_file_2 = open("nvm_default_dataflash.bin", "wb")
        nvm_default_values.extend(crc.to_bytes(4, 'little'))
        binary_file_2.write(nvm_default_values)

        
        binary_file_2.close()

        #print(nvm_default_values)
        #print('\nbytearray\n')
        # using join() + format()
        # Converting bytearray to hexadecimal string
        varname = 'const uint8_t nvm_default_data[] = {\n'

        data_hex = '0x'            
        data_hex += ',0x'.join(format(x, '02x') for x in nvm_default_values)
        endbrace = '};'
        #print(data_hex)

        data_hex_with_newline = '\n'

        counter_32_ = 0
        for x in data_hex:
            #print(x)
            data_hex_with_newline += x
            if(x == ','):
                counter_32_ += 1
            if (counter_32_ == 12):
                counter_32_ = 0
                data_hex_with_newline += '\n'

        #print(data_hex_with_newline)


        binary_file_3 = open("../../exo_fw/nvm/src/nvm_defaults.c", "w")

        binary_file_3.write("// <<< Auto generated file >>>\n\n\n\n")        
        binary_file_3.write("#include \"exo_types.h\"\n")        
        binary_file_3.write("#include \"exo_common.h\"\n")        
        binary_file_3.write("#include \"nvm.h\"\n\n")        
        binary_file_3.write(str(varname))
        binary_file_3.write(str(data_hex_with_newline))
        binary_file_3.write(str(endbrace))
        binary_file_3.write("\n\nvoid nvm_load_defaults(uint8_t *buff)\n{\n")        
        binary_file_3.write("	int u16ctr;\n")        
        binary_file_3.write("	for(u16ctr = 0; u16ctr < sizeof(nvm_default_data); u16ctr++)\n")        
        binary_file_3.write("	{\n")        
        binary_file_3.write("		buff[u16ctr] = nvm_default_data[u16ctr];\n")        
        binary_file_3.write("	}\n")        
        binary_file_3.write("}")        

        binary_file_3.close()

        print("FILE: exo_fw/nvm/src/nvm_defaults.c has been updtaed !!!")


        file_stats = os.stat("nvm_default_dataflash.bin")
        print(f'File Size in Bytes is {file_stats.st_size}')
        file_size_bin = file_stats.st_size
        #print("fileseize is", file_size_bin)
        file_size_bin_mod = 2048 - (file_size_bin % 2048)
        
        print("Dummy bytes to be added is", file_size_bin_mod)

        append_dummy_bytes = bytearray(b'\xFF')

        for x in range(file_size_bin_mod-1):
            append_dummy_bytes.extend(b'\xFF')

        #print(append_dummy_bytes)
        shutil.copyfile('nvm_default_dataflash.bin','nvm_default_dataflash_alligned2kb.bin')

        binary_file_3 = open("nvm_default_dataflash_alligned2kb.bin", "ab")
        binary_file_3.write(append_dummy_bytes)
        binary_file_3.close()

        print("###### END ######")
        
        
        exit(1)
 
# Close file
binary_file.close()
