# cPy - demonstrating cooperative async multi processing
# Provided by Anthony Vogelaar, Perth Scotland
# rev 0.10  2024-06-03  initial release

import  os;             os.system('clear')
import  time
import  asyncio

# ============================ Three tasks ===========================

# Seperating a large program into tasks, see also 1_scheduler.py demo2
def demo1():

    # task 1, print '*' at 1 sec
    async def task1():
        while True:
            print('*', end='', flush=True)
            await asyncio.sleep(1)


    # task 2, print '.' at 2 sec
    async def task2():
        while True:
            print('.', end='', flush=True)
            await asyncio.sleep(2)


    # task 3, print '!' at 4 sec
    async def task3():
        while True:
            print('!', end='', flush=True)
            await asyncio.sleep(4)


    async def scheduler():
        await asyncio.gather(task1(), task2(), task3())


    try:
        asyncio.run(scheduler())
    except KeyboardInterrupt:
        print('\nApplication terminated by ctrl-C')

# ======================== context switch speed ======================
    
def demo2():

    async def task(ch):
        for i in range(10):
            print(ch, end='', flush=True)
            await asyncio.sleep(0)


    async def main():
        await asyncio.gather(task('*'), task('.'))


    asyncio.run(main())


def demo3():
        
    async def task(ch):
        for i in range(1000000):
            await asyncio.sleep(0)

    
    async def main():
        t0 = time.time()
        await asyncio.gather(task('*'), task('.'))
        dt = time.time() - t0
        print(f'2000000 switches in {dt:.3f} sec')
        print(f'dit is {dt/2:.3f} uSec per switch')


    asyncio.run(main())

# =========================== async & await ==========================

def demo4():

    def print1():
        print('Hello')


    async def print2():
        print('Hello World')


    async def print3():
        print('line 1')
        await print2()
        print('line 2')
        await print2()


    # print1()
    # print2()
    # print2().send(None)
    print3().send(None)

# ============================== Server ==============================
    
def demo5():

    async def handleClient(reader, writer):
        peer = writer.get_extra_info('peername')
        print('connected to:', peer)
        while True:
            b_rx = await reader.read(255)
            if b_rx:
                print('rx:', b_rx)
                writer.write(b'echo: ' + b_rx)
                await writer.drain()
            else: break
            if not b_rx: break
        writer.close()
        print('disconnected from:', peer)


    async def main():
        server = await asyncio.start_server(handleClient, host='localhost', port=5000)
        print('server listening on port 5000')
        print('   test with: $ nc localhost 5000')
        async with server:
            await server.serve_forever()
    

    asyncio.run(main())

# ============================== Client ==============================
    
def demo6():

    # use as echo server: $ socat -v tcp-l:5000,fork exec:'/bin/cat'

    async def client(s):
        reader, writer = await asyncio.open_connection('localhost', 5000)
        writer.write(s.encode())
        await writer.drain()
        b_rx = await reader.read(255)
        writer.close()
        await writer.wait_closed()
        return b_rx.decode()
    

    async def TUI():
        while True:
            s = input('What will I send? ')
            if s: 
                str = await client(s)
                print('rx:', str)
            else: break


    async def monitor():
        while True:
            print('*', end='', flush=True)
            await asyncio.sleep(1)


    async def main():       
        await asyncio.gather(TUI())
        # await asyncio.gather(monitor())
        # await asyncio.gather(TUI(), monitor())


    asyncio.run(main())

    # see: https://pypi.org/ for modules based on aio

# =============================== Main ===============================
            
# demo1()
# demo2()
# demo3()
# demo4()
# demo5()
demo6()

# =============================== End ===============================
