from .std import N_BYT
#N_BYT = 14

def uart_open(port,baud=10000000):
    from serial import Serial
    dev = Serial(port,baud,stopbits=2,timeout=0.1)
    return dev,dev.write,lambda:dev.read(N_BYT)

def uart_close(dev):
    dev.close()

def ft601_open(sn='IONCV2PROT',sync=True):
    import PyD3XX            
    pad_byt = (4-N_BYT % 4) % 4
    frm_byt = N_BYT + pad_byt
    dev_tot = 0.1
    PyD3XX.SetPrintLevel(PyD3XX.PRINT_NONE)
    sta, dev = PyD3XX.FT_GetDeviceInfoDetail(0)
    sta = PyD3XX.FT_Create(sn,PyD3XX.FT_OPEN_BY_SERIAL_NUMBER,dev)
    if sta != PyD3XX.FT_OK:
        raise RuntimeError(f'FT601 device {sn} does not exist or is in use')
    pipe = [0,0]
    ova = [None,None]
    for i in range(2):
        sta, pipe[i] = PyD3XX.FT_GetPipeInformation(dev, 1, i)
        PyD3XX.FT_SetPipeTimeout(dev, pipe[i], 1000)
        sta, ova[i] = PyD3XX.FT_InitializeOverlapped(dev)
    if sync:
        import time
    else:
        import asyncio
    def dev_wr(dat):
        buf = PyD3XX.FT_Buffer.from_bytearray(dat)
        sta, bwr = PyD3XX.FT_WritePipe(dev, pipe[0], buf, len(dat), PyD3XX.NULL)
        if sta != PyD3XX.FT_OK:
            PyD3XX.FT_AbortPipe(dev, pipe[0])
            time.sleep(dev_tot)
            PyD3XX.FT_WritePipe(dev, pipe[0], buf, len(dat), PyD3XX.NULL)
    def dev_rd():
        sta, buf, brd = PyD3XX.FT_ReadPipe(dev, pipe[1], frm_byt, PyD3XX.NULL)
        frm = buf.Value()
        if brd == frm_byt:
            vld = frm[pad_byt] >> 4
            if vld == 0:
                return frm
            time.sleep(dev_tot)
            return
        time.sleep(dev_tot)
        PyD3XX.FT_AbortPipe(dev, pipe[1])
        buf = PyD3XX.FT_Buffer.from_bytes(b'\xFF'*frm_byt)
        PyD3XX.FT_WritePipe(dev, pipe[0], buf, frm_byt, PyD3XX.NULL)
        time.sleep(dev_tot)
        return        
    async def dev_awr(dat):       
        buf = PyD3XX.FT_Buffer.from_bytearray(dat)
        async def awr():
            if PyD3XX.Platform == 'windows':
                sta,bwr = PyD3XX.FT_WritePipe(dev, pipe[0], buf, len(dat), ova[0])
            else:
                sta,bwr = PyD3XX.FT_WritePipeAsync(dev, 0, buf, len(dat), ova[0])
            while True:
                sta,bwr = PyD3XX.FT_GetOverlappedResult(dev, ova[0], False)
                if sta != PyD3XX.FT_IO_INCOMPLETE:
                    break
                await asyncio.sleep(0.001)
        await awr()
        if sta != PyD3XX.FT_OK:
            PyD3XX.FT_AbortPipe(dev, pipe[0])
            await asyncio.sleep(dev_tot)
            await awr()
    async def dev_ard():
        if PyD3XX.Platform == 'windows':
            sta,buf,brd = PyD3XX.FT_ReadPipe(dev, pipe[1], frm_byt, ova[1])
        else:
            sta,buf,brd = PyD3XX.FT_ReadPipeAsync(dev, 0, frm_byt, ova[1])
        while True:
            sta,brd = PyD3XX.FT_GetOverlappedResult(dev, ova[1], False)
            if sta != PyD3XX.FT_IO_INCOMPLETE:
                break
            await asyncio.sleep(0.001)
        frm = buf.Value()
        if brd == frm_byt:
            vld = frm[pad_byt] >> 4
            if vld == 0:
                return frm
            await asyncio.sleep(dev_tot)
            return
        await asyncio.sleep(dev_tot)
        PyD3XX.FT_AbortPipe(dev, pipe[1])
        buf = PyD3XX.FT_Buffer.from_bytes(b'\xFF'*frm_byt)
        if PyD3XX.Platform == 'windows':
            sta,bwr = PyD3XX.FT_WritePipe(dev, pipe[0], buf, frm_byt, ova[0])
        else:
            sta,bwr = PyD3XX.FT_WritePipeAsync(dev, 0, buf, frm_byt, ova[0])
        while True:
            sta,bwr = PyD3XX.FT_GetOverlappedResult(dev, ova[0], False)
            if sta != PyD3XX.FT_IO_INCOMPLETE:
                break
            await asyncio.sleep(0.001)
        await asyncio.sleep(dev_tot)    
    return (dev,dev_wr,dev_rd) if sync else (dev,dev_awr,dev_ard)

def ft601_close(dev):
    import PyD3XX
    PyD3XX.FT_Close(dev)