/*
 * Copyright (c) 2015 Paul Scherrer Institute. All rights reserved.
 */

import ch.psi.pshell.ui.App;
import ch.psi.pshell.ui.View;
import ch.psi.utils.swing.SwingUtils;
import ch.psi.utils.swing.SwingUtils.OptionResult;
import ch.psi.utils.swing.SwingUtils.OptionType;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author boccioli_m
 */
public class NewTest extends javax.swing.JPanel {
    /**
     * constants declarations
     */
    public static String TESTS_ROOT_DIR = Paths.get(".", "home", "script", "tests").toString();
    public static Path TESTS_DEVICES_DEFAULT_DIR        = Paths.get(TESTS_ROOT_DIR, "devices");
    public static Path TESTS_TESTS_DEFAULT_DIR          = Paths.get(TESTS_ROOT_DIR, "tests");
    public static Path TESTS_CONFIG_DEFAULT_DIR         = Paths.get(TESTS_ROOT_DIR, "config");
    public static Path TEMPLATES_HELP_FILEPATH          = Paths.get(TESTS_ROOT_DIR, "templates","helpTemplate.html");
    public static Path TEMPLATES_TESTSCRIPT_FILEPATH    = Paths.get(TESTS_ROOT_DIR, "templates","testTemplate.py");
    public static Path TEMPLATES_TESTCONFIG_FILEPATH    = Paths.get(TESTS_ROOT_DIR, "templates","testTemplate.config");
    public static Path TEMPLATES_DEVICECONFIG_FILEPATH  = Paths.get(TESTS_ROOT_DIR, "templates","deviceTemplate.config");
    public static String TESTS_CONFIG_FILENAME = ".config";
    public static String TESTS_HELP_FILENAME = "help.html";
    public static String ALLOWED_CHARSET1 = "[a-z A-Z 0-9 . _ \\- \\[\\] () : , < > =]*"; //les restricted set of chars
    public static String ALLOWED_CHARSET2 = "[a-z A-Z 0-9 . _ \\- \\[\\] ()]*"; //more restricted set of chars
    public static String ALLOWED_CHARSET3 = "[a-zA-Z0-9._\\-]*"; //even more restricted set of chars
   
    /**
     * enumeration of table column indexes
     */
    public enum TypeOfNewFile {
        Test,
        Device;
    };    
    /**
     * Creates new form TestingListDetails
     */
    private TypeOfNewFile sType;
    
    public NewTest() {
        initComponents();
    }
    
