/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.ui.trans.steps.ssh;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransPreviewFactory;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.StepDialogInterface;
import org.pentaho.di.trans.steps.ssh.SSHData;
import org.pentaho.di.trans.steps.ssh.SSHMeta;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.dialog.EnterNumberDialog;
import org.pentaho.di.ui.core.dialog.EnterTextDialog;
import org.pentaho.di.ui.core.dialog.ErrorDialog;
import org.pentaho.di.ui.core.dialog.PreviewRowsDialog;
import org.pentaho.di.ui.core.events.dialog.FilterType;
import org.pentaho.di.ui.core.events.dialog.SelectionAdapterFileDialogText;
import org.pentaho.di.ui.core.events.dialog.SelectionAdapterFileDialogTextVar;
import org.pentaho.di.ui.core.events.dialog.SelectionAdapterOptions;
import org.pentaho.di.ui.core.events.dialog.SelectionOperation;
import org.pentaho.di.ui.core.widget.LabelTextVar;
import org.pentaho.di.ui.core.widget.StyledTextComp;
import org.pentaho.di.ui.trans.dialog.TransPreviewProgressDialog;
import org.pentaho.di.ui.trans.step.BaseStepDialog;

import com.trilead.ssh2.Connection;

public class SSHDialog extends BaseStepDialog implements StepDialogInterface {
  private static Class<?> PKG = SSHMeta.class; // for i18n purposes, needed by Translator2!!

  private CTabFolder wTabFolder;
  private FormData fdTabFolder;

  private CTabItem wGeneralTab, wSettingsTab;

  private Composite wGeneralComp, wSettingsComp;
  private FormData fdGeneralComp, fdSettingsComp;

  private Label wlCommandField;
  private CCombo wCommandField;
  private FormData fdlCommandField, fdCommandField;

  private Group wSettingsGroup;
  private FormData fdSettingsGroup;

  private Group wOutput;
  private FormData fdOutput;

  private FormData fdTimeOut;
  private LabelTextVar wTimeOut;
  private SSHMeta input;

  private Group wCommands;
  private FormData fdLogSettings;

  private Label wldynamicCommand;
  private FormData fdlynamicBase;
  private Button wdynamicCommand;
  private FormData fdynamicCommand;

  private FormData fdPort;
  private LabelTextVar wPort;

  private LabelTextVar wUserName;
  private FormData fdUserName;

  private LabelTextVar wPassword;
  private FormData fdPassword;

  private Label wlUseKey;
  private FormData fdlUseKey;
  private Button wUseKey;
  private FormData fdUseKey;

  private LabelTextVar wPrivateKey;
  private FormData fdPrivateKey;

  private LabelTextVar wPassphrase;
  private FormData fdPassphrase;

  private LabelTextVar wResultOutFieldName, wResultErrFieldName;
  private FormData fdResultOutFieldName, fdResultErrFieldName;

  private Label wlCommand;
  private StyledTextComp wCommand;
  private FormData fdlCommand, fdCommand;

  private Button wTest;

  private FormData fdTest;

  private Listener lsTest;

  private LabelTextVar wProxyHost;
  private FormData fdProxyHost;

  private LabelTextVar wProxyPort;
  private FormData fdProxyPort;

  private LabelTextVar wProxyUsername;
  private FormData fdProxyUsername;

  private LabelTextVar wProxyPassword;
  private FormData fdProxyPassword;

  private LabelTextVar wServerName;
  private FormData fdServerName;

  private Button wbFilename;
  private FormData fdbFilename;

  private boolean gotPreviousFields = false;

  public SSHDialog( Shell parent, Object in, TransMeta transMeta, String sname ) {
    super( parent, (BaseStepMeta) in, transMeta, sname );
    input = (SSHMeta) in;
  }

