import socket
import struct
import time
import threading
import random
import time

import argparse

parser = argparse.ArgumentParser(description='Mininet demo')

parser.add_argument('--fid', help='Funtion id',
                    type=int, action="store", required=False)
parser.add_argument('--c', help='Concurrency',
                   type=int, action="store", required=True)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--rps', help='Requests per second',
                   type=int, action="store")
group.add_argument('--n', help='Number of requests to send',
                   type=int, action="store")


args = parser.parse_args()

PORT = 8000
dataInt = 0
fid = args.fid
SERVER_IP = "192.168.2.3"

egress_time = []
ingress_time = []
stop_thread = False


def receive():
    global egress_time, stop_thread
    CLIENT_IP = "0.0.0.0"
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind((CLIENT_IP, 8080))
    print "listening to {} at port {}".format(CLIENT_IP, 8080)
    run_status = {}

    while True:
        if stop_thread:
            break
        packet, addr = s.recvfrom(1024)
        # print packet
        base = 0
        chain_id = struct.unpack(">I", packet[base:base + 4])[0]
        base += 4
        exec_id = struct.unpack(">I", packet[base:base + 4])[0]
        base += 4
        function_id = struct.unpack(">I", packet[base:base + 4])[0]
        base += 4
        data = struct.unpack(">I", packet[base:base + 4])[0]
        base += 4
        function_count = struct.unpack("B", packet[base])[0]

        t = int(time.time() * 1000) % 1000000000
        data = int(data) - t
        print "rec", chain_id, exec_id, data, function_id, function_count, 


def genPacket():
    global fid
    packet = None
    exec_id = random.randint(0, 2 ** 30)
    print exec_id
    chain_id = 1
    function_count = 5
    function_id = fid if (fid) else 1
    f0 = 0; f1 = 1; f2 = 2; f3 = 0; f4 = 0
    print chain_id, exec_id, "function_id", function_id, function_count, \
        f0, f1, f2, f3, f4
    
    chain_id = struct.pack(">I", chain_id)  # chain id
    exec_id = struct.pack(">I", exec_id)  # execution id

    dataInt = int(time.time() * 1000) % 1000000000
    data = struct.pack(">I", dataInt)  # data
    
    function_count = struct.pack("B", function_count)  # function count
    function_id_packed = struct.pack(">I", function_id)
    f0 = struct.pack("B", f0)  # f0
    f1 = struct.pack("B", f1)  # f1
    f2 = struct.pack("B", f2)  # f2 -> f0
    f3 = struct.pack("B", f3)  # f3 -> f1 f2
    f4 = struct.pack("B", f4)  # f4 -> f3
    
    packet = chain_id + exec_id + function_id_packed + data + function_count + f0 + f1 + f2 + f3 + f4
    # print dataInt, offload_status
    return packet, function_id


def sendThread(start_time, runtime, sleep_time):
    global ingress_time
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while True:
            packet, function_id = genPacket()
            if time.time() - start_time > runtime:
                break
            s.sendto(packet, (SERVER_IP, PORT))
            ingress_time.append(time.time())
            time.sleep(sleep_time)


def send():
    global egress_time, ingress_time
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    print "Sending packet to %s at port %s" % (SERVER_IP, PORT)
    print "chain id, exec id, data, function count, functions dependencies..."

    # op = struct.unpack("B", packet[0])
    packet, _ = genPacket()
    if args.n is not None:
        for i in range(args.req_count):
            s.sendto(packet, (SERVER_IP, PORT))
            ingress_time.append(time.time())

    elif args.rps is not None:

        runtime = 10
        thread_count = args.c
        start_time = time.time()
        sleep_time = thread_count / float(args.rps)
        print "calculated inter-arrival time, offload mode", sleep_time
        for i in range(thread_count):
            t = threading.Thread(target=sendThread, args=[
                                 start_time, runtime, sleep_time])
            t.daemon = True
            t.start()
        time.sleep(runtime)

    stop_thread = True
    # s.sendto(packet, (SERVER_IP, PORT))
    # r.join()




r = threading.Thread(name="receive", target=receive)
r.daemon = True
r.start()

time.sleep(1)
send()
r.join()
