""" 
reference with 
https://maker.wiznet.io/ronpang/projects/6-micropython-development-for-w5100s-w5500-rp2040/
https://github.com/WIZnet-ioNIC/WIZnet-ioNIC-micropython/blob/master/WIZnet-ioNIC_examples/Loopback.py

# generator matrix
G=[[1,0,0,0,1,1,0],[0,1,0,0,1,0,1],[0,0,1,0,0,1,1],[0,0,0,1,1,1,1]]
# parity-check matrix
H=[[1,1,0,1,1,0,0],[1,0,1,1,0,1,0],[0,1,1,1,0,0,1]]

"""

encode_table=[0, 15, 19, 28, 37, 42, 54, 57, 70, 73, 85, 90, 99, 108, 112, 127]
decode_table=[0, 0, 0, 2, 0, 4, 8, 1, 0, 9, 5, 1, 3, 1, 1, 1, 0, 2, 2, 2, 3, 10, 6, 2, 3, 7, 11, 2, 3, 3, 3, 1, 0, 4, 5, 12, 4, 4, 6, 4, 5, 7, 5, 5, 13, 4, 5, 1, 14, 7, 6, 2, 6, 4, 6, 6, 7, 7, 5, 7, 3, 7, 6, 15, 0, 9, 8, 12, 8, 10, 8, 8, 9, 9, 11, 9, 13, 9, 8, 1, 14, 10, 11, 2, 10, 10, 8, 10, 11, 9, 11, 11, 3, 10, 11, 15, 14, 12, 12, 12, 13, 4, 8, 12, 13, 9, 5, 12, 13, 13, 13, 15, 14, 14, 14, 12, 14, 10, 6, 15, 14, 7, 11, 15, 13, 15, 15, 15]
def hamming_encode_table_method(plaintext):
    return_list=list()
    for s in range(len(plaintext)):
        ### for debug check 
        # print(s) 
        # codeword1 = encode_table[int(plaintext[s]/16)]
        # print(codeword1)
        return_list.append(encode_table[int(plaintext[s]/16)])
        # codeword2 = encode_table[int(plaintext[s]%16)]
        # print(codeword2)
        return_list.append(encode_table[int(plaintext[s]%16)])
        # print(return_bytes)
    return bytes(return_list)

def hamming_decode_table_method(encryptext):
    return_list=list()
    for s in range(len(encryptext)):
        if s % 2 ==0:
            high_nibble=decode_table[int(encryptext[s])]
            low_nibble=decode_table[int(encryptext[s+1])]
            c_append=high_nibble*16+low_nibble
            # print(high_nibble)
            # print(low_nibble)
            # print(c_append)
            return_list.append(c_append)
        
    return bytes(return_list)

import usocket
from machine import Pin,WIZNET_PIO_SPI
import network
import time

from machine import PWM #PWM module

local_port = 5000
nic = None

#W5x00 chip init
def w5x00_init():
    global nic
    PWM(14,freq=25000000,duty_ns=20) #make PWM as w5x00 clock
    
    spi = WIZNET_PIO_SPI(baudrate=31_250_000, mosi=Pin(23),miso=Pin(22),sck=Pin(21)) #W55RP20 PIO_SPI
    nic = network.WIZNET5K(spi,Pin(20),Pin(25)) #spi,cs,reset pin
    nic.active(True)
    
    #None DHCP
    nic.ifconfig(('192.168.137.20','255.255.255.0','192.168.137.1','8.8.8.8')) # share network with WiFi
    
    #DHCP
    #nic.ifconfig('dhcp')
    
    print('IP address :', nic.ifconfig())
    while not nic.isconnected():
        time.sleep(1)
        print(nic.regs())

def udp_loop():
    global nic
    s = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM)
    s.bind((nic.ifconfig()[0], local_port)) #Source IP Address
    ### without print improve performance
    # print(f"Opened UDP loopback\r\nip:{nic.ifconfig()[0]},port:{local_port}")
    time.sleep(1)
    while True:
        data,addr = s.recvfrom(2048)
        print(f'Received:{data} from:{addr}')
        hamming_decoded_data = hamming_decode_table_method(data)
        print(f'Decode as:{hamming_decoded_data}')
        hamming_encoded_data = hamming_encode_table_method(hamming_decoded_data)
        print(f'Send back encode data:{hamming_encoded_data}')
        s.sendto(b'%s' % hamming_encoded_data, addr)

def main():
    w5x00_init()

###UDP CLIENT###
    udp_loop()


if __name__ == "__main__":
    main()