  @Override
  public String open() {
    Shell parent = getParent();
    Display display = parent.getDisplay();

    shell = new Shell( parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN );
    props.setLook( shell );
    setShellImage( shell, input );

    ModifyListener lsMod = new ModifyListener() {
      @Override
      public void modifyText( ModifyEvent e ) {
        input.setChanged();
      }
    };

    changed = input.hasChanged();

    FormLayout formLayout = new FormLayout();
    formLayout.marginWidth = Const.FORM_MARGIN;
    formLayout.marginHeight = Const.FORM_MARGIN;

    shell.setLayout( formLayout );
    shell.setText( BaseMessages.getString( PKG, "SSHDialog.Shell.Title" ) );

    int middle = props.getMiddlePct();
    int margin = Const.MARGIN;

    // Stepname line
    wlStepname = new Label( shell, SWT.RIGHT );
    wlStepname.setText( BaseMessages.getString( PKG, "SSHDialog.Stepname.Label" ) );
    props.setLook( wlStepname );
    fdlStepname = new FormData();
    fdlStepname.left = new FormAttachment( 0, 0 );
    fdlStepname.right = new FormAttachment( middle, -margin );
    fdlStepname.top = new FormAttachment( 0, margin );
    wlStepname.setLayoutData( fdlStepname );
    wStepname = new Text( shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER );
    wStepname.setText( stepname );
    props.setLook( wStepname );
    wStepname.addModifyListener( lsMod );
    fdStepname = new FormData();
    fdStepname.left = new FormAttachment( middle, 0 );
    fdStepname.top = new FormAttachment( 0, margin );
    fdStepname.right = new FormAttachment( 100, 0 );
    wStepname.setLayoutData( fdStepname );

    wTabFolder = new CTabFolder( shell, SWT.BORDER );
    props.setLook( wTabFolder, PropsUI.WIDGET_STYLE_TAB );

    // ////////////////////////
    // START OF GENERAL TAB ///
    // ////////////////////////
    wGeneralTab = new CTabItem( wTabFolder, SWT.NONE );
    wGeneralTab.setText( BaseMessages.getString( PKG, "SSHDialog.General.Tab" ) );

    wGeneralComp = new Composite( wTabFolder, SWT.NONE );
    props.setLook( wGeneralComp );

    FormLayout fileLayout = new FormLayout();
    fileLayout.marginWidth = 3;
    fileLayout.marginHeight = 3;
    wGeneralComp.setLayout( fileLayout );

    // ///////////////////////////////
    // START OF Settings GROUP //
    // ///////////////////////////////

    wSettingsGroup = new Group( wGeneralComp, SWT.SHADOW_NONE );
    props.setLook( wSettingsGroup );
    wSettingsGroup.setText( BaseMessages.getString( PKG, "SSHDialog.wSettingsGroup.Label" ) );

    FormLayout settingGroupLayout = new FormLayout();
    settingGroupLayout.marginWidth = 10;
    settingGroupLayout.marginHeight = 10;
    wSettingsGroup.setLayout( settingGroupLayout );

    // Server port line
    wServerName =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.Server.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.Server.Tooltip" ) );
    props.setLook( wServerName );
    wServerName.addModifyListener( lsMod );
    fdServerName = new FormData();
    fdServerName.left = new FormAttachment( 0, 0 );
    fdServerName.top = new FormAttachment( wStepname, margin );
    fdServerName.right = new FormAttachment( 100, 0 );
    wServerName.setLayoutData( fdServerName );

    // Server port line
    wPort =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.Port.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.Port.Tooltip" ) );
    props.setLook( wPort );
    wPort.addModifyListener( lsMod );
    fdPort = new FormData();
    fdPort.left = new FormAttachment( 0, 0 );
    fdPort.top = new FormAttachment( wServerName, margin );
    fdPort.right = new FormAttachment( 100, 0 );
    wPort.setLayoutData( fdPort );

    // Server TimeOut line
    wTimeOut =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.TimeOut.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.TimeOut.Tooltip" ) );
    props.setLook( wTimeOut );
    wTimeOut.addModifyListener( lsMod );
    fdTimeOut = new FormData();
    fdTimeOut.left = new FormAttachment( 0, 0 );
    fdTimeOut.top = new FormAttachment( wPort, margin );
    fdTimeOut.right = new FormAttachment( 100, 0 );
    wTimeOut.setLayoutData( fdTimeOut );

    // Usernameline
    wUserName =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.UserName.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.UserName.Tooltip" ) );
    props.setLook( wUserName );
    wUserName.addModifyListener( lsMod );
    fdUserName = new FormData();
    fdUserName.left = new FormAttachment( 0, 0 );
    fdUserName.top = new FormAttachment( wTimeOut, margin );
    fdUserName.right = new FormAttachment( 100, 0 );
    wUserName.setLayoutData( fdUserName );

    // Passwordline
    wPassword =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.Password.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.Password.Tooltip" ), true );
    props.setLook( wPassword );
    wPassword.addModifyListener( lsMod );
    fdPassword = new FormData();
    fdPassword.left = new FormAttachment( 0, 0 );
    fdPassword.top = new FormAttachment( wUserName, margin );
    fdPassword.right = new FormAttachment( 100, 0 );
    wPassword.setLayoutData( fdPassword );

    // Use key?
    wlUseKey = new Label( wSettingsGroup, SWT.RIGHT );
    wlUseKey.setText( BaseMessages.getString( PKG, "SSHDialog.UseKey.Label" ) );
    props.setLook( wlUseKey );
    fdlUseKey = new FormData();
    fdlUseKey.left = new FormAttachment( 0, 0 );
    fdlUseKey.top = new FormAttachment( wPassword, margin );
    fdlUseKey.right = new FormAttachment( middle, -margin );
    wlUseKey.setLayoutData( fdlUseKey );
    wUseKey = new Button( wSettingsGroup, SWT.CHECK );
    props.setLook( wUseKey );
    wUseKey.setToolTipText( BaseMessages.getString( PKG, "SSHDialog.UseKey.Tooltip" ) );
    fdUseKey = new FormData();
    fdUseKey.left = new FormAttachment( middle, margin );
    fdUseKey.top = new FormAttachment( wPassword, margin );
    fdUseKey.right = new FormAttachment( 100, 0 );
    wUseKey.setLayoutData( fdUseKey );
    wUseKey.addSelectionListener( new SelectionAdapter() {
      @Override
      public void widgetSelected( SelectionEvent e ) {
        input.setChanged();
        activateKey();
      }
    } );

    wbFilename = new Button( wSettingsGroup, SWT.PUSH | SWT.CENTER );
    props.setLook( wbFilename );
    wbFilename.setText( BaseMessages.getString( PKG, "System.Button.Browse" ) );
    fdbFilename = new FormData();
    fdbFilename.right = new FormAttachment( 100, -margin );
    fdbFilename.top = new FormAttachment( wUseKey, margin );
    wbFilename.setLayoutData( fdbFilename );

    // Private key
    wPrivateKey =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.PrivateKey.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.PrivateKey.Tooltip" ) );
    props.setLook( wPassword );
    wPrivateKey.addModifyListener( lsMod );
    fdPrivateKey = new FormData();
    fdPrivateKey.left = new FormAttachment( 0, 5 * margin );
    fdPrivateKey.top = new FormAttachment( wUseKey, margin );
    fdPrivateKey.right = new FormAttachment( wbFilename, -margin );
    wPrivateKey.setLayoutData( fdPrivateKey );

    wbFilename.addSelectionListener( new SelectionAdapterFileDialogText( log, wPrivateKey.getTextWidget(), transMeta,
      new SelectionAdapterOptions( SelectionOperation.FILE,
        new FilterType[] { FilterType.PEM, FilterType.ALL }, FilterType.PEM ) ) );

    // Passphraseline
    wPassphrase =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.Passphrase.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.Passphrase.Tooltip" ), true );
    props.setLook( wPassphrase );
    wPassphrase.addModifyListener( lsMod );
    fdPassphrase = new FormData();
    fdPassphrase.left = new FormAttachment( 0, 0 );
    fdPassphrase.top = new FormAttachment( wbFilename, margin );
    fdPassphrase.right = new FormAttachment( 100, 0 );
    wPassphrase.setLayoutData( fdPassphrase );

    // ProxyHostline
    wProxyHost =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.ProxyHost.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.ProxyHost.Tooltip" ) );
    props.setLook( wProxyHost );
    wProxyHost.addModifyListener( lsMod );
    fdProxyHost = new FormData();
    fdProxyHost.left = new FormAttachment( 0, 0 );
    fdProxyHost.top = new FormAttachment( wPassphrase, 2 * margin );
    fdProxyHost.right = new FormAttachment( 100, 0 );
    wProxyHost.setLayoutData( fdProxyHost );

    // ProxyPortline
    wProxyPort =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.ProxyPort.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.ProxyPort.Tooltip" ) );
    props.setLook( wProxyPort );
    wProxyPort.addModifyListener( lsMod );
    fdProxyPort = new FormData();
    fdProxyPort.left = new FormAttachment( 0, 0 );
    fdProxyPort.top = new FormAttachment( wProxyHost, margin );
    fdProxyPort.right = new FormAttachment( 100, 0 );
    wProxyPort.setLayoutData( fdProxyPort );

    // ProxyUsernameline
    wProxyUsername =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.ProxyUsername.Label" ),
        BaseMessages.getString( PKG, "SSHDialog.ProxyUsername.Tooltip" ) );
    props.setLook( wProxyUsername );
    wProxyUsername.addModifyListener( lsMod );
    fdProxyUsername = new FormData();
    fdProxyUsername.left = new FormAttachment( 0, 0 );
    fdProxyUsername.top = new FormAttachment( wProxyPort, margin );
    fdProxyUsername.right = new FormAttachment( 100, 0 );
    wProxyUsername.setLayoutData( fdProxyUsername );

