package eu.gity.jmeter.ddos.trafgencustom;

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.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
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 Custom DoS attack
 *
 * @author Gity a.s.
 */
public class TrafgenCustomGui extends AbstractSamplerGui {

    public static final String configRelativePath = "/lib/ext/ddos";
    private static final long serialVersionUID = 240L;
    private static final Logger log = LoggerFactory.getLogger(TrafgenCustomGui.class);
    private JTextField number;
    private JTextField rate;
    private final JTextArea textArea = new JTextArea(100, 60);
    private final JButton browse = new JButton(JMeterUtils.getLocaleString(DDOS_BROWSE));
    private boolean dynamicRate;

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

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

    /**
     * Finds out where JMeter resides
     *
     * @return path to JMeter main directory
     */
    private String getJmeterHomeDir() {
        if (System.getProperty("jmeter.home") == null) {
            File userDir = new File(System.getProperty("user.dir"));
            return userDir.getAbsoluteFile().getParent();
        } else {
            return System.getProperty("jmeter.home");
        }
    }

    /**
     * Loads text file content into GUI text area
     *
     * @param file a file from which the text should be loaded
     * @return true if the reading was successful
     */
    public boolean loadTextIntoArea(File file) {
        FileReader reader;
        try {
            reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            try {
                textArea.read(br, null);
            } catch (IOException e) {
                log.error("Error while reading configuration file " + file + ":\n" + e.getStackTrace().toString());
                return false;
            } finally {
                try {
                    br.close();
                } catch (IOException e) {
                    log.error("Error while closing configuration file " + file + ":\n" + e.getStackTrace().toString());
                    return true;
                }
            }
            textArea.requestFocus();
        } catch (FileNotFoundException e) {
            log.error("The configuration file " + file + " was not found");
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getLabelResource() {
        return DDOS_TRAFGEN_CUSTOM_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_TRAFGEN_CUSTOM_TITLE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public TestElement createTestElement() {
        TrafgenCustomSampler sampler = new TrafgenCustomSampler();
        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(TrafgenCustomSampler.INTERF, selectedDev.toString());
        }
        te.setProperty(TrafgenCustomSampler.CONFIG, textArea.getText());
        te.setProperty(TrafgenCustomSampler.NUMBER, number.getText());
        te.setProperty(TrafgenCustomSampler.RATE, rate.getText());
        ((TrafgenCustomSampler) 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(TrafgenCustomSampler.INTERF));
        textArea.setText(element.getPropertyAsString(TrafgenCustomSampler.CONFIG));

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

    /**
     * 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;
    }

    /**
     * Creates a panel for loading trafgen configuration file
     *
     * @return panel containing GUI components for loading trafgen configuration file
     */
    private JPanel browsePanel() {
        JPanel browsePanel = new JPanel(new BorderLayout(5, 0));
        browsePanel.add(browse, BorderLayout.WEST);

        JLabel label = new JLabel(JMeterUtils.getLocaleString(DDOS_FILE_NAME));
        browsePanel.add(label, BorderLayout.CENTER);
        browse.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                final JFileChooser fc = new JFileChooser();
                fc.setCurrentDirectory(new java.io.File(getJmeterHomeDir() + configRelativePath));
                if (evt.getSource() == browse) {
                    @SuppressWarnings("unused")
                    int returnVal = fc.showOpenDialog(TrafgenCustomGui.this);
                    File file = fc.getSelectedFile();
                    if (file != null) {
                        if (loadTextIntoArea(file)) {
                            String fileName = file.getName();
                            label.setText(JMeterUtils.getLocaleString(DDOS_FILE_NAME) + " " + fileName);
                        } else {
                            JOptionPane.showMessageDialog(null, JMeterUtils.getLocaleString(DDOS_ERROR_LOADING_CONFIG),
                                    JMeterUtils.getLocaleString(DDOS_ERROR), JOptionPane.ERROR_MESSAGE);
                        }
                    }
                }
            }
        });
        return browsePanel;
    }

    /**
     * Creates a panel for editing trafgen configuration file
     *
     * @return panel containing GUI components for editing trafgen configuration file
     */
    private JPanel textPanel() {
        JPanel textPanel = new JPanel(new BorderLayout(5, 0));
        textPanel.add(textArea, BorderLayout.CENTER);

        return textPanel;
    }

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

        VerticalPanel mainPanel = new VerticalPanel();

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

        startupLayer.add(interfPanel());
        startupLayer.add(numberPanel());
        startupLayer.add(ratePanel());

        mainPanel.add(startupLayer);
        mainPanel.add(browsePanel());
        mainPanel.add(textPanel());

        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) {
        if (!dynamicRate) {
            number.setEnabled(en);
            rate.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);
    }
}
