import socket
import sys
import os
import struct
import binascii
import math
import time
import logging,coloredlogs
from pathlib import Path

# Open the config file and make it accessible via "cfg"
import json
with open("config.json", "r") as file:
    cfg = json.load(file)

l = logging.getLogger(__name__)
coloredlogs.install()

MCAST_GRP = cfg["multicastGroup"]
MCAST_IF = cfg["multicastIf"]
BUF_SIZE = 1396

MULTICAST_TTL = 2

conn = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
conn.bind((MCAST_IF, 63216))
# The below multicast socket options to bind to an interface/add a membership doesn't work on Linux systems.
# conn.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MCAST_GRP)+socket.inet_aton(MCAST_IF))

test = b"This is a test"

def sendFile(files, commands, numSgmts, Pri):
    if Pri == 0:
        MCAST_PORT = 7787
    elif Pri == 1:
        MCAST_PORT = 7788
    else:
        l.critical("Invalid Priority Flag. 0 = Routine Message        1 = High Priority Message\n\nScript will now terminate...")
        exit()

    # Create msgId if it doesn't exist
    msg_id_file = Path('./.temp/msgId.txt')
    if not os.path.exists(msg_id_file):
        msg_id_file_p = Path('./.temp/msgId.txt')
        msg_id_file_p.parent.mkdir(exist_ok=True, parents=True)
        msg_id_file_p.write_text("1")

    # Get the next message ID
    with open(msg_id_file, "r") as f:
        oMsgId = f.read()
        msgNum = int(oMsgId)
        f.close()
    
    nMsgNum = msgNum + 1    
    h = open(msg_id_file, "w")
    h.write(str(nMsgNum))
    h.close()
    segnmNum = 0
    if Pri == 0:
        l.info("Sending Routine Msg-" + str(msgNum) + " on UDP " + MCAST_GRP + " " + str(MCAST_PORT) + "....")
    elif Pri == 1:
        l.info("Sending High Priority Msg-" + str(msgNum) + " on UDP " + MCAST_GRP + " " + str(MCAST_PORT) + "....")
    startFlag = False

    for x, y in zip(files, commands):
        size = os.path.getsize(x)
        check = size - BUF_SIZE
        pToBeSent = size / 1405
        packRounded = math.ceil(pToBeSent) + 1
        numSegments = numSgmts + 3
        total_sent = 0
        payloadLength = 0
        packet_count = 1
        j = 0
        pc = packet_count.to_bytes(1, byteorder='big')
        i = 0
        encode1 = bytes(y + 'I2MSG', 'UTF-8')
        commandLength = len(y)
        encode2 = commandLength.to_bytes(4, byteorder='little')
        theCommand = b"".join([encode1, encode2])
        char = ''
        new_file = open(x, "ab")
        new_file.write(theCommand) # Append command to end of the file
        new_file.close()
        new_size = os.path.getsize(x)

        if startFlag == False:
            #Our 34 byte beginning packet
            p1 = struct.pack(">BHHHIIBBBBBBBIBIBBB", 18, 1, 0 , 16, msgNum, 0, segnmNum, 0, 0, 8, numSegments, 3, 0, 0, 8, packRounded, 0, 0, 0)
            conn.sendto(p1, (MCAST_GRP, MCAST_PORT))
            startFlag = True
        with open(x,"rb") as message:
            message.seek(0)
            data = message.read(BUF_SIZE)
            while data:
                packetHeader = struct.pack(">BHHHIIBBB", 18, 1, 0, 1405, msgNum, packet_count, 0, 0, 0)
                fec = struct.pack("<IBI", packet_count, 0, new_size)
                if len(data) < BUF_SIZE:
                    nullCharacterLen = BUF_SIZE - len(data)
                    char = ''
                    while(i < nullCharacterLen):
                        char += '00'
                        i = i+1
                    theNull = bytes.fromhex(char)
                    conn.sendto(packetHeader + fec + data + theNull, (MCAST_GRP, MCAST_PORT))
                else:
                    conn.sendto(packetHeader + fec + data, (MCAST_GRP, MCAST_PORT))
                l.debug(packet_count)
                packet_count += 1
                j += 1
            
                #Rate Limit UDP Packets To Prevent Packet Overflow On Transport Stream.
                if j == 1000: #Number of packets to be sent before pausing
                    time.sleep(2) #Pause for this number of seconds
                    j = 0
                    data = message.read(BUF_SIZE)     
                else:
                    data = message.read(BUF_SIZE)
        segnmNum += 1

    # OUR TEST MESSAGE BLOCK
    #-------------------------------------------------------------------------------------------------------
    w = 3
    while w <= 3 and w != 0:
        p3 = struct.pack(">BHHHIIBBBBBBBI", 18, 1, 1, 8, msgNum, 0, segnmNum, 0, 0, 8, 0, 0, 0, 67108864)
        p4 = struct.pack(">BHHHIIBBB", 18, 1, 1, 14, msgNum, 1, segnmNum, 0, 0) + test
        conn.sendto(p3, (MCAST_GRP, MCAST_PORT))
        conn.sendto(p4, (MCAST_GRP, MCAST_PORT))
        segnmNum += 1
        w -= 1
    #-------------------------------------------------------------------------------------------------------