    // ProxyUsernameline
    wProxyPassword =
      new LabelTextVar(
        transMeta, wSettingsGroup, BaseMessages.getString( PKG, "SSHDialog.ProxyPassword.Label" ),
        BaseMessages.getString( PKG, "SSHDialog.ProxyPassword.Tooltip" ), true );
    props.setLook( wProxyUsername );
    wProxyPassword.addModifyListener( lsMod );
    fdProxyPassword = new FormData();
    fdProxyPassword.left = new FormAttachment( 0, 0 );
    fdProxyPassword.top = new FormAttachment( wProxyUsername, margin );
    fdProxyPassword.right = new FormAttachment( 100, 0 );
    wProxyPassword.setLayoutData( fdProxyPassword );

    // Test connection button
    wTest = new Button( wSettingsGroup, SWT.PUSH );
    wTest.setText( BaseMessages.getString( PKG, "SSHDialog.TestConnection.Label" ) );
    props.setLook( wTest );
    fdTest = new FormData();
    wTest.setToolTipText( BaseMessages.getString( PKG, "SSHDialog.TestConnection.Tooltip" ) );
    fdTest.top = new FormAttachment( wProxyPassword, 2 * margin );
    fdTest.right = new FormAttachment( 100, 0 );
    wTest.setLayoutData( fdTest );

