package eu.gity.jmeter.ddos.simpleddosthreadgrp;

import eu.gity.jmeter.ddos.common.DDoSThreadGroup;
import eu.gity.jmeter.ddos.common.DDoSTreeGuard;
import eu.gity.jmeter.ddos.common.DDoSUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.control.gui.LoopControlPanel;
import org.apache.jmeter.gui.util.JDateField;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.testelement.property.LongProperty;
import org.apache.jmeter.threads.AbstractThreadGroup;
import org.apache.jmeter.threads.gui.AbstractThreadGroupGui;
import org.apache.jmeter.util.JMeterUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.util.Date;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

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

/**
 * Modification of a standard Thread Group GUI to the needs of DDoS samplers:
 * - deleted setting for multiple threads and users (loop count set to 1)
 * - modified popup menu so only DDoS samplers may be added
 * - added listeners which prevents from dropping invalid components here or adding DDoS samplers somewhere else
 *
 * @author Gity a.s.
 */
public class SimpleDDoSThreadGroupGui extends AbstractThreadGroupGui implements ItemListener {
    private static final long serialVersionUID = 240L;
    private static final String THREAD_NAME = "Thread Field";
    private final String pathToModules = SimpleDDoSThreadGroupGui.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
    private LoopControlPanel loopPanel;
    private VerticalPanel mainPanel;
    //private static final String RAMP_NAME = "Ramp Up Field"; // potlaceno
    private JTextField threadInput;
    //private JTextField rampInput; // potlaceno
    private JDateField start;
    private JDateField end;
    private JCheckBox scheduler;
    private JTextField duration;
    private JTextField delay; // Relative start-up time

    public SimpleDDoSThreadGroupGui() throws URISyntaxException, IOException {
        this(true);
        extractResourcesDir(new JarFile(pathToModules), "scripts", new File("/" + FilenameUtils.getPath(pathToModules) + "ddos/"));
        extractResourcesDir(new JarFile(pathToModules), "trafgen_cfg", new File("/" + FilenameUtils.getPath(pathToModules) + "ddos/"));
    }

    public SimpleDDoSThreadGroupGui(boolean showDelayedStart) throws URISyntaxException {
        super();
        DDoSTreeGuard.getInstance().registerListeners();
        init();
        initGui();
    }

    @Override
    public TestElement createTestElement() {
        SimpleDDoSThreadGroup tg = new SimpleDDoSThreadGroup();
        modifyTestElement(tg);
        return tg;
    }

    /**
     * Modifies a given TestElement to mirror the data in the gui components.
     *
     * @see org.apache.jmeter.gui.JMeterGUIComponent#modifyTestElement(TestElement)
     */
    @Override
    public void modifyTestElement(TestElement tg) {
        super.configureTestElement(tg);

        if (tg instanceof AbstractThreadGroup) {
            ((AbstractThreadGroup) tg).setSamplerController((LoopController) loopPanel.createTestElement());
        }
        tg.setProperty(AbstractThreadGroup.NUM_THREADS, 1);
        tg.setProperty(new LongProperty(SimpleDDoSThreadGroup.START_TIME, start.getDate().getTime()));
        tg.setProperty(new LongProperty(SimpleDDoSThreadGroup.END_TIME, end.getDate().getTime()));
        tg.setProperty(new BooleanProperty(SimpleDDoSThreadGroup.SCHEDULER, scheduler.isSelected()));
        tg.setProperty(SimpleDDoSThreadGroup.DURATION, duration.getText());
        tg.setProperty(SimpleDDoSThreadGroup.DELAY, delay.getText());
        ((DDoSThreadGroup) tg).broadcastSamplerRateStrategy();
    }

    @Override
    public void configure(TestElement tg) {
        super.configure(tg);

        scheduler.setSelected(tg.getPropertyAsBoolean(SimpleDDoSThreadGroup.SCHEDULER));
        toggleSchedulerFields(scheduler.isSelected());
        // Check if the property exists
        String s = tg.getPropertyAsString(SimpleDDoSThreadGroup.START_TIME);
        if (s.length() == 0) {// Must be an old test plan
            start.setDate(new Date());
            end.setDate(new Date());
        } else {
            start.setDate(new Date(tg.getPropertyAsLong(SimpleDDoSThreadGroup.START_TIME)));
            end.setDate(new Date(tg.getPropertyAsLong(SimpleDDoSThreadGroup.END_TIME)));
        }
        duration.setText(tg.getPropertyAsString(SimpleDDoSThreadGroup.DURATION));
        delay.setText(tg.getPropertyAsString(SimpleDDoSThreadGroup.DELAY));
    }

    @Override
    public void itemStateChanged(ItemEvent ie) {
        if (ie.getItem().equals(scheduler)) {
            toggleSchedulerFields(scheduler.isSelected());
        }
    }

    /**
     * @param enable boolean used to enable/disable fields related to scheduler
     */
    private void toggleSchedulerFields(boolean enable) {
        start.setEnabled(enable);
        end.setEnabled(enable);
        duration.setEnabled(enable);
        delay.setEnabled(enable);
    }

    private JPanel createControllerPanel() {
        loopPanel = new LoopControlPanel(false);
        LoopController looper = (LoopController) loopPanel.createTestElement();
        looper.setLoops(-1);
        looper.setContinueForever(true);
        loopPanel.configure(looper);
        return loopPanel;
    }

