from random import randint
from collections import deque

from Attack import BaseAttack
from Attack.AttackParameters import Parameter as Param
from Attack.AttackParameters import ParameterTypes
from ID2TLib import IPPacketGenerator as IPPktGen, FileUtils
from ID2TLib.IPGenerator import MappingIPGenerator 


class MembersMgmtCommAttack(BaseAttack.BaseAttack):
    def __init__(self):
        """
        Creates a new instance of the Membership Management Communication.

        """
        # Initialize communication
        super(MembersMgmtCommAttack, self).__init__("Membership Management Communication Attack (MembersMgmtCommAttack)", 
                                        "Injects Membership Management Communication", "Botnet communication")

        # Define allowed parameters and their type
        self.supported_params = {
            # parameters regarding attack 
            Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
            Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
            Param.PACKETS_PER_SECOND: ParameterTypes.TYPE_FLOAT,
            Param.PACKETS_LIMIT: ParameterTypes.TYPE_INTEGER_POSITIVE,
            Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,

            # use num_attackers to specify number of communicating devices?
            Param.NUMBER_ATTACKERS: ParameterTypes.TYPE_INTEGER_POSITIVE,

            # input file containing botnet communication
            Param.FILE_CSV: ParameterTypes.TYPE_FILEPATH,
            Param.FILE_XML: ParameterTypes.TYPE_FILEPATH
        }

    def init_params(self):
        """
        Initialize some parameters of this communication-attack using the user supplied command line parameters.
		The remaining parameters are implicitly set in the provided data file. Note: the timestamps in the file 
		have to be sorted in ascending order

        :param statistics: Reference to a statistics object.
        """
        # set class constants
        self.MESSAGE_TYPES = {2: "SALITY", 3: "TIMEOUT"}
        self.DEFAULT_XML_PATH = "resources/MembersMgmtComm_example.xml"

        # PARAMETERS: initialize with default values
        # (values are overwritten if user specifies them)
        self.add_param_value(Param.INJECT_AFTER_PACKET, randint(0, self.statistics.get_packet_count()))
        self.add_param_value(Param.PACKETS_PER_SECOND, 0)
        self.add_param_value(Param.FILE_XML, self.DEFAULT_XML_PATH)
        
    def generate_attack_pcap(self):
        filepath_xml = self.get_param_value(Param.FILE_XML)
        filepath_csv = self.get_param_value(Param.FILE_CSV)

        # prefer XML input over CSV input
        if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
            filepath_xml = FileUtils.parse_csv_to_xml(filepath_csv) 

        comm_entries = FileUtils.parse_xml(filepath_xml)

        # Setup initial parameters for packet creation
        BUFFER_SIZE = 1000
        id_to_ip_mapper = MappingIPGenerator()
        file_timestamp_prv = float(comm_entries[0]["Time"])
        pcap_timestamp = self.get_param_value(Param.INJECT_AT_TIMESTAMP)
        duration = 0
        packets = deque(maxlen=BUFFER_SIZE)
        total_pkts = 0
        limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
        limit_duration = self.get_param_value(Param.ATTACK_DURATION)
        path_attack_pcap = None

        # create packets to write to pcap file
        for entry in comm_entries:
            # map/retrieve ip addresses to ids from input file
            ip_src = id_to_ip_mapper.get_mapped_ip(entry["Src"])
            ip_dst = id_to_ip_mapper.get_mapped_ip(entry["Dst"])
            
            # update timestamps and duration
            file_timestamp = float(entry["Time"])
            file_time_delta = file_timestamp - file_timestamp_prv
            pcap_timestamp += file_time_delta
            duration += file_time_delta
            file_timestamp_prv = file_timestamp

            # if total number of packets has been sent or the attack duration has been exceeded, stop
            if ((limit_packetcount is not None and total_pkts >= limit_packetcount) or 
                    (limit_duration is not None and duration >= limit_duration)):
                break
        
            # create ip packet and add to packets list
            message_type = self.MESSAGE_TYPES[int(entry["Type"])]
            packet = IPPktGen.generate_ip_packet(ip_src=ip_src, ip_dst=ip_dst, payload=message_type)
            packet.time = pcap_timestamp
            packets.append(packet)
            total_pkts += 1

            # Store timestamp of first packet (for attack label)
            if total_pkts <= 1 :
                self.attack_start_utime = packets[0].time
            elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the pcap file (append)
                last_packet = packets[-1]
                packets = list(packets)
                path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                packets = deque(maxlen=BUFFER_SIZE)

        if len(packets) > 0:
            packets = list(packets)
            path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
            last_packet = packets[-1]

        # Store timestamp of last packet
        self.attack_end_utime = last_packet.time

        # Return packets sorted by packet by timestamp and total number of packets (sent)
        return total_pkts , path_attack_pcap