    fdSettingsGroup = new FormData();
    fdSettingsGroup.left = new FormAttachment( 0, margin );
    fdSettingsGroup.top = new FormAttachment( wStepname, margin );
    fdSettingsGroup.right = new FormAttachment( 100, -margin );
    wSettingsGroup.setLayoutData( fdSettingsGroup );

    // ///////////////////////////////
    // END OF Settings Fields GROUP //

    fdGeneralComp = new FormData();
    fdGeneralComp.left = new FormAttachment( 0, 0 );
    fdGeneralComp.top = new FormAttachment( 0, 0 );
    fdGeneralComp.right = new FormAttachment( 100, 0 );
    fdGeneralComp.bottom = new FormAttachment( 100, 0 );
    wGeneralComp.setLayoutData( fdGeneralComp );

    wGeneralComp.layout();
    wGeneralTab.setControl( wGeneralComp );

    // ///////////////////////////////////////////////////////////
    // / END OF GENERAL TAB
    // ///////////////////////////////////////////////////////////

    // ////////////////////////
    // START OF Settings TAB///
    // /
    wSettingsTab = new CTabItem( wTabFolder, SWT.NONE );
    wSettingsTab.setText( BaseMessages.getString( PKG, "SSHDialog.Settings.Tab" ) );

    FormLayout SettingsLayout = new FormLayout();
    SettingsLayout.marginWidth = 3;
    SettingsLayout.marginHeight = 3;

    wSettingsComp = new Composite( wTabFolder, SWT.NONE );
    props.setLook( wSettingsComp );
    wSettingsComp.setLayout( SettingsLayout );

    // ///////////////////////////////
    // START OF Output GROUP //
    // ///////////////////////////////

    wOutput = new Group( wSettingsComp, SWT.SHADOW_NONE );
    props.setLook( wOutput );
    wOutput.setText( BaseMessages.getString( PKG, "SSHDialog.wOutput.Label" ) );

    FormLayout outputGroupLayout = new FormLayout();
    outputGroupLayout.marginWidth = 10;
    outputGroupLayout.marginHeight = 10;
    wOutput.setLayout( outputGroupLayout );

    // ResultOutFieldNameline
    wResultOutFieldName =
      new LabelTextVar(
        transMeta, wOutput, BaseMessages.getString( PKG, "SSHDialog.ResultOutFieldName.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.ResultOutFieldName.Tooltip" ) );
    props.setLook( wResultOutFieldName );
    wResultOutFieldName.addModifyListener( lsMod );
    fdResultOutFieldName = new FormData();
    fdResultOutFieldName.left = new FormAttachment( 0, 0 );
    fdResultOutFieldName.top = new FormAttachment( wStepname, margin );
    fdResultOutFieldName.right = new FormAttachment( 100, 0 );
    wResultOutFieldName.setLayoutData( fdResultOutFieldName );