def sendCommand(command, Pri, msgNum = None):
    if Pri == 0:
        MCAST_PORT = 7787
    elif Pri == 1:
        MCAST_PORT = 7788
    else:
        l.critical("Invalid Priority Flag. 0 = Routine Message        1 = High Priority Message\n\nScript will now terminate...")
        exit()

    # Create msgId if it doesn't exist
    msg_id_file = Path('./.temp/msgId.txt')
    if not os.path.exists(msg_id_file):
        msg_id_file_p = Path('./.temp/msgId.txt')
        msg_id_file_p.parent.mkdir(exist_ok=True, parents=True)
        msg_id_file_p.write_text("1")

    # Get the next message ID
    with open(msg_id_file, "r") as f:
        oMsgId = f.read()
        msgNum = int(oMsgId)
        l.debug(f"Got message ID {msgNum}")
        f.close()
    
    nMsgNum = msgNum + 1    
    h = open(msg_id_file, "w")
    h.write(str(nMsgNum))
    h.close()
    segnmNum = 0
    if Pri == 0:
        l.info("Sending Routine Msg-" + str(msgNum) + " on UDP " + MCAST_GRP + " " + str(MCAST_PORT) + "....")
    elif Pri == 1:
        l.info("Sending High Priority Msg-" + str(msgNum) + " on UDP " + MCAST_GRP + " " + str(MCAST_PORT) + "....")
    startFlag = False

    for x in command:
        bx = bytes(x, 'utf-8')
        with open('./.temp/command', 'wb') as c:
            c.write(bx)
            c.close()
        size = os.path.getsize('./.temp/command')
        encode1 = bytes('I2MSG', 'UTF-8')
        commandLength = size
        encode2 = commandLength.to_bytes(4, byteorder='little')
        theCommand = b"".join([encode1, encode2])
        with open('./.temp/command', 'ab') as d:
            d.write(theCommand)
            d.close()
        check = size - BUF_SIZE
        pToBeSent = size / 1405
        packRounded = math.ceil(pToBeSent) + 1
        numSegments = 4
        total_sent = 0
        payloadLength = 0
        packet_count = 1
        j = 0
        pc = packet_count.to_bytes(4, byteorder='little')
        i = 0
        char = ''
        new_size = os.path.getsize('./.temp/command')

        if startFlag == False:
            #Our 34 byte beginning packet
            p1 = struct.pack(">BHHHIIBBBBBBBIBIBBB", 18, 1, 0 , 16, msgNum, 0, segnmNum, 0, 0, 8, numSegments, 3, 0, 0, 8, packRounded, 0, 0, 0)
            conn.sendto(p1, (MCAST_GRP, MCAST_PORT))
            startFlag = True
        with open('./.temp/command',"rb") as message:
            message.seek(0)
            data = message.read(BUF_SIZE)
            while data:
                packetHeader = struct.pack(">BHHHIIBBB", 18, 1, 0, 1405, msgNum, packet_count, 0, 0, 0)
                fec = struct.pack("<IBI", packet_count, 0, new_size)
                if len(data) < BUF_SIZE:
                    nullCharacterLen = BUF_SIZE - len(data)
                    char = ''
                    while(i < nullCharacterLen):
                        char += '00'
                        i = i+1
                    theNull = bytes.fromhex(char)
                    conn.sendto(packetHeader + fec + data + theNull, (MCAST_GRP, MCAST_PORT))
                else:
                    conn.sendto(packetHeader + fec + data, (MCAST_GRP, MCAST_PORT))
                l.debug(packet_count)
                packet_count += 1
                j += 1
            
                #Rate limit UDP Packets to prevent Packet Overflow on i2 machine.
                if j == 1000: #Number of packets to be sent before pausing
                    time.sleep(10) #Pause for this number of seconds
                    j = 0
                    data = message.read(BUF_SIZE)     
                else:
                    data = message.read(BUF_SIZE)
        segnmNum += 1

    # OUR TEST MESSAGE BLOCK
    #-------------------------------------------------------------------------------------------------------
    w = 3
    while w <= 3 and w != 0:
        p3 = struct.pack(">BHHHIIBBBBBBBI", 18, 1, 1, 8, msgNum, 0, segnmNum, 0, 0, 8, 0, 0, 0, 67108864)
        p4 = struct.pack(">BHHHIIBBB", 18, 1, 1, 14, msgNum, 1, segnmNum, 0, 0) + test
        conn.sendto(p3, (MCAST_GRP, MCAST_PORT))
        conn.sendto(p4, (MCAST_GRP, MCAST_PORT))
        segnmNum += 1
        w -= 1
    #-------------------------------------------------------------------------------------------------------
