package eu.gity.jmeter.ddos.ntpflood;

import eu.gity.jmeter.ddos.common.DDoSInputVerifier;
import eu.gity.jmeter.ddos.common.DDoSUtils;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.gui.util.HorizontalPanel;
import org.apache.jmeter.gui.util.MenuFactory;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.util.JMeterUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static eu.gity.jmeter.ddos.properties.strings.DdosPropertiesStrings.*;

/**
 * This GUI class provides user interface to setup NTP flood DoS attack
 *
 * @author Gity a.s.
 */
public class NtpFloodGui extends AbstractSamplerGui {
    private static final long serialVersionUID = 240L;

    private JTextField targetIP;
    private JTextField sourceIP;
    private JTextField sourcePort;
    private JTextField destPort;
    private JTextField number;
    private JTextField rate;
    private JTextField sMACsingle;
    private JTextField sMACmin;
    private JTextField sMACmax;
    private JCheckBox incrementalMAC;
    private JTextField dMAC;
    private JTextField ttl;
    private boolean dynamicRate;

    private final JComboBox<String> selectInt = new JComboBox<>();
    private final DDoSInputVerifier verifier = new DDoSInputVerifier();

    /**
     * Ctor which starts GUI initialization
     */
    public NtpFloodGui() {
        init();
        registerTestStateListener();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLabelResource() {
        return DDOS_NTP_FLOOD_TITLE; // $NON-NLS-1$
    }

    /**
     * @return The name of component used in title, tree and name of the test
     */
    @Override
    public String getStaticLabel() {
        return JMeterUtils.getLocaleString(DDOS_NTP_FLOOD_TITLE);
    }

    /**
     * Ctor which starts GUI initialization
     */
    @Override
    public TestElement createTestElement() {
        NtpFloodSampler sampler = new NtpFloodSampler();
        modifyTestElement(sampler);
        return sampler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void modifyTestElement(TestElement te) {
        te.clear();
        configureTestElement(te);
        Object selectedDev = selectInt.getSelectedItem();
        if (selectedDev != null) { //Maybe no network devices were found       
            te.setProperty(NtpFloodSampler.INTERF, selectedDev.toString());
        }

        te.setProperty(NtpFloodSampler.DMAC, dMAC.getText());
        te.setProperty(NtpFloodSampler.SMACSINGLE, sMACsingle.getText());
        te.setProperty(NtpFloodSampler.SMACMIN, sMACmin.getText());
        te.setProperty(NtpFloodSampler.SMACMAX, sMACmax.getText());
        te.setProperty(NtpFloodSampler.INCREMENTALMAC, incrementalMAC.isSelected());
        te.setProperty(NtpFloodSampler.TARGETIP, targetIP.getText());
        te.setProperty(NtpFloodSampler.SOURCEIP, sourceIP.getText());
        te.setProperty(NtpFloodSampler.TTL, ttl.getText());
        te.setProperty(NtpFloodSampler.SOURCEPORT, sourcePort.getText());
        te.setProperty(NtpFloodSampler.DPORT, destPort.getText());
        te.setProperty(NtpFloodSampler.NUMBER, number.getText());
        te.setProperty(NtpFloodSampler.RATE, rate.getText());

        //Following blocks assigns final values for trafgen config to separate properties
        if (incrementalMAC.isSelected()) {
            te.setProperty(NtpFloodSampler.SMACTG, DDoSUtils.getInstance().macIncremental(sMACmin.getText(), sMACmax.getText()));
        } else {
            te.setProperty(NtpFloodSampler.SMACTG, sMACsingle.getText());
        }

        te.setProperty(NtpFloodSampler.SOURCEPORTTG, DDoSUtils.getInstance().toHexPair(sourcePort.getText()));
        te.setProperty(NtpFloodSampler.DESTPORTTG, DDoSUtils.getInstance().toHexPair(destPort.getText()));
        te.setProperty(NtpFloodSampler.SOURCEIPTG, DDoSUtils.getInstance().parseIPtoHex(sourceIP.getText()));
        te.setProperty(NtpFloodSampler.TARGETIPTG, DDoSUtils.getInstance().parseIPtoHex(targetIP.getText()));

        int ttlint = Integer.parseInt(ttl.getText());
        String ttl = "";
        if (ttlint < 10) {
            ttl = "0x0" + Integer.toHexString(ttlint);
        } else {
            ttl = "0x" + Integer.toHexString(ttlint);
        }
        te.setProperty(NtpFloodSampler.TTLTG, ttl);
        ((NtpFloodSampler) te).checkInit();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configure(TestElement element) {
        super.configure(element);

        List<String> devs = DDoSUtils.getInstance().getValidDevNames();
        if (devs.size() > selectInt.getItemCount()) {
            selectInt.setModel(new DefaultComboBoxModel<String>(devs.toArray(new String[devs.size()])));
        }
        selectInt.setSelectedItem(element.getPropertyAsString(NtpFloodSampler.INTERF));
        dMAC.setText(element.getPropertyAsString(NtpFloodSampler.DMAC));
        sMACsingle.setText(element.getPropertyAsString(NtpFloodSampler.SMACSINGLE));
        incrementalMAC.setSelected(element.getPropertyAsBoolean(NtpFloodSampler.INCREMENTALMAC, false));
        sMACmin.setText(element.getPropertyAsString(NtpFloodSampler.SMACMIN));
        sMACmax.setText(element.getPropertyAsString(NtpFloodSampler.SMACMAX));
        targetIP.setText(element.getPropertyAsString(NtpFloodSampler.TARGETIP));
        sourceIP.setText(element.getPropertyAsString(NtpFloodSampler.SOURCEIP));
        ttl.setText(element.getPropertyAsString(NtpFloodSampler.TTL));
        destPort.setText(element.getPropertyAsString(NtpFloodSampler.DPORT));
        sourcePort.setText(element.getPropertyAsString(NtpFloodSampler.SOURCEPORT));

        ((NtpFloodSampler) element).checkInit();
        if (((NtpFloodSampler) element).isDynamicRate()) {
            dynamicRate = true;
            element.setProperty(NtpFloodSampler.DYNAMICRATE, true); //Must be here to prevent DYNAMICRATE property from being deleted by JMeter
            number.setEnabled(false);
            rate.setEnabled(false);
        } else {
            dynamicRate = false;
            element.setProperty(NtpFloodSampler.DYNAMICRATE, false); //Must be here to prevent DYNAMICRATE property from being deleted by JMeter
            number.setText(element.getPropertyAsString(NtpFloodSampler.NUMBER));
            rate.setText(element.getPropertyAsString(NtpFloodSampler.RATE));
            if (selectInt.isEnabled()) { //Means the test is not running
                number.setEnabled(true);
                rate.setEnabled(true);
            }
        }
    }

    /**
     * Creates a panel for NTP server IP setting
     *
     * @return panel containing GUI components for setting target IP
     */
    private JPanel createServerPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_NTP_SERVER_IP)); //$NON-NLS-1$

        targetIP = new JTextField("1.1.1.1");
        label.setLabelFor(targetIP);
        targetIP.setName("ip");
        targetIP.setInputVerifier(verifier);

        JPanel serverPanel = new JPanel(new BorderLayout(5, 0));

        serverPanel.add(label, BorderLayout.WEST);
        serverPanel.add(targetIP, BorderLayout.CENTER);

        return serverPanel;
    }