    // ResultErrFieldNameline
    wResultErrFieldName =
      new LabelTextVar(
        transMeta, wOutput, BaseMessages.getString( PKG, "SSHDialog.ResultErrFieldName.Label" ), BaseMessages
          .getString( PKG, "SSHDialog.ResultErrFieldName.Tooltip" ) );
    props.setLook( wResultErrFieldName );
    wResultErrFieldName.addModifyListener( lsMod );
    fdResultErrFieldName = new FormData();
    fdResultErrFieldName.left = new FormAttachment( 0, 0 );
    fdResultErrFieldName.top = new FormAttachment( wResultOutFieldName, margin );
    fdResultErrFieldName.right = new FormAttachment( 100, 0 );
    wResultErrFieldName.setLayoutData( fdResultErrFieldName );

    fdOutput = new FormData();
    fdOutput.left = new FormAttachment( 0, margin );
    fdOutput.top = new FormAttachment( wStepname, margin );
    fdOutput.right = new FormAttachment( 100, -margin );
    wOutput.setLayoutData( fdOutput );

    // ///////////////////////////////
    // END OF Output Fields GROUP //

    // ////////////////////////
    // START OF Commands SETTINGS GROUP///
    // /
    wCommands = new Group( wSettingsComp, SWT.SHADOW_NONE );
    props.setLook( wCommands );
    wCommands.setText( BaseMessages.getString( PKG, "SSHDialog.LogSettings.Group.Label" ) );

    FormLayout LogSettingsgroupLayout = new FormLayout();
    LogSettingsgroupLayout.marginWidth = 10;
    LogSettingsgroupLayout.marginHeight = 10;

    wCommands.setLayout( LogSettingsgroupLayout );

    // Is command defined in a Field
    wldynamicCommand = new Label( wCommands, SWT.RIGHT );
    wldynamicCommand.setText( BaseMessages.getString( PKG, "SSHDialog.dynamicCommand.Label" ) );
    props.setLook( wldynamicCommand );
    fdlynamicBase = new FormData();
    fdlynamicBase.left = new FormAttachment( 0, margin );
    fdlynamicBase.top = new FormAttachment( wOutput, margin );
    fdlynamicBase.right = new FormAttachment( middle, -margin );
    wldynamicCommand.setLayoutData( fdlynamicBase );

    wdynamicCommand = new Button( wCommands, SWT.CHECK );
    props.setLook( wdynamicCommand );
    wdynamicCommand.setToolTipText( BaseMessages.getString( PKG, "SSHDialog.dynamicCommand.Tooltip" ) );
    fdynamicCommand = new FormData();
    fdynamicCommand.left = new FormAttachment( middle, margin );
    fdynamicCommand.top = new FormAttachment( wOutput, margin );
    wdynamicCommand.setLayoutData( fdynamicCommand );
    SelectionAdapter ldynamicCommand = new SelectionAdapter() {
      @Override
      public void widgetSelected( SelectionEvent arg0 ) {
        activateDynamicCommand();
        input.setChanged();
      }
    };
    wdynamicCommand.addSelectionListener( ldynamicCommand );

    // CommandField field
    wlCommandField = new Label( wCommands, SWT.RIGHT );
    wlCommandField.setText( BaseMessages.getString( PKG, "SSHDialog.MessageNameField.Label" ) );
    props.setLook( wlCommandField );
    fdlCommandField = new FormData();
    fdlCommandField.left = new FormAttachment( 0, margin );
    fdlCommandField.right = new FormAttachment( middle, -margin );
    fdlCommandField.top = new FormAttachment( wdynamicCommand, margin );
    wlCommandField.setLayoutData( fdlCommandField );

    wCommandField = new CCombo( wCommands, SWT.BORDER | SWT.READ_ONLY );
    props.setLook( wCommandField );
    wCommandField.setEditable( true );
    wCommandField.addModifyListener( lsMod );
    fdCommandField = new FormData();
    fdCommandField.left = new FormAttachment( middle, margin );
    fdCommandField.top = new FormAttachment( wdynamicCommand, margin );
    fdCommandField.right = new FormAttachment( 100, 0 );
    wCommandField.setLayoutData( fdCommandField );
    wCommandField.addFocusListener( new FocusListener() {
      @Override
      public void focusLost( org.eclipse.swt.events.FocusEvent e ) {
      }

      @Override
      public void focusGained( org.eclipse.swt.events.FocusEvent e ) {
        get();
      }
    } );

