/*
** Module   : SendText.java
** Abstract : implement the send-text step in a test
**
** Copyright (c) 2009-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- -----------------------Description------------------------
** 001 GES 20090113 Implement the send-text step in a test.
** 002 TJD 20230615 Trace mode additions
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.goldencode.harness.test;

import com.goldencode.harness.*;
import com.goldencode.harness.terminal.*;
import com.goldencode.util.*;

/**
 * Implement the send-text step in a test. This directive has the
 * effect of sending zero or more keys to the back end (terminal). Each
 * character in the input text string is directly interpreted as a key and
 * that key is separately sent to the back end. The character is directly
 * accessed in the string and is cast to an integer key code as well as
 * passed as the key character itself. Note that special keys that have no
 * natural text representation (e.g. the page-down key) cannot be included
 * in the text sent (with one exception noted below). Such special keys must
 * be sent via {@link SendSpecialKey}.
 * <p>
 * Optionally, a single special key can be sent at the end of all other text
 * being sent. This is useful for a common idiom where after some sequence of
 * normal text, a special key such as <code>ENTER</code> or <code>TAB</code>
 * must be used to complete the entry. Shell commands and interactive editing
 * can be simplified using this mechanism.
 * <p>
 * As with all key processing, it is possible to specify modifier keys that
 * are sent along with each key. Modifier keys include the control, alt and
 * shift keys. This allows a key that is sent to be interpreted differently.
 * For example, the C key will be interpreted as CTRL-C is the control
 * modifier is included. Zero or more modifiers can be sent with each key.
 * <p>
 * Modifiers cannot be sent as a separate key (by themselves).
 * <p>
 * The following symbolic modifier names are valid:
 * <p>
 * <pre>
 * CTRL
 * ALT
 * SHIFT
 * </pre>
 */
public class SendText
extends SendKey
{
   /** Short name for the activity. */
   private static final String NAME = "SEND-TEXT";
   
   /** Format specification for creating a description of this step. */
   private static final String SPEC = "%s\ndelay = '%d'; special = '%s';";
   
   /** Fixed delay after each key is sent. */
   private int delay = -1;
   
   /** Optional special key to send after all other text keys are sent. */
   private String special = null;
   
   /**
    * Construct an instance of a step which sends keys to the terminal.
    *
    * @param    value
    *           The text to send where each character is sent as a separate
    *           key. If <code>null</code> or the empty string then this
    *           directive is a no-operation.
    * @param    special
    *           A special key to send after all other text keys are sent. If
    *           <code>null</code> or the empty string then there is no extra
    *           special key sent. Note that any modifiers specified are also
    *           sent with the special key, just as they are sent with the text
    *           keys.
    * @param    modifiers
    *           Zero or more modifier key names (CTRL, ALT and/or SHIFT).
    *           These can be separated by spaces, commas, plus signs or any
    *           other separator (e.g. "CTRL,ALT"). Case will be ignored. This
    *           may be empty or <code>null</code> which is the equivalent of
    *           no modifiers. If not <code>null</code>, this specifies
    *           modifier keys that are to be treated as "pressed" when the
    *           key(s) are sent.
    * @param    delay
    *           If greater than zero, this is the number of milliseconds to
    *           wait after each key is sent, otherwise this value is ignored.
    */
   public SendText(String value, String special, String modifiers, int delay)
   {
      super(NAME, value, modifiers);
      
      this.delay   = delay;
      this.special = special;
   }
   
   /**
    * Set the special key to send after all other keys are sent.
    *
    * @param    special
    *           A special key to send after all other text keys are sent. If
    *           <code>null</code> or the empty string then there is no extra
    *           special key sent. Note that any modifiers specified are also
    *           sent with the special key, just as they are sent with the text
    *           keys.
    */
   public void setSpecial(String special)
   {
      this.special = special;
   }
   
   /**
    * Set the fixed wait period after each key is sent.
    *
    * @param    delay
    *           If greater than zero, this is the number of milliseconds to
    *           wait after each key is sent, otherwise this value is ignored.
    */
   public void setDelay(int delay)
   {
      this.delay = delay;
   }
   
   /**
    * Finds the setter associated with the given field name and this current
    * instance (such a setter can only be used to set the field for this
    * specific instance).
    *
    * @param    parm
    *           The field name.
    *
    * @return   The delegated setter or <code>null</code> on any failure.
    */
   public Setter obtainSetter(String parm)
   {
      Setter setter = super.obtainSetter(parm);
      
      if (setter == null && parm != null)
      {
         if (parm.equals("special"))
         {
            setter = getSpecialSetter();
         }
         else if (parm.equals("delay"))
         {
            setter = getDelaySetter();
         }
      }
      
      return setter;
   }
   
   /**
    * Obtain an instance that can delegate the calling of the
    * {@link #setSpecial} method.
    *
    * @return   The delegated instance.
    */
   public Setter getSpecialSetter()
   {
      return new Setter()
      {
         public void set(String value)
         {
            setSpecial(value);
         }
      };
   }
   
   /**
    * Obtain an instance that can delegate the calling of the
    * {@link #setDelay} method.
    *
    * @return   The delegated instance.
    */
   public Setter getDelaySetter()
   {
      return new Setter()
      {
         public void set(String value)
         {
            try
            {
               setDelay(Integer.parseInt(value));
            }
            
            catch (NumberFormatException nfe)
            {
               throw new RuntimeException(nfe);
            }
         }
      };
   }
   
   /**
    * Called after substitutions are processed to allow the description
    * to be updated if necessary.
    */
   protected void updateDescription()
   {
      super.updateDescription();
      
      String descr = getDescription();
      String other = (special == null || special.length() == 0) ? "NONE" 
                                                                : special;
      setDescription(String.format(SPEC, descr, delay, other));
   }
   
   /**
    * Send zero or more keys to the back end (terminal).
    *
    * @param    term
    *           The terminal that represents the system under test.
    * @param    value
    *           The text to send where each character is sent as a separate
    *           key. If <code>null</code> or the empty string then this
    *           directive is a no-operation.
    * @param    modifiers
    *           Zero or more modifier key names (CTRL, ALT and/or SHIFT).
    *           These can be separated by spaces, commas, plus signs or any
    *           other separator (e.g. "CTRL,ALT"). Case will be ignored. This
    *           may be empty or <code>null</code> which is the equivalent of
    *           no modifiers. If not <code>null</code>, this specifies
    *           modifier keys that are to be treated as "pressed" when the
    *           key(s) are sent.
    */
   protected void send(Terminal term, String value, String modifiers)
   {
      if (LogHelper.isFinestLoggable())
      {
         LogHelper.finest("sendText:" + value + " modifiers:" + modifiers);
      }
      if (value != null && value.length() > 0)
      {
         term.sendText(value, modifiers, delay);
         
         if (special != null && special.length() > 0)
         {
            term.sendSpecialKey(special, modifiers);
         }
      }
      
      setStatus(Status.PASSED, null);
   }
}