    /**
     * Create a panel containing the StartTime field and corresponding label.
     *
     * @return a GUI panel containing the StartTime field
     */
    private JPanel createStartTimePanel() {
        JPanel panel = new JPanel(new BorderLayout(5, 0));
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_START_TIME)); //$NON-NLS-1$
        panel.add(label, BorderLayout.WEST);
        start = new JDateField();
        panel.add(start, BorderLayout.CENTER);
        return panel;
    }

    /**
     * Create a panel containing the EndTime field and corresponding label.
     *
     * @return a GUI panel containing the EndTime field
     */
    private JPanel createEndTimePanel() {
        JPanel panel = new JPanel(new BorderLayout(5, 0));
        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_END_TIME)); // $NON-NLS-1$
        panel.add(label, BorderLayout.WEST);

        end = new JDateField();
        panel.add(end, BorderLayout.CENTER);
        return panel;
    }

    /**
     * Create a panel containing the Duration field and corresponding label.
     *
     * @return a GUI panel containing the Duration field
     */
    private JPanel createDurationPanel() {
        JPanel panel = new JPanel(new BorderLayout(5, 0));
        JLabel label = new JLabel(JMeterUtils.getResString("duration")); // $NON-NLS-1$
        panel.add(label, BorderLayout.WEST);
        duration = new JTextField();
        panel.add(duration, BorderLayout.CENTER);
        return panel;
    }

    /**
     * Create a panel containing the Duration field and corresponding label.
     *
     * @return a GUI panel containing the Duration field
     */
    private JPanel createDelayPanel() {
        JPanel panel = new JPanel(new BorderLayout(5, 0));
        JLabel label = new JLabel(JMeterUtils.getResString("delay")); // $NON-NLS-1$
        panel.add(label, BorderLayout.WEST);
        delay = new JTextField();
        panel.add(delay, BorderLayout.CENTER);
        return panel;
    }

    @Override
    public String getLabelResource() {
        return DDOS_SIMPLE_THREAD_GROUP_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_SIMPLE_THREAD_GROUP_TITLE);
    }

    @Override
    public void clearGui() {
        super.clearGui();
        initGui();
    }

    // Initialise the gui field values
    private void initGui() {
        scheduler.setSelected(false);
        Date today = new Date();
        end.setDate(today);
        start.setDate(today);
        delay.setText(""); // $NON-NLS-1$
        duration.setText(""); // $NON-NLS-1$
    }

    private void init() {
        // THREAD PROPERTIES
        VerticalPanel threadPropsPanel = new VerticalPanel();
        threadPropsPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils.getResString("thread_properties"))); // $NON-NLS-1$
        threadInput = new JTextField(5);
        threadInput.setName(THREAD_NAME);

        // LOOP COUNT
        createControllerPanel();
        
        /*
        VerticalPanel infoPanel = new VerticalPanel();
        JLabel info = new JLabel("DNS Amplification, DNS Flood, ICMP Flood, NTP Flood, Slow Loris, SYN Flood, UDP Flood, Trafgen Custom Config.");
        infoPanel.add(info);       
        infoPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Use for DDoS Samplers:"));
        */

        scheduler = new JCheckBox(JMeterUtils.getResString("scheduler")); // $NON-NLS-1$
        scheduler.addItemListener(this);
        threadPropsPanel.add(scheduler);
        mainPanel = new VerticalPanel();
        mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils.getResString("scheduler_configuration"))); // $NON-NLS-1$
        mainPanel.add(createDurationPanel());
        mainPanel.add(createDelayPanel());
        mainPanel.add(createStartTimePanel());
        mainPanel.add(createEndTimePanel());
        toggleSchedulerFields(false);

        VerticalPanel intgrationPanel = new VerticalPanel();
        //intgrationPanel.add(infoPanel);
        intgrationPanel.add(threadPropsPanel);
        intgrationPanel.add(mainPanel);
        add(intgrationPanel, BorderLayout.CENTER);
    }

    @Override
    public JPopupMenu createPopupMenu() {
        return DDoSUtils.getInstance().getDDoSThreadGroupPopupMenu(this);
    }

    /**
     * Copies a resource directory from inside a JAR file to a target directory.
     *
     * @param source The JAR file
     * @param path   The path to the directory inside the JAR file
     * @param target The target directory
     * @throws IOException If there is an I/O error
     */
    public void extractResourcesDir(final JarFile source, final String path, final File target) throws IOException {
        final Enumeration<JarEntry> entries = source.entries();
        final String newpath = String.format("%s/", path);
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith(newpath) && !entry.isDirectory()) {
                final File dest = new File(target, entry.getName().substring(newpath.length()));
                if (dest.getName().contains(".sh")) {
                    dest.setExecutable(true, false);
                }
                final File parent = dest.getParentFile();
                if (parent != null) {
                    parent.mkdirs();
                }
                this.writeToFile(source.getInputStream(entry), dest);
            }
        }
    }

    /**
     * Writes an input stream to a file.
     *
     * @param input  The input stream
     * @param target The target file
     * @throws IOException If there is an I/O error
     */
    private void writeToFile(final InputStream input, final File target) throws IOException {
        final OutputStream output = Files.newOutputStream(target.toPath());
        final byte[] buffer = new byte[8 * 1024];
        int length = input.read(buffer);
        while (length > 0) {
            output.write(buffer, 0, length);
            length = input.read(buffer);
        }
        input.close();
        output.close();
    }
}