    // Command String
    wlCommand = new Label( wCommands, SWT.RIGHT );
    wlCommand.setText( BaseMessages.getString( PKG, "SSHDialog.Command.Label" ) );
    props.setLook( wlCommand );
    fdlCommand = new FormData();
    fdlCommand.left = new FormAttachment( 0, margin );
    fdlCommand.top = new FormAttachment( wCommandField, margin );
    fdlCommand.right = new FormAttachment( middle, -2 * margin );
    wlCommand.setLayoutData( fdlCommand );

    wCommand =
      new StyledTextComp(
        transMeta, wCommands, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL, "" );
    wCommand.setToolTipText( BaseMessages.getString( PKG, "SSHDialog.Command.Tooltip" ) );
    props.setLook( wCommand );
    wCommand.addModifyListener( lsMod );
    fdCommand = new FormData();
    fdCommand.left = new FormAttachment( middle, margin );
    fdCommand.top = new FormAttachment( wCommandField, margin );
    fdCommand.right = new FormAttachment( 100, -2 * margin );
    fdCommand.bottom = new FormAttachment( 100, -margin );
    wCommand.setLayoutData( fdCommand );

    fdLogSettings = new FormData();
    fdLogSettings.left = new FormAttachment( 0, margin );
    fdLogSettings.top = new FormAttachment( wOutput, margin );
    fdLogSettings.right = new FormAttachment( 100, -margin );
    fdLogSettings.bottom = new FormAttachment( 100, -margin );
    wCommands.setLayoutData( fdLogSettings );
    // ///////////////////////////////////////////////////////////
    // / END OF Log SETTINGS GROUP
    // ///////////////////////////////////////////////////////////

    fdSettingsComp = new FormData();
    fdSettingsComp.left = new FormAttachment( 0, 0 );
    fdSettingsComp.top = new FormAttachment( 0, 0 );
    fdSettingsComp.right = new FormAttachment( 100, 0 );
    fdSettingsComp.bottom = new FormAttachment( 100, 0 );
    wSettingsComp.setLayoutData( fdSettingsComp );

    wSettingsComp.layout();
    wSettingsTab.setControl( wSettingsComp );

    // ///////////////////////////////////////////////////////////
    // / END OF Settings TAB
    // ///////////////////////////////////////////////////////////

    fdTabFolder = new FormData();
    fdTabFolder.left = new FormAttachment( 0, 0 );
    fdTabFolder.top = new FormAttachment( wStepname, margin );
    fdTabFolder.right = new FormAttachment( 100, 0 );
    fdTabFolder.bottom = new FormAttachment( 100, -50 );
    wTabFolder.setLayoutData( fdTabFolder );

    // THE BUTTONS
    wOK = new Button( shell, SWT.PUSH );
    wOK.setText( BaseMessages.getString( PKG, "System.Button.OK" ) );
    wPreview = new Button( shell, SWT.PUSH );
    wPreview.setText( BaseMessages.getString( PKG, "SSHDialog.Button.PreviewRows" ) );
    wCancel = new Button( shell, SWT.PUSH );
    wCancel.setText( BaseMessages.getString( PKG, "System.Button.Cancel" ) );

    setButtonPositions( new Button[] { wOK, wPreview, wCancel }, margin, wTabFolder );

    // Add listeners
    lsOK = new Listener() {
      @Override
      public void handleEvent( Event e ) {
        ok();
      }
    };
    lsPreview = new Listener() {
      @Override
      public void handleEvent( Event e ) {
        preview();
      }
    };
    lsCancel = new Listener() {
      @Override
      public void handleEvent( Event e ) {
        cancel();
      }
    };
    lsTest = new Listener() {
      @Override
      public void handleEvent( Event e ) {
        test();
      }
    };

    wOK.addListener( SWT.Selection, lsOK );
    wCancel.addListener( SWT.Selection, lsCancel );
    wPreview.addListener( SWT.Selection, lsPreview );
    wTest.addListener( SWT.Selection, lsTest );

    lsDef = new SelectionAdapter() {
      @Override
      public void widgetDefaultSelected( SelectionEvent e ) {
        ok();
      }
    };

    wStepname.addSelectionListener( lsDef );

    // Detect X or ALT-F4 or something that kills this window...
    shell.addShellListener( new ShellAdapter() {
      @Override
      public void shellClosed( ShellEvent e ) {
        cancel();
      }
    } );
    wTabFolder.setSelection( 0 );
    // Set the shell size, based upon previous time...
    setSize();
    getData();
    activateKey();
    activateDynamicCommand();
    input.setChanged( changed );

