#!/usr/bin/env python

# python client bob LISTEN
# arecord -D hw:0 -t raw | python proj/voip/client alice CALL bob | aplay -D hw:0 -t raw -
# client -u bob --wait
# client -u alice --call bob

import socket
import sys
import re
import select
import getopt
import time
import os
import fcntl

opt = {}

def readline(sock):
    line = ""
    while True:
        select.select([sock], [], [], 1) ## needed?
        ret = sock.recv(1)
        line += ret
        if ret == '\n':
            break
    return line

def get_public_ip_port(sock):
    sock.sendto("hello", (opt["headerhost"], opt["headerport"]))
    data, addr = sock.recvfrom(1024)
    words = data.split(" ")
#    print "get_public_ip_port ", repr(data)
    return (words[1], int(words[2]))

def udpsweep(sock, ip, low, high):
    for i in range(low, high):
        sock.sendto("destination " + ip + " " + str(i)+"\n", (ip, i))

def do_chat(udpsock, peerhost, peerport):
    if "echo" in opt:
        while True:
            data, addr = udpsock.recvfrom(4000)
            udpsock.sendto(data, (peerhost, peerport))

    if "ping" in opt:
        while True:
            (i, o, e) = select.select([udpsock], [], [], 1)
            if udpsock in i:
                data,addr = udpsock.recvfrom(1024)
                words = data.split(" ")
                if words[0] == "ping":
                    start = float(words[1])
                    end = time.time()
                    print "Received "+str(len(data)) + " bytes in " + str((end-start)*1000) + " msec at " + str(time.time())
            else:
                payload = "ping " + repr(time.time())+ " " + "A"*1500
                payload = payload[0:opt["pingsize"]] # at least 32
                udpsock.sendto(payload, (peerhost, peerport))
                
                
    mic = open(opt["in"], 'r')
    speaker = open(opt["out"], 'w')
    micbuf = ""
    speakerbuf = ""
    while True:
        outputs = []
        if len(speakerbuf) > 0:
            outputs += [speaker]
        if len(micbuf) > 0:
            outputs += [udpsock]
            
        (i, o, e) = select.select([mic,udpsock], outputs, [], 1.5)
        if mic in i:
            micbuf += os.read(mic.fileno(), 256)
        if speaker in o and len(speakerbuf) > 0:
            nwritten = os.write(speaker.fileno(), speakerbuf)
            speakerbuf = speakerbuf[nwritten:]
        if udpsock in o and len(micbuf) > 0:
            udpsock.sendto(micbuf[0:1024], (peerhost, peerport))
            micbuf = micbuf[1024:]
        if udpsock in i:
            data,addr = udpsock.recvfrom(3000)
            speakerbuf += data
        
def managerping():
    relaysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    relaysock.connect((opt["managerhost"], opt["managerport"]))
    while True:
        start = time.time()
        relaysock.send(("PING "+"A"*1024)[0:opt["pingsize"]-1] + "\n")
        readline(relaysock)
        end = time.time()
        print "Received " + str(opt["pingsize"]) + " bytes in " + str((end-start)*1000) + " msec at " + str(time.time())
        time.sleep(1)
    
def chat():
    udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udpsock.bind(('', 8080))

    relaysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    relaysock.connect((opt["managerhost"], opt["managerport"]))
    relaysock.send("USER "+opt["user"]+"\n")
    relaysock.send("PASS "+opt["password"]+"\n")
    if "wait" in opt:
        line = readline(relaysock) # 100 peer CALL ip port port2
        words = line.split(" ")
        peerhost = words[3]
        
        pub = get_public_ip_port(udpsock)
        relaysock.send("MSG "+words[1]+" TRANSFER "+pub[0]+" "+str(pub[1])+" "+str(pub[1]+opt["portrangelen"])+"\n")
        readline(relaysock) # SWEEP
        udpsweep(udpsock, peerhost, int(words[4]), int(words[5]))

        line = readline(relaysock) # 100 peer PORT port
        peerport = int(line.split(" ")[3])

    else:
        pub = get_public_ip_port(udpsock)
        relaysock.send("MSG "+opt["call"]+" CALL "+pub[0]+" "+str(pub[1])+" "+str(pub[1]+opt["portrangelen"])+"\n")
        line = readline(relaysock)
        words = line.split(" ")
        peerhost = words[3]
        udpsweep(udpsock, peerhost, int(words[4]), int(words[5]))
        relaysock.send("MSG "+opt["call"]+" SWEEP\n")
        
        data, addr = udpsock.recvfrom(1024)
        print "Received ", repr(data), repr(addr)
        relaysock.send("MSG "+opt["call"]+" PORT "+data.split(" ")[2]+"\n")
        peerport = addr[1]

#        udpsock.sendto("probe 1", (peerhost, peerport))
#        data,addr = udpsock.recvfrom(1024)
#        print "Received ", repr(data), repr(addr)        

    do_chat(udpsock, peerhost, peerport)

def usage():
    print "example: client -u bob --wait"
    print "         client -u alice --call bob"
    pass

def main():
    global opt
    try:
        longopts = ["managerhost=","headerhost=","portrangelen=","help","call=","wait","echo","password","in=","out=","ping","pingsize=","managerping"]
        opts, args = getopt.getopt(sys.argv[1:], "u:h", longopts)
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    opt["managerhost"] = "juliano.metsahovi.fi"
    opt["portrangelen"] = 10
    opt["managerport"] = 8080
    opt["in"] = "/dev/dsp"
    opt["out"] = "/dev/dsp"
    opt["pingsize"] = 64

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o == "-u":
            opt["user"] = a
        if o == "--managerhost":
            opt["managerhost"] = a
        if o == "--headerhost":
            opt["headerhost"] = a
        if o == "--portrangelen":
            opt["portrangelen"] = int(a)
        if o == "--call":
            opt["call"] = a
        if o == "--wait":
            opt["wait"] = True
        if o == "--echo":
            opt["echo"] = True
        if o == "--password":
            opt["password"] = a
        if o == "--in":
            if a == "-":
                a = "/dev/stdin"
            opt["in"] = a
        if o == "--out":
            if a == "-":
                a = "/dev/stdout"
            opt["out"] = a
        if o == "--ping":
            opt["ping"] = True
        if o == "--pingsize":
            opt["pingsize"] = int(a)
            if opt["pingsize"] < 32:
                print "pingsize cannot be less than 32"
                sys.exit()
        if o == "--managerping":
            opt["managerping"] = True
            
    if "managerping" in opt:
        managerping()
        
    if "user" not in opt:
        usage()
        sys.exit()
    if "call" not in opt and "wait" not in opt:
        usage()
        sys.exit()
        
    if "headerhost" not in opt:
        opt["headerhost"] = opt["managerhost"]
    if "headerport" not in opt:
        opt["headerport"] = opt["managerport"]
    if "password" not in opt:
        opt["password"] = opt["user"] + "secret"

    print opt
    chat()
        
main()