    /**
     * Creates a panel for source IP setting
     *
     * @return panel containing GUI components for setting source IP
     */
    private JPanel sourceIPPanel() {
        sourceIP = new JTextField("192.168.0.15");
        sourceIP.setName("ip");
        sourceIP.setInputVerifier(verifier);

        final JPanel sourcePanel = new HorizontalPanel();

        JLabel labelS = new JLabel(JMeterUtils.getLocaleString(DDOS_SPOOFED_SOURCE_IP));
        labelS.setLabelFor(sourceIP);

        sourcePanel.add(labelS);
        sourcePanel.add(sourceIP);

        return sourcePanel;
    }

    /**
     * Creates a panel for source port setting
     *
     * @return panel containing GUI components for setting source port
     */
    private JPanel sourcePortPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_TARGET_UDP_PORT)); //$NON-NLS-1$

        sourcePort = new JTextField("1123");
        label.setLabelFor(sourcePort);

        JPanel sourcePortPanel = new JPanel(new BorderLayout(5, 0));

        sourcePortPanel.add(label, BorderLayout.WEST);
        sourcePortPanel.add(sourcePort, BorderLayout.CENTER);

        return sourcePortPanel;
    }


    /**
     * Creates a panel for destination port setting
     *
     * @return panel containing GUI components for setting destination port
     */
    private JPanel destPortPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_NTP_UDP_PORT)); //$NON-NLS-1$

        destPort = new JTextField("123");
        destPort.setName("port");
        destPort.setInputVerifier(verifier);
        label.setLabelFor(destPort);

        JPanel destPortPanel = new JPanel(new BorderLayout(5, 0));

        destPortPanel.add(label, BorderLayout.WEST);
        destPortPanel.add(destPort, BorderLayout.CENTER);

        return destPortPanel;
    }

    /**
     * Creates a panel for source MAC setting
     *
     * @return panel containing GUI components for setting source MAC
     */
    private JPanel sMACPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_SINGLE_VALUE)); //$NON-NLS-1$

        sMACsingle = new JTextField("aa:bb:cc:dd:ee:ff");
        label.setLabelFor(sMACsingle);
        sMACsingle.setName("mac");
        sMACsingle.setInputVerifier(verifier);

        sMACmin = new JTextField("aa:bb:cc:dd:ee:ff");
        sMACmin.setName("mac");
        sMACmin.setInputVerifier(verifier);
        sMACmin.setEnabled(false);

        sMACmax = new JTextField("aa:bb:cc:dd:ef:ff");
        sMACmax.setName("mac");
        sMACmax.setInputVerifier(verifier);
        sMACmax.setEnabled(false);

        incrementalMAC = new JCheckBox(JMeterUtils.getLocaleString(DDOS_INCREMENT)); // $NON-NLS-1$
        incrementalMAC.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(final ItemEvent e) {
                enableRandom(e.getStateChange() == ItemEvent.SELECTED);
            }

            private void enableRandom(boolean b) {
                sMACmin.setEnabled(b);
                sMACmax.setEnabled(b);
                sMACsingle.setEnabled(!b);
            }
        });

        JLabel labelMin = new JLabel("Min: ");
        labelMin.setLabelFor(sMACmin);
        JLabel labelMax = new JLabel("Max: ");
        labelMax.setLabelFor(sMACmax);

        JPanel sMACPanel = new HorizontalPanel();
        sMACPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils.getLocaleString(DDOS_SOURCE_MAC)));
        sMACPanel.add(label);
        sMACPanel.add(sMACsingle);
        sMACPanel.add(incrementalMAC);
        sMACPanel.add(labelMin);
        sMACPanel.add(sMACmin);
        sMACPanel.add(labelMax);
        sMACPanel.add(sMACmax);

        return sMACPanel;
    }


    /**
     * Creates a panel for destination MAC setting
     *
     * @return panel containing GUI components for setting destination MAC
     */
    private JPanel dMACPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_DEST_MAC)); //$NON-NLS-1$

        dMAC = new JTextField("00:ff:ff:ff:ff:ff");
        label.setLabelFor(dMAC);
        dMAC.setName("mac");
        dMAC.setInputVerifier(verifier);

        JPanel dMACPanel = new JPanel(new BorderLayout(5, 0));

        dMACPanel.add(label, BorderLayout.WEST);
        dMACPanel.add(dMAC, BorderLayout.CENTER);

        return dMACPanel;
    }

    /**
     * Creates a panel for TTL setting
     *
     * @return panel containing GUI components for setting TTL
     */
    private JPanel ttlPanel() {
        JLabel label = new JLabel("TTL:"); //$NON-NLS-1$

        ttl = new JTextField("64");
        label.setLabelFor(ttl);
        ttl.setName("natural");
        ttl.setInputVerifier(verifier);

        JPanel ttlPanel = new JPanel(new BorderLayout(5, 0));

        ttlPanel.add(label, BorderLayout.WEST);
        ttlPanel.add(ttl, BorderLayout.CENTER);

        return ttlPanel;
    }

    /**
     * Creates a panel for choosing output interface
     *
     * @return panel containing GUI components for choosing output interface
     */
    private JPanel interfPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_NET_INTERFACE)); //$NON-NLS-1$
        label.setLabelFor(selectInt);

        JPanel interfPanel = new JPanel(new BorderLayout(5, 0));

        interfPanel.add(label, BorderLayout.WEST);
        interfPanel.add(selectInt, BorderLayout.CENTER);

        return interfPanel;
    }


    /**
     * Creates a panel for setting a number of packets to be generated
     *
     * @return panel containing GUI components for setting number of packets
     */
    private JPanel numberPanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_PACKET_COUNT)); //$NON-NLS-1$

        number = new JTextField("100");
        label.setLabelFor(number);
        number.setName("naturalOrNothing");
        number.setInputVerifier(verifier);

        JPanel numberPanel = new JPanel(new BorderLayout(5, 0));

        numberPanel.add(label, BorderLayout.WEST);
        numberPanel.add(number, BorderLayout.CENTER);

        return numberPanel;
    }

    /**
     * Creates a panel for setting generation rate
     *
     * @return panel containing GUI components for setting generation rate
     */
    private JPanel ratePanel() {
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_PACKET_RATE)); //$NON-NLS-1$

        rate = new JTextField("10");
        label.setLabelFor(rate);
        rate.setName("naturalOrNothing");
        rate.setInputVerifier(verifier);

        JPanel ratePanel = new JPanel(new BorderLayout(5, 0));

        ratePanel.add(label, BorderLayout.WEST);
        ratePanel.add(rate, BorderLayout.CENTER);

        return ratePanel;
    }


    /**
     * Performs GUI initialization
     */
    private void init() {
        setLayout(new BorderLayout(5, 5));
        setBorder(makeBorder());
        add(makeTitlePanel(), BorderLayout.NORTH);

        VerticalPanel mainPanel = new VerticalPanel();

        JPanel linkLayer = new VerticalPanel();
        linkLayer.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(Color.LIGHT_GRAY, Color.BLACK), JMeterUtils.getLocaleString(DDOS_LINK_LAYER)));

        JPanel ipLayer = new VerticalPanel();
        ipLayer.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(Color.LIGHT_GRAY, Color.BLACK), JMeterUtils.getLocaleString(DDOS_IP_LAYER)));

        JPanel udpLayer = new VerticalPanel();
        udpLayer.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(Color.LIGHT_GRAY, Color.BLACK), JMeterUtils.getLocaleString(DDOS_TRANSPORT_LAYER)));

        JPanel attack = new VerticalPanel();
        attack.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(Color.LIGHT_GRAY, Color.BLACK), JMeterUtils.getLocaleString(DDOS_ATTACK_STRENGTH)));

        linkLayer.add(sMACPanel());
        linkLayer.add(dMACPanel());

        ipLayer.add(createServerPanel());
        ipLayer.add(sourceIPPanel());
        ipLayer.add(ttlPanel());

        udpLayer.add(sourcePortPanel());
        udpLayer.add(destPortPanel());

        attack.add(numberPanel());
        attack.add(ratePanel());

        mainPanel.add(interfPanel());
        mainPanel.add(linkLayer);
        mainPanel.add(ipLayer);
        mainPanel.add(udpLayer);
        mainPanel.add(attack);

        add(mainPanel, BorderLayout.CENTER);
    }


    /**
     * Enables/disables GUI components (used when test starts/ends)
     *
     * @param en true if the components should be enabled, false if disabled
     */
    private void enableGUIComponents(boolean en) {
        targetIP.setEnabled(en);
        sourceIP.setEnabled(en);
        sourcePort.setEnabled(en);
        destPort.setEnabled(en);
        if (!dynamicRate) {
            number.setEnabled(en);
            rate.setEnabled(en);
        }
        incrementalMAC.setEnabled(en);
        if (incrementalMAC.isSelected()) {
            sMACmin.setEnabled(en);
            sMACmax.setEnabled(en);
        } else {
            sMACsingle.setEnabled(en);
        }
        dMAC.setEnabled(en);
        ttl.setEnabled(en);
        selectInt.setEnabled(en);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clearGui() {
        super.clearGui();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<String> getMenuCategories() {
        return new ArrayList<String>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JPopupMenu createPopupMenu() {
        JPopupMenu pop = new JPopupMenu();
        MenuFactory.addEditMenu(pop, true);
        MenuFactory.addFileMenu(pop);
        return pop;
    }

    /**
     * Registers a listener which reacts to start/end of the test events
     */
    private void registerTestStateListener() {
        TestStateListener testStateListener = new TestStateListener() {

            @Override
            public void testStarted() {
                enableGUIComponents(false);
            }

            @Override
            public void testStarted(String host) {
                testStarted();
            }

            @Override
            public void testEnded() {
                //Register again for a next run
                registerTestStateListener();
                enableGUIComponents(true);
            }

            @Override
            public void testEnded(String host) {
                testEnded();
            }
        };
        StandardJMeterEngine.register(testStateListener);
    }
}