    shell.open();
    while ( !shell.isDisposed() ) {
      if ( !display.readAndDispatch() ) {
        display.sleep();
      }
    }
    return stepname;
  }

  /**
   * Copy information from the meta-data input to the dialog fields.
   */
  public void getData() {
    wdynamicCommand.setSelection( input.isDynamicCommand() );
    if ( input.getCommand() != null ) {
      wCommand.setText( input.getCommand() );
    }
    if ( input.getcommandfieldname() != null ) {
      wCommandField.setText( input.getcommandfieldname() );
    }
    if ( input.getServerName() != null ) {
      wServerName.setText( input.getServerName() );
    }
    if ( input.getPort() != null ) {
      wPort.setText( input.getPort() );
    }
    if ( input.getuserName() != null ) {
      wUserName.setText( input.getuserName() );
    }
    if ( input.getpassword() != null ) {
      wPassword.setText( input.getpassword() );
    }
    wUseKey.setSelection( input.isusePrivateKey() );
    if ( input.getKeyFileName() != null ) {
      wPrivateKey.setText( input.getKeyFileName() );
    }
    if ( input.getPassphrase() != null ) {
      wPassphrase.setText( input.getPassphrase() );
    }
    if ( input.getStdOutFieldName() != null ) {
      wResultOutFieldName.setText( input.getStdOutFieldName() );
    }
    if ( input.getStdErrFieldName() != null ) {
      wResultErrFieldName.setText( input.getStdErrFieldName() );
    }
    wTimeOut.setText( Const.NVL( input.getTimeOut(), "0" ) );
    if ( input.getProxyHost() != null ) {
      wProxyHost.setText( input.getProxyHost() );
    }
    if ( input.getProxyPort() != null ) {
      wProxyPort.setText( input.getProxyPort() );
    }
    if ( input.getProxyUsername() != null ) {
      wProxyUsername.setText( input.getProxyUsername() );
    }
    if ( input.getProxyPassword() != null ) {
      wProxyPassword.setText( input.getProxyPassword() );
    }

    wStepname.selectAll();
    wStepname.setFocus();
  }

  private void cancel() {
    stepname = null;
    input.setChanged( changed );
    dispose();
  }

  private void getInfo( SSHMeta in ) throws KettleException {
    stepname = wStepname.getText(); // return value

    in.setDynamicCommand( wdynamicCommand.getSelection() );
    in.setCommand( wCommand.getText() );
    in.setcommandfieldname( wCommandField.getText() );
    in.setServerName( wServerName.getText() );
    in.setPort( wPort.getText() );
    in.setuserName( wUserName.getText() );
    in.setpassword( wPassword.getText() );
    in.usePrivateKey( wUseKey.getSelection() );
    in.setKeyFileName( wPrivateKey.getText() );
    in.setPassphrase( wPassphrase.getText() );
    in.setstdOutFieldName( wResultOutFieldName.getText() );
    in.setStdErrFieldName( wResultErrFieldName.getText() );
    in.setTimeOut( wTimeOut.getText() );
    in.setProxyHost( wProxyHost.getText() );
    in.setProxyPort( wProxyPort.getText() );
    in.setProxyUsername( wProxyUsername.getText() );
    in.setProxyPassword( wProxyPassword.getText() );
  }

  private void ok() {
    if ( Utils.isEmpty( wStepname.getText() ) ) {
      return;
    }

    try {
      getInfo( input );
    } catch ( KettleException e ) {
      new ErrorDialog( shell, "Error", "Error while previewing data", e );
    }

    dispose();
  }

  private void activateKey() {
    wPrivateKey.setEnabled( wUseKey.getSelection() );
    wPassphrase.setEnabled( wUseKey.getSelection() );
  }

  private void activateDynamicCommand() {
    wlCommand.setEnabled( !wdynamicCommand.getSelection() );
    wCommand.setEnabled( !wdynamicCommand.getSelection() );
    wlCommandField.setEnabled( wdynamicCommand.getSelection() );
    wCommandField.setEnabled( wdynamicCommand.getSelection() );
    wPreview.setEnabled( !wdynamicCommand.getSelection() );
  }

  private void get() {
    if ( !gotPreviousFields ) {
      gotPreviousFields = true;
      try {
        String source = wCommandField.getText();

        wCommandField.removeAll();
        RowMetaInterface r = transMeta.getPrevStepFields( stepname );
        if ( r != null ) {
          wCommandField.setItems( r.getFieldNames() );
          if ( source != null ) {
            wCommandField.setText( source );
          }
        }
      } catch ( KettleException ke ) {
        new ErrorDialog(
          shell, BaseMessages.getString( PKG, "SSHDialog.FailedToGetFields.DialogTitle" ), BaseMessages
            .getString( PKG, "SSHDialog.FailedToGetFields.DialogMessage" ), ke );
      }
    }
  }

  private void test() {
    boolean testOK = false;
    String errMsg = null;
    String servername = transMeta.environmentSubstitute( wServerName.getText() );
    int nrPort = Const.toInt( transMeta.environmentSubstitute( wPort.getText() ), 22 );
    String username = transMeta.environmentSubstitute( wUserName.getText() );
    String password = Utils.resolvePassword( transMeta,  wPassword.getText() );
    String keyFilename = transMeta.environmentSubstitute( wPrivateKey.getText() );
    String passphrase = transMeta.environmentSubstitute( wPassphrase.getText() );
    int timeOut = Const.toInt( transMeta.environmentSubstitute( wTimeOut.getText() ), 0 );
    String proxyhost = transMeta.environmentSubstitute( wProxyHost.getText() );
    int proxyport = Const.toInt( transMeta.environmentSubstitute( wProxyPort.getText() ), 0 );
    String proxyusername = transMeta.environmentSubstitute( wProxyUsername.getText() );
    String proxypassword = Utils.resolvePassword( transMeta,  wProxyPassword.getText() );

    Connection conn = null;
    try {
      conn =
        SSHData.OpenConnection(
          servername, nrPort, username, password, wUseKey.getSelection(), keyFilename, passphrase, timeOut,
          transMeta, proxyhost, proxyport, proxyusername, proxypassword );
      testOK = true;

    } catch ( Exception e ) {
      errMsg = e.getMessage();
    } finally {
      if ( conn != null ) {
        try {
          conn.close();
        } catch ( Exception e ) { /* Ignore */
        }
      }
    }
    if ( testOK ) {
      MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_INFORMATION );
      mb.setMessage( BaseMessages.getString( PKG, "SSHDialog.Connected.OK", servername, username ) + Const.CR );
      mb.setText( BaseMessages.getString( PKG, "SSHDialog.Connected.Title.Ok" ) );
      mb.open();
    } else {
      MessageBox mb = new MessageBox( shell, SWT.OK | SWT.ICON_ERROR );
      mb.setMessage( BaseMessages.getString( PKG, "SSHDialog.Connected.NOK.ConnectionBad", servername, username )
        + Const.CR + errMsg + Const.CR );
      mb.setText( BaseMessages.getString( PKG, "SSHDialog.Connected.Title.Bad" ) );
      mb.open();
    }

  }

  /**
   * Preview the data generated by this step. This generates a transformation using this step & a dummy and previews it.
   *
   */
  private void preview() {
    try {
      // Create the Access input step
      SSHMeta oneMeta = new SSHMeta();
      getInfo( oneMeta );

      TransMeta previewMeta =
        TransPreviewFactory.generatePreviewTransformation( transMeta, oneMeta, wStepname.getText() );
      EnterNumberDialog numberDialog =
        new EnterNumberDialog(
          shell, 1, BaseMessages.getString( PKG, "SSHDialog.NumberRows.DialogTitle" ), BaseMessages.getString(
            PKG, "SSHDialog.NumberRows.DialogMessage" ) );

      int previewSize = numberDialog.open();
      if ( previewSize > 0 ) {
        TransPreviewProgressDialog progressDialog =
          new TransPreviewProgressDialog(
            shell, previewMeta, new String[] { wStepname.getText() }, new int[] { previewSize } );
        progressDialog.open();

        if ( !progressDialog.isCancelled() ) {
          Trans trans = progressDialog.getTrans();
          String loggingText = progressDialog.getLoggingText();

          if ( trans.getResult() != null && trans.getResult().getNrErrors() > 0 ) {
            EnterTextDialog etd =
              new EnterTextDialog(
                shell, BaseMessages.getString( PKG, "System.Dialog.PreviewError.Title" ), BaseMessages
                  .getString( PKG, "System.Dialog.PreviewError.Message" ), loggingText, true );
            etd.setReadOnly();
            etd.open();
          }
          PreviewRowsDialog prd =
            new PreviewRowsDialog(
              shell, transMeta, SWT.NONE, wStepname.getText(), progressDialog.getPreviewRowsMeta( wStepname
                .getText() ), progressDialog.getPreviewRows( wStepname.getText() ), loggingText );
          prd.open();

        }
      }
    } catch ( KettleException e ) {
      new ErrorDialog( shell, "Error", "Error while previewing data", e );
    }
  }
}
