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

import argparse

parser = argparse.ArgumentParser(description='Mininet demo')
parser.add_argument('--client-port', help='Port of client',
                    type=int, action="store", required=True)

parser.add_argument('--send-data', help='Data to send',
                    type=int, action="store", required=False)

parser.add_argument('--fid', help='Funtion id',
                    type=int, action="store", required=False)

parser.add_argument('--closed', help='Closed loop',
                    type=int, action="store", required=True)

group = parser.add_mutually_exclusive_group(required=True)
# group.add_argument('--bandwidth', help='Bandwidth',
#                     type=int, action="store")
group.add_argument('--rps', help='Requests per second',
                   type=int, action="store")
group.add_argument('--req-count', help='Number of requests to send',
                   type=int, action="store")
parser.add_argument('--offload', help='offload a portion of workloads',
                    type=float, action="store")


args = parser.parse_args()
print args.send_data
PORT = args.client_port
dataInt = args.send_data
fid = args.fid
SERVER_IP = "192.168.2.2"

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, 7070))
    print "listening to {} at port {}".format(CLIENT_IP, 9000)
    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 += 1
        data = struct.unpack(">I", packet[base:base + 4])[0]
        base += 4
        function_count = struct.unpack("B", packet[base])[0]
        print "rec", chain_id, exec_id, data, function_id, function_count


def genPacket():
    global fid
    packet = None
    exec_id = random.randint(0, 16384)
    chain_id = 1
    # data = 100
    function_count = 5
    function_id = fid if (fid) else 0
    f0 = 0
    f1 = 0
    f2 = 2
    f3 = 6
    f4 = 2
    print chain_id, exec_id, "function_id", function_id, function_count, \
        f0, f1, f2, f3, f4
    offload_status = False
    chain_id = struct.pack(">I", chain_id)  # chain id
    exec_id = struct.pack(">I", exec_id)  # execution id
    if args.offload is not None:
        max_workload = 100
        dataInt = random.randint(1, max_workload)
        cutoff = max_workload * args.offload
        if dataInt <= cutoff:
            data = struct.pack(">I", dataInt * 256)  # data
            offload_status = True
        else:
            data = struct.pack(">I", dataInt)  # data
    else:
        data = struct.pack(">I", dataInt)  # data
    # print "{0:b}".format(data)
    function_count = struct.pack("B", function_count)  # function count
    function_id = struct.pack(">I", function_id)  # function count
    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 + data + function_count + \
        f0 + f1 + f2 + f3 + f4
    # print dataInt, offload_status
    return packet, offload_status


def sendThread(start_time, runtime, sleep_time, s):
    global ingress_time
    while True:
            packet, offload_status = 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.req_count is not None:
        for i in range(args.req_count):
            s.sendto(packet, (SERVER_IP, PORT))
            ingress_time.append(time.time())
            # print "%.20f" % time.time()
            # time.sleep(2)
            # break

    elif args.offload is None and args.rps is not None:
        runtime = 10
        start_time = time.time()
        sleep_time = 1 / float(args.rps)
        print "calculated inter-arrival time", sleep_time
        while True:
            if time.time() - start_time > runtime:
                break
            s.sendto(packet, (SERVER_IP, PORT))
            ingress_time.append(time.time())
            time.sleep(sleep_time)

    elif args.offload is not None:

        runtime = 10
        thread_count = 8
        start_time = time.time()
        sleep_time = 1 / float(args.rps) * thread_count
        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, s])
            t.daemon = True
            t.start()
        time.sleep(runtime)

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


# r.join()
if args.closed == 1:
    r = threading.Thread(name="receive", target=receive)
    r.daemon = True
    r.start()

time.sleep(1)
send()