    public NewTest(String hDetails) {
        initComponents();
        if(hDetails.equals("Device")){
            sType = TypeOfNewFile.Device;
        }
        else{
            sType = TypeOfNewFile.Test;
        }
        fillComponents(sType);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPopupMenuTable = new javax.swing.JPopupMenu();
        jMenuItemAddRow = new javax.swing.JMenuItem();
        jMenuItemDeleteRow = new javax.swing.JMenuItem();
        lblName = new javax.swing.JLabel();
        lblSuite = new javax.swing.JLabel();
        lblParams = new javax.swing.JLabel();
        lblDescr = new javax.swing.JLabel();
        lblContact = new javax.swing.JLabel();
        txtTestName = new javax.swing.JTextField();
        jComboBoxTestSuites = new javax.swing.JComboBox();
        jScrollPane3 = new javax.swing.JScrollPane();
        txtTestDescription = new javax.swing.JTextArea();
        txtContactName = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTableParams = new javax.swing.JTable();
        jPanel1 = new javax.swing.JPanel();
        jButtonGenerate = new javax.swing.JButton();

        jMenuItemAddRow.setText("Add Row");
        jMenuItemAddRow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItemAddRowActionPerformed(evt);
            }
        });
        jPopupMenuTable.add(jMenuItemAddRow);

        jMenuItemDeleteRow.setText("Delete Row");
        jMenuItemDeleteRow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItemDeleteRowActionPerformed(evt);
            }
        });
        jPopupMenuTable.add(jMenuItemDeleteRow);

        lblName.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        lblName.setText("Name *");
        lblName.setToolTipText("Test Name");

        lblSuite.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        lblSuite.setText("Test Case/Suite *");

        lblParams.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        lblParams.setText("Test Parameters");

        lblDescr.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        lblDescr.setText("Description *");

        lblContact.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        lblContact.setText("Contact person *");
        lblContact.setToolTipText("Write here the user name of the person who can be contacted for any question concerning this test");

        txtTestName.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        txtTestName.setToolTipText("");
        txtTestName.setMinimumSize(new java.awt.Dimension(30, 23));
        txtTestName.setPreferredSize(new java.awt.Dimension(60, 23));

        jComboBoxTestSuites.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        jComboBoxTestSuites.setToolTipText("Select a Test Suite or add a new one");
        jComboBoxTestSuites.setMinimumSize(new java.awt.Dimension(31, 26));
        jComboBoxTestSuites.setPreferredSize(new java.awt.Dimension(31, 26));

        jScrollPane3.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N

        txtTestDescription.setColumns(20);
        txtTestDescription.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        txtTestDescription.setLineWrap(true);
        txtTestDescription.setRows(5);
        txtTestDescription.setToolTipText("Add here a detailed description");
        jScrollPane3.setViewportView(txtTestDescription);

        txtContactName.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        txtContactName.setToolTipText("Write here the user name of the person who can be contacted for any question concerning this test");

        jTableParams.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        jTableParams.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Parameter", "Value", "Description"
            }
        ));
        jTableParams.setColumnSelectionAllowed(true);
        jTableParams.setDragEnabled(true);
        jTableParams.setRowHeight(20);
        jTableParams.getTableHeader().setReorderingAllowed(false);
        jTableParams.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jTableParamsMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(jTableParams);
        jTableParams.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        if (jTableParams.getColumnModel().getColumnCount() > 0) {
            jTableParams.getColumnModel().getColumn(0).setMinWidth(150);
            jTableParams.getColumnModel().getColumn(0).setPreferredWidth(150);
            jTableParams.getColumnModel().getColumn(0).setMaxWidth(150);
            jTableParams.getColumnModel().getColumn(1).setPreferredWidth(80);
            jTableParams.getColumnModel().getColumn(1).setMaxWidth(100);
        }

        jButtonGenerate.setText("Generate");
        jButtonGenerate.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonGenerateActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jButtonGenerate, javax.swing.GroupLayout.PREFERRED_SIZE, 188, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(0, 0, 0)
                .addComponent(jButtonGenerate, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addGap(1, 1, 1))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(lblParams)
                                .addGap(18, 18, 18)
                                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 514, Short.MAX_VALUE))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(lblName)
                                    .addComponent(lblSuite))
                                .addGap(0, 0, Short.MAX_VALUE)))
                        .addGap(12, 12, 12))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(lblDescr)
                            .addComponent(lblContact))
                        .addGap(10, 10, 10)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(txtTestName, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jComboBoxTestSuites, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(txtContactName))
                        .addGap(12, 12, 12))))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblName)
                    .addComponent(txtTestName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(lblSuite)
                    .addComponent(jComboBoxTestSuites, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 81, Short.MAX_VALUE)
                    .addComponent(lblDescr))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(txtContactName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(lblContact))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(lblParams)
                        .addGap(0, 132, Short.MAX_VALUE))
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void jButtonGenerateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonGenerateActionPerformed
        // TODO add your handling code here:
        if(checkDataFields()){
            generateTestFiles();
        }
    }//GEN-LAST:event_jButtonGenerateActionPerformed

    private void jTableParamsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jTableParamsMouseClicked
        // TODO add your handling code here:
        if(evt.getButton() == java.awt.event.MouseEvent.BUTTON3){
            Point cursor = evt.getPoint();
            int rowAtPoint = jTableParams.rowAtPoint(cursor);
            jTableParams.setRowSelectionInterval(rowAtPoint, rowAtPoint);                
            jPopupMenuTable.show(jTableParams, evt.getX(), evt.getY());
        }        
    }//GEN-LAST:event_jTableParamsMouseClicked

    private void jMenuItemAddRowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItemAddRowActionPerformed
        // TODO add your handling code here:
        DefaultTableModel model = (DefaultTableModel) jTableParams.getModel();
        model.addRow(new Object[]{"", "", ""});                        
        jTableParams.setModel(model);           
    }//GEN-LAST:event_jMenuItemAddRowActionPerformed

    private void jMenuItemDeleteRowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItemDeleteRowActionPerformed
        // TODO add your handling code here:
        int row = jTableParams.getSelectedRow();
        if(row>=0 && row<jTableParams.getRowCount()){
            DefaultTableModel model = (DefaultTableModel) jTableParams.getModel();
            model.removeRow(row);
        }
    }//GEN-LAST:event_jMenuItemDeleteRowActionPerformed

    /**
     * fill table with example parameters
     * @param newType of type TypeOfNewFile, can be device or test.
     */
    private void fillComponents(TypeOfNewFile newType) {

        // listener for mouse-click also on table header
        jTableParams.getTableHeader().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                if(evt.getButton() == java.awt.event.MouseEvent.BUTTON3){
                    jPopupMenuTable.show(jTableParams, evt.getX(), evt.getY());
                }   
            }
        });        
        
        //adapt labels according to type
        if(newType == TypeOfNewFile.Device){
            this.lblDescr.setText(newType + " "+ this.lblDescr.getText());
            this.lblName.setText( newType + " "+ this.lblName.getText());
            this.lblName.setToolTipText(newType + " name. Can be a PV name.");
            this.txtTestName.setToolTipText(newType + " name. Can be a PV name.");
            lblParams.setVisible(false);
            jTableParams.setVisible(false);
            jScrollPane1.setVisible(false);
            lblContact.setVisible(false);
            txtContactName.setVisible(false);
        }
        
        //fill test suites combo box
        File testsFolder = Paths.get(TESTS_TESTS_DEFAULT_DIR.toString()).toFile();
        if (testsFolder.exists() && testsFolder.isDirectory()) {
            File[] listOfTests = testsFolder.listFiles();
            for (File listOfTest : listOfTests) {
                if (listOfTest.isDirectory()) {
                    this.jComboBoxTestSuites.addItem(listOfTest.getName());
                }
            }
        }
        this.jComboBoxTestSuites.setEditable(true);
        this.jComboBoxTestSuites.setSelectedItem(null);
        
        //fill parameters table
        InputStream ist = null;
        try {
            Properties propTest = new Properties();
            File configFile = TEMPLATES_TESTCONFIG_FILEPATH.toFile();
            ist = new FileInputStream(configFile);
            propTest.load(ist);
            String testParams = propTest.getProperty("parameters");
            if (testParams == null) {
                testParams = "";
            }
            ist.close();
            //separate items in string by building a HashMap
            HashMap hParams = (HashMap) buildParametersMap(testParams);
            String name="", value="", description="";
            DefaultTableModel model = (DefaultTableModel) jTableParams.getModel();
            //put items in table
            for(Object entry : hParams.keySet()){
                Object param = entry;
                if(param instanceof String){
                    name = (String) param;
                    HashMap attributes = (HashMap) hParams.get(param);
                    value = (String) attributes.get("value");
                    description = (String) attributes.get("description");
                    model.addRow(new Object[]{name, value, description});
                }
                jTableParams.setModel(model);
            }
        } catch (FileNotFoundException ex) {
            SwingUtils.showMessage(this, "fillComponents()", "Cannot find file " + TEMPLATES_TESTCONFIG_FILEPATH.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);     
        } catch (IOException ex) {
            SwingUtils.showMessage(this, "fillComponents()", "Cannot read file " + TEMPLATES_TESTCONFIG_FILEPATH.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                ist.close();
            } catch (IOException ex) {
                SwingUtils.showMessage(this, "fillComponents()", "Cannot close file " + TEMPLATES_TESTCONFIG_FILEPATH.toString());
                Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
   
    }

    /**
     * Build a map with optional parameters to be passed to the testing script.
     * The map is like this: parameters \_ name | \_ value | \_ description \_
     * name | \_ value | \_ description ... the name 'name' is the mapping key.
     * 'value' and 'description' are constant mapping keys of a nested map.
     *
     * @param parametersString string containing the parameters. Syntax:
     * name:value:description[;name:value:description;...]
     */
    private HashMap buildParametersMap(String parametersString) {
        HashMap mParameters = new HashMap(); // contains name and attributes
        HashMap mParameterAttributes = new HashMap(); //contians value and description
        String[] dsParameterAttributes = null;
        String[] dsParameters = parametersString.split(";");
        for (String sParameter : dsParameters) {
            dsParameterAttributes = sParameter.split(":");
            if (dsParameterAttributes.length > 2) {
                mParameterAttributes = new HashMap();
                mParameterAttributes.put("value", (Object) dsParameterAttributes[1]);
                mParameterAttributes.put("description", dsParameterAttributes[2]);
                //add parameter name and attributes (value + description)
                mParameters.put(dsParameterAttributes[0], mParameterAttributes);
            }
        }
        return mParameters;
    }
    
    /**
     * Pick the items one by one from the table and build a HashMap.
     * For details on the HashMap structure, see buildParametersMap()
     * @return contains the test parameters.
     */
    private HashMap getParametersFromTable(){
        HashMap mParameters = new HashMap(); // contains name and attributes
        HashMap mParameterAttributes = new HashMap(); //contians value and description
        for(int row=0 ; row<jTableParams.getRowCount() ; row++){
            String sParam = jTableParams.getValueAt(row, 0).toString();
            String sVal = jTableParams.getValueAt(row, 1).toString();
            String sDescr = jTableParams.getValueAt(row, 2).toString();
            mParameterAttributes = new HashMap();
            mParameterAttributes.put("value", (Object) sVal);
            mParameterAttributes.put("description", sDescr);
            //add parameter name and attributes (value + description)
            mParameters.put(sParam, mParameterAttributes);          
        }
        return mParameters;
    }

    /**
     * verifies that the data introduced in the fields is correct
     * i.e. fields filled, and only allowed characters
     * @return true if the data is correct, false if else
     */
    public boolean checkDataFields(){
        //check compulsory data fields
        if(this.txtTestName.getText().isEmpty() || 
                        this.txtTestDescription.getText().isEmpty() || 
                        this.jComboBoxTestSuites.getSelectedItem().toString().isEmpty()){
            SwingUtils.showMessage(this, "checkDataFields()", 
                    "Please fill-in all compulsory fields (marked with *)");
            return false;
        }
        if(sType == TypeOfNewFile.Test && this.txtContactName.getText().isEmpty()){
            SwingUtils.showMessage(this, "checkDataFields()", 
                    "Please write a user name in the field Contact person");
            return false;
        }
        String allowedChars = ALLOWED_CHARSET2;
        if (sType == TypeOfNewFile.Device){
            allowedChars = ALLOWED_CHARSET1;
        }
        if(!(txtTestName.getText().matches(allowedChars)
        )){
            SwingUtils.showMessage(this, "checkDataFields()", 
                    "Please remove forbidden chars from the name; allowed are: "+ allowedChars.replace("\\", "").replace("*", ""));   
            return false;
        }
        if(!(txtTestDescription.getText().matches(ALLOWED_CHARSET1)
        )){
            SwingUtils.showMessage(this, "checkDataFields()", 
                    "Please remove forbidden chars from the description; allowed are: "+ ALLOWED_CHARSET1.replace("\\", "").replace("*", ""));   
            return false;
        }
        if(!(jComboBoxTestSuites.getSelectedItem().toString().matches(allowedChars)
        )){
            SwingUtils.showMessage(this, "checkDataFields()", 
                    "Please remove forbidden chars from the tests suite name; allowed are: "+ allowedChars.replace("\\", "").replace("*", ""));   
            return false;
        }
        if (sType == TypeOfNewFile.Test){
            //check user name on LDAP
            if(!(txtContactName.getText().matches(ALLOWED_CHARSET3) 
            )){
                SwingUtils.showMessage(this, "checkDataFields()", 
                        "Please provide a valid user name");   
                return false;
            } else{ 
                if ( !userExists(txtContactName.getText())){
                        OptionResult ret = SwingUtils.showOption(this, "checkDataFields()", 
                                "Could not find user name "+txtContactName.getText() + "\nDo you want to continue generating the test?", OptionType.YesNoCancel);
                        if(ret != OptionResult.Yes){
                            return false;
                        }
                }
            }
        }
        //check parameters table
        for(int row=0 ; row<jTableParams.getRowCount() ; row++){
            String sParam = jTableParams.getValueAt(row, 0).toString();
            String sVal = jTableParams.getValueAt(row, 1).toString();
            String sDescr = jTableParams.getValueAt(row, 2).toString();            
            if(sParam.isEmpty() || sVal.isEmpty() || sDescr.isEmpty()){
                SwingUtils.showMessage(this, "checkDataFields()", "You must fill-in all the values in the parameters table");
                return false;
            }        
            if(!(sParam.matches(ALLOWED_CHARSET3))
            ){
                SwingUtils.showMessage(this, "checkDataFields()", 
                        "Please remove forbidden chars from parameters name in table; allowed are: "+ ALLOWED_CHARSET3.replace("\\", "").replace("*", ""));
                return false;
            }
            if(!(sVal.matches(ALLOWED_CHARSET3) )
            ){
                SwingUtils.showMessage(this, "checkDataFields()", 
                        "Please remove forbidden chars from parameters value in table; allowed are: "+ ALLOWED_CHARSET3.replace("\\", "").replace("*", ""));
                return false;
            }
            if(!(sDescr.matches(ALLOWED_CHARSET2))
            ){
                SwingUtils.showMessage(this, "checkDataFields()", 
                        "Please remove forbidden chars from parameters description in table; allowed are: "+ ALLOWED_CHARSET2.replace("\\", "").replace("*", ""));
                return false;
            }
        }
        return true;
    }

    /**
     * pick the data introduced by the user 
     * and create folders and files for the new test to generate
     */
    public void generateTestFiles(){
        HashMap hmTestParams = (HashMap) getParametersFromTable();
        if(sType == TypeOfNewFile.Device){
            generateDeviceFiles(this.txtTestName.getText(), 
                              this.txtTestDescription.getText(), 
                              this.jComboBoxTestSuites.getSelectedItem().toString(), 
                              hmTestParams);            
        }else{
            generateTestFiles(this.txtTestName.getText(), 
                              this.txtTestDescription.getText(), 
                              this.txtContactName.getText(), 
                              this.jComboBoxTestSuites.getSelectedItem().toString(), 
                              hmTestParams);
        }
    }
    
    /**
     * create folders and files for the new test to generate
     * 
     * @param sTestName name of the test
     * @param sTestDescription description of the test
     * @param sContactName user name of the contact person for this test
     * @param sTestSuite test cases/test suite name
     * @param hmTestParams test parameters. For the details of the structure, see buildParametersMap
     */
    public void generateTestFiles(String sTestName,
                                  String sTestDescription,
                                  String sContactName,
                                  String sTestSuite,
                                  HashMap hmTestParams){
            if (TESTS_TESTS_DEFAULT_DIR.toFile().isDirectory()) {
                //check that the tests suite dir exists. If not, create it
                String testSuiteDir = generateTestSuite(sTestSuite);
                //new test directory inside tests suite dir
                Path testDir = Paths.get(testSuiteDir,cleanFileName(sTestName));
                if( testDir.toFile().isDirectory()){
                    //test already exists: ask to overwrite
                    OptionResult res = SwingUtils.showOption(this, "Overwrite Test?", "Test already exists. Overwrite with this new one?", OptionType.YesNo);
                    if (res == OptionResult.No){
                        return;
                    }
                }else{
                    boolean success = testDir.toFile().mkdirs();
                    if (!success) {
                        // Directory creation failed
                        SwingUtils.showMessage(this, "generateTestFiles()", "Cannot create directory " + testDir.toString());
                        return;
                    }
                }
                //generate test files inside test dir
                
                if (!generateTestConfig(TEMPLATES_TESTCONFIG_FILEPATH,
                                testDir,
                                sTestName,
                                sTestDescription,
                                sTestSuite,
                                hmTestParams)) return;
                if (!generateTestHelp(testDir,
                                sTestName,
                                sTestDescription,
                                sContactName,
                                sTestSuite,
                                hmTestParams)) return;
                if (!generateTestScript(testDir,
                                sTestName,
                                sTestDescription,
                                sTestSuite,
                                hmTestParams)) return;
                //SwingUtils.showMessage(this, "Test generated", "Test "+sTestName+" successfully generated in \n"+testDir.toString());
                OptionResult res = SwingUtils.showOption(this, "Test generated", 
                        "Test "+sTestName+" successfully generated in \n"+testDir.toString() + "\nDo you want to edit the testing script now?", 
                        OptionType.YesNo);
                if (res == OptionResult.Yes){
                    try {
                        ((View)App.getInstance().getMainFrame()).openScript(makeTestFilePath(testDir, sTestName).toString());
                    } catch (IOException ex) {
                        Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

            }else{
                SwingUtils.showMessage(this, "generateTestFiles()", "Cannot find tests default directory " + TESTS_TESTS_DEFAULT_DIR.toString());
            }
    }
    

    /**
     * create folders and files for the new device to generate
     * 
     * @param sTestName name of the test
     * @param sTestDescription description of the test
     * @param sTestSuite test cases/test suite name
     * @param hmTestParams test parameters. For the details of the structure, see buildParametersMap
     */
    public void generateDeviceFiles(String sTestName,
                                  String sTestDescription,
                                  String sTestSuite,
                                  HashMap hmTestParams){
            if (TESTS_DEVICES_DEFAULT_DIR.toFile().isDirectory()) {
                //check that the tests suite dir exists. If not, create it
                String testSuiteDir = generateTestSuite(sTestSuite);
                //new device directory inside devices dir
                Path testDir = Paths.get(TESTS_DEVICES_DEFAULT_DIR.toString(),cleanFileName(sTestName));
                if( testDir.toFile().isDirectory()){
                    //device already exists: ask to overwrite
                    OptionResult res = SwingUtils.showOption(this, "Overwrite Device?", "Device already exists. Overwrite with this new one?", OptionType.YesNo);
                    if (res == OptionResult.No){
                        return;
                    }
                }else{
                    boolean success = testDir.toFile().mkdirs();
                    if (!success) {
                        // Directory creation failed
                        SwingUtils.showMessage(this, "generateTestFiles()", "Cannot create directory " + testDir.toString());
                        return; 
                    }
                }
                //generate device files inside device dir               
                if (!generateTestConfig(TEMPLATES_DEVICECONFIG_FILEPATH,
                                testDir,
                                sTestName,
                                sTestDescription,
                                sTestSuite,
                                hmTestParams)) return;
                SwingUtils.showMessage(this, "Test generated", "Test "+sTestName+" successfully generated in \n"+testDir.toString());

            }else{
                SwingUtils.showMessage(this, "generateTestFiles()", "Cannot find tests default directory " + TESTS_TESTS_DEFAULT_DIR.toString());
            }
    }
    
    public Path makeTestFilePath(Path testDir, String sTestName){
        return Paths.get(testDir.toString(), cleanFileName(sTestName+".py"));
    }
    
    private String generateTestSuite(String sTestSuite){
                //check that the tests suite dir exists. If not, create it
                Path testSuiteDir = Paths.get(TESTS_TESTS_DEFAULT_DIR.toString(),cleanFileName(sTestSuite));
                if( !testSuiteDir.toFile().isDirectory()){
                    boolean success = testSuiteDir.toFile().mkdirs();
                    if (!success) {
                        // Directory creation failed
                        SwingUtils.showMessage(this, "generateTestFiles()", "Cannot create directory " + testSuiteDir.toString());
                        return testSuiteDir.toString();
                    }
                }
        return testSuiteDir.toString();
    }
    
    /**
     * generate the python script that runs the test
     * 
     * @param testDir path where the test is located (including the test name dir)
     * @param sTestName name of the test
     * @param sTestDescription description of the test
     * @param sTestSuite test cases/test suite name
     * @param hmTestParams test parameters. For the details of the structure, see buildParametersMap
     * @return true if success, false if else
     */
    public boolean generateTestScript(Path testDir,
                                  String sTestName,
                                  String sTestDescription,
                                  String sTestSuite,
                                  HashMap hmTestParams){
        boolean success = false;
        Path path = makeTestFilePath(testDir, sTestName);
        try {
            if(Files.exists(path)){
                Files.delete(path);
            }
            Files.copy(TEMPLATES_TESTSCRIPT_FILEPATH, path);
            //add test parameters
            String name="", value="", description="";
            DefaultTableModel model = (DefaultTableModel) jTableParams.getModel();
            String sSyntaxGetTestParameters = "";
            for(Object entry : hmTestParams.keySet()){
                Object param = entry;
                if(param instanceof String){
                    name = (String) param;
                    HashMap attributes = (HashMap) hmTestParams.get(param);
                    value = (String) attributes.get("value");
                    description = (String) attributes.get("description");
                    //build the python code for getting the test parameter
                    sSyntaxGetTestParameters = sSyntaxGetTestParameters + name + " = float(test.getParam('"+name+"')) ; "; 
                }               
            }      
            replaceParameters(path, 
                    new String[]{"$testName", "$testDescription","$testSuite","#$testParameters"}, 
                    new String[]{sTestName, sTestDescription,sTestSuite,sSyntaxGetTestParameters});
            success = true;
        } catch (IOException ex) {
            SwingUtils.showMessage(this, "generateTestScript()", "Cannot copy file " + ex.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }

    /**
     * generate the python script that runs the test
     * 
     * @param templateDir the path of the template to be used for the generation of theconfig.
     *          it can be: TEMPLATES_TESTCONFIG_FILEPATH, TEMPLATES_DEVICECONFIG_FILEPATH
     * @param testDir path where the test is located (including the test name dir)
     * @param sTestName name of the test
     * @param sTestDescription description of the test
     * @param sTestSuite test cases/test suite name
     * @param hmTestParams test parameters. For the details of the structure, see buildParametersMap
     * @return true if success, false if else
     */
    public boolean generateTestConfig(Path templateDir,
                                    Path testDir,
                                    String sTestName,
                                    String sTestDescription,
                                    String sTestSuite,
                                    HashMap hmTestParams){
        //generate test config file from template        
        boolean success = false;        
        try {        
            Path path = Paths.get(testDir.toString(),TESTS_CONFIG_FILENAME);
            if(Files.exists(path)){
                Files.delete(path);
            }
            Files.copy(templateDir, path);
            File configFile = path.toFile();
            //Loop among the test parameters and generate variable assignement syntax for python
            String sTestParameters = "";
            String name="", value="", description="";
            for(Object entry : hmTestParams.keySet()){
                Object param = entry;
                if(param instanceof String){
                    name = (String) param;
                    HashMap attributes = (HashMap) hmTestParams.get(param);
                    value = (String) attributes.get("value");
                    description = (String) attributes.get("description");
                    //build the python code for getting the test parameter
                    sTestParameters = sTestParameters + name + ":" + value + ":" + description + ";" ; 
                }               
            }            
            FileInputStream in = new FileInputStream(configFile);
            Properties props = new Properties();
            props.load(in);
            in.close();
            FileOutputStream out = new FileOutputStream(configFile);
            if(sType == TypeOfNewFile.Device){
                //creation of device config
                props.setProperty("name", sTestName);
                props.setProperty("description", sTestDescription);
                props.setProperty("tests", sTestSuite);                
            }else{
                //creation of test config
                props.setProperty("name", sTestName);
                props.setProperty("description", sTestDescription);
                props.setProperty("parameters", sTestParameters);                
                
            }
            props.store(out, null);
            out.close();            
            success = true;            
        } catch (FileNotFoundException ex) {
            SwingUtils.showMessage(this, "generateTestConfig()", "Cannot find file: " + ex.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            SwingUtils.showMessage(this, "generateTestConfig()", "Problem with file aaccess: " + ex.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }
    
    
    /**
     * generate the html help file of the test
     * 
     * @param testDir path where the test is located (including the test name dir)
     * @param sTestName name of the test
     * @param sTestDescription description of the test
     * @param sTestSuite test cases/test suite name
     * @param hmTestParams test parameters. For the details of the structure, see buildParametersMap
     * @return true if success, false if else
     */
    public boolean generateTestHelp(Path testDir,
                                String sTestName,
                                String sTestDescription,
                                String sContactName,
                                String sTestSuite,
                                HashMap hmTestParams){
        //generate test config file from template
        Path path = Paths.get(testDir.toString(),TESTS_HELP_FILENAME);
        boolean success = false;
        try {
            if(Files.exists(path)){
                Files.delete(path);
            }
            Files.copy(TEMPLATES_HELP_FILEPATH, path);
            //Loop among the test parameters and generate parameter help
            String sTestParameters = "";
            String name="", value="", description="";
            for(Object entry : hmTestParams.keySet()){
                Object param = entry;
                if(param instanceof String){
                    name = (String) param;
                    HashMap attributes = (HashMap) hmTestParams.get(param);
                    value = (String) attributes.get("value");
                    description = (String) attributes.get("description");
                    //build the python code for getting the test parameter
                    sTestParameters = sTestParameters + "<code>" + name + " </code>" + description + "<br/>\n" ; 
                }               
            }               
            Attributes user = null;
            user = getUserInfo(sContactName);
            String contactTelephone = "";
            String contactFirstName = sContactName;
            String contactSurname = "";
            if (user != null){
                contactTelephone  = user.get("telephoneNumber").toString().replace("telephoneNumber: ", "").replace(" ", "");
                contactTelephone  = "Tel. " + contactTelephone.substring(contactTelephone.length()-4);
                contactFirstName  = user.get("givenname").toString().replace("givenName: ", "");
                contactSurname    = user.get("sn").toString().replace("sn: ", "");
            } 
            success = replaceParameters(path,
                new String[]{"$testName","$testDescription","$testParameters","$contactUserName","$contactFirstName","$contactSurname","$contactTelephone"}, 
                new String[]{sTestName,sTestDescription,sTestParameters,sContactName,contactFirstName,contactSurname,contactTelephone});

        } catch (IOException ex) {
            SwingUtils.showMessage(this, "generateTestHelp()", "Cannot create directory " + ex.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }    
    
    /**
     * replace thetemplateparameters(usually starting with $) with real values
     * 
     * @param path path of the file whose parameters must be replaced
     * @param parameterNames array of template parameters names that must be searched in the file.
     *          the size must be equal to the size of parameterValues
     * @param parameterValues array of template parameters values 
     *          that must replace the template parameter names in the file.
     *          the size must be equal to the size of parameterNames
     * @return 
     */
    public boolean replaceParameters(Path path,
                                String parameterNames[],
                                String parameterValues[]){
        boolean success = false;
        try {
            //replace parameters in template
            String content = new String(Files.readAllBytes(path));
            for(int i=0 ; i<parameterNames.length ; i++ ){
                content = content.replace(parameterNames[i], parameterValues[i]);
            }
            //System.out.print(content);
            Charset charset = StandardCharsets.UTF_8;            
            Files.write(path, content.getBytes(charset));
            OutputStream is = new FileOutputStream(path.toFile());
            is.write(content.getBytes());
            is.close();   
            success = true;
        } catch (IOException ex) {
            SwingUtils.showMessage(this, "replaceParameters()", "Cannot access file " + path + ". "+ ex.toString());
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }
    
    /**
     * remove forbidden file system characters from file name, replace them with "-"
     * 
     * @param sName text to be cleaned
     * @return cleaned text
     */
    private String cleanFileName(String sName){
        return sName.replace(":", "-");
    }
    
    //does not work
    private Attributes getUserInfo(String userName){
        Attributes attrs = null;
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://d.psi.ch");
        DirContext ctx = null;
        try {
            ctx = new InitialDirContext(env);
        } catch (NamingException ex) {
            System.out.println("initialdircontext error");
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            attrs = ctx.getAttributes("cn="+userName+",ou=users,ou=psi,dc=d,dc=psi,dc=ch");
        } catch (NamingException ex) {
            Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        if(attrs == null){
            System.out.println("LDAP: user " + userName +" not found.");
        }
        return attrs;
    }
    
    private boolean userExists(String userName){
        Attributes attrs = getUserInfo(userName);
        return (attrs!=null);
    }
    

    /**
     * LDAP tool for authenticating a user
    */    

    public class LdapClient {

         /**
          * Build Ldap context. It is used later for getting user info
          * @return  LdapContext
          */
        private  LdapContext getLdapContext(String userName, String password)  {
            LdapContext ctx = null;
            try {
                Hashtable<String, String> env = new Hashtable<String, String>();
                
                //this can be used to authenticate:
                env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
                env.put(Context.SECURITY_AUTHENTICATION, "simple");//with "none" it does not seem to work. It works with "simple" but it needs a user name and password
                env.put(Context.SECURITY_PRINCIPAL, "PSICH\\"+userName);//input user & password for access to ldap
                env.put(Context.SECURITY_CREDENTIALS, password);//here I should write my password in clear!?!?!?
                env.put(Context.PROVIDER_URL, "ldap://d.psi.ch");
                env.put(Context.REFERRAL, "follow");
                
                ctx = new InitialLdapContext(env, null);
                
                //System.out.println("LDAP Connection: COMPLETE");
            } catch (NamingException ex) {
                Logger.getLogger(NewTest.class.getName()).log(Level.SEVERE, null, ex);
            }
            return ctx;
        }

        private Attributes authenticateUser(String userName, LdapContext ctx, SearchControls searchControls) {
            System.out.println("*** " + userName + " ***");
            Attributes attrs = null;
            try {
                NamingEnumeration<SearchResult> answer = ctx.search("dc=d,dc=psi,dc=ch", "sAMAccountName=" + userName, searchControls);
                if (answer.hasMore()) {
                    attrs = answer.next().getAttributes();
                    //System.out.println(attrs.get("givenname"));
                    //System.out.println(attrs.get("sn"));
                    //System.out.println(attrs.get("mail"));
                    //System.out.println(attrs.get("telephoneNumber"));
                } else {
                    System.out.println("Ldap user not found.");
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return attrs;
        }

        public Attributes authenticateUser(String userName, String password) {
            Attributes attrs = null;
            LdapContext ldapContext = getLdapContext(userName,password);
            SearchControls searchControls = getSearchControls();
            attrs = authenticateUser(userName, ldapContext, searchControls);
            String tel = attrs.get("telephonenumber").toString().replace(" ", "");
            tel = "Tel.: " +  tel.substring(tel.length()-4);
            attrs.put("telephoneNumber",tel);
            return attrs; 
        }


        private  SearchControls getSearchControls() {
            SearchControls cons = new SearchControls();
            cons.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String[] attrIDs = {"sn", "givenname", "mail", "telephoneNumber"};
            cons.setReturningAttributes(attrIDs);
            return cons;
        }

    }    

    

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButtonGenerate;
    private javax.swing.JComboBox jComboBoxTestSuites;
    private javax.swing.JMenuItem jMenuItemAddRow;
    private javax.swing.JMenuItem jMenuItemDeleteRow;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPopupMenu jPopupMenuTable;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JTable jTableParams;
    private javax.swing.JLabel lblContact;
    private javax.swing.JLabel lblDescr;
    private javax.swing.JLabel lblName;
    private javax.swing.JLabel lblParams;
    private javax.swing.JLabel lblSuite;
    private javax.swing.JTextField txtContactName;
    private javax.swing.JTextArea txtTestDescription;
    private javax.swing.JTextField txtTestName;
    // End of variables declaration//GEN-END:variables
